/*
 * Copyright (c) 2007 iptelorg GmbH
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * Kamailio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version
 *
 * Kamailio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

/*! \file
 * \brief Parser :: Date header
 *
 * \ingroup parser
 */



#include <string.h>
#include "parse_date.h"
#include "parse_def.h"
#include "parser_f.h"  /* eat_space_end and so on */
#include "../mem/mem.h"

/*
 * Parse Date header field
 */

#define READ(val) \
(*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24))

inline static int char2int (char *p, int *t)
{
	if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9')
		return -1;
	*t = (*p - '0')*10 + *(p + 1) - '0';

	return 0;
}

/*! \brief
 * Converts a RFC 1123 formatted date string to stuct tm
 */
static int rfc1123totm (char *stime, struct tm *ttm ) {
	char *ptime = stime;
	unsigned int uval;
	int ires;

	uval = READ(ptime);
	ptime+=4;
	switch (uval) {
		/* Sun, */
		case 0x2c6e7553: ttm->tm_wday = 0; break;
		/* Mon, */
		case 0x2c6e6f4d: ttm->tm_wday = 1; break;
		/* Tue, */
		case 0x2c657554: ttm->tm_wday = 2; break;
		/* Wed, */
		case 0x2c646557: ttm->tm_wday = 3; break;
		/* Thu, */
		case 0x2c756854: ttm->tm_wday = 4; break;
		/* Fri, */
		case 0x2c697246: ttm->tm_wday = 5; break;
		/* Sat, */
		case 0x2c746153: ttm->tm_wday = 6; break;
		default: return -2;
	}

	if (*(ptime++)!=' ') return -3;


	if (char2int(ptime,&ttm->tm_mday) || ttm->tm_mday > 31) return -4;
	ptime+=2;

	if (*(ptime++) != ' ') return -5;

	uval = READ(ptime);
	ptime+=4;
	switch (uval) {
		/* Jan, */
		case 0x206e614a: ttm->tm_mon = 0; break;
		/* Feb, */
		case 0x20626546: ttm->tm_mon = 1; break;
		/* Mar, */
		case 0x2072614d: ttm->tm_mon = 2; break;
		/* Apr, */
		case 0x20727041: ttm->tm_mon = 3; break;
		/* May, */
		case 0x2079614d: ttm->tm_mon = 4; break;
		/* Jun, */
		case 0x206e754a: ttm->tm_mon = 5; break;
		/* Jul, */
		case 0x206c754a: ttm->tm_mon = 6; break;
		/* Aug, */
		case 0x20677541: ttm->tm_mon = 7; break;
		/* Sep, */
		case 0x20706553: ttm->tm_mon = 8; break;
		/* Oct, */
		case 0x2074634f: ttm->tm_mon = 9; break;
		/* Nov, */
		case 0x20766f4e: ttm->tm_mon = 10; break;
		/* Dec, */
		case 0x20636544: ttm->tm_mon = 11; break;
		default: return -6;
	}

	if (char2int(ptime,&ires)) return -7;
	ptime+=2;
	if (char2int(ptime,&ttm->tm_year)) return -8;
	ptime+=2;
	ttm->tm_year+=(ires-19)*100;

	if (*(ptime++) != ' ') return -9;

	if (char2int(ptime,&ttm->tm_hour) || ttm->tm_hour > 23) return -10;
	ptime+=2;
	if (*(ptime++) != ':') return -11;

	if (char2int(ptime,&ttm->tm_min) || ttm->tm_min > 59) return -12;
	ptime+=2;
	if (*(ptime++) != ':') return -13;

	if (char2int(ptime,&ttm->tm_sec) || ttm->tm_sec > 59) return -14;
	ptime+=2;

	/* " GMT" */
	uval = READ(ptime);
	if ((uval | 0x20202020) != 0x746d6720) return -15;

	return 0;
}

void parse_date(char *buffer, char *end, struct date_body *db)
{
	db->error=PARSE_ERROR;

	/* check whether enough characters are available */
	if (end - buffer < RFC1123DATELENGTH)
		goto error;

	if (rfc1123totm(buffer,&db->date))
		goto error;

	db->error=PARSE_OK;
	return ;
error:
	LM_ERR("parse error\n");
	return ;
}

int parse_date_header(struct sip_msg *msg)
{
	struct date_body* date_b;


	if ( !msg->date && (parse_headers(msg,HDR_DATE_F,0)==-1 || !msg->date) ) {
		LM_ERR("bad msg or missing DATE header\n");
		goto error;
	}

	/* maybe the header is already parsed! */
	if (msg->date->parsed)
		return 0;

	date_b=pkg_malloc(sizeof(*date_b));
	if (date_b==0){
		PKG_MEM_ERROR;
		goto error;
	}
	memset(date_b, 0, sizeof(*date_b));

	parse_date(msg->date->body.s,
			   msg->date->body.s + msg->date->body.len+1,
			   date_b);
	if (date_b->error==PARSE_ERROR){
		free_date(date_b);
		goto error;
	}
	msg->date->parsed=(void*)date_b;

	return 0;
error:
	return -1;
}

void free_date(struct date_body *db)
{
	pkg_free(db);
}
