static char rcsid[] = "@(#)$Id: parsarpdat.c,v 1.3 2006/04/09 07:37:08 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.3 $   $State: Exp $
 *
 *  Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI> 
 *                           (was hurtta+elm@ozone.FMI.FI)
 ******************************************************************************
 *  The Elm Mail System 
 *
 * 			Copyright (c) 1993 USENET Community Trust
 *****************************************************************************/


#include "def_mbox.h"

DEBUG_VAR(Debug,__FILE__,"header");

/*

Quoting from RFC 822:
     5.  DATE AND TIME SPECIFICATION

     5.1.  SYNTAX

     date-time   =  [ day "," ] date time        ; dd mm yy
						 ;  hh:mm:ss zzz

     day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
		 /  "Fri"  / "Sat" /  "Sun"

     date        =  1*2DIGIT month 2DIGIT        ; day month year
						 ;  e.g. 20 Jun 82

     month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
		 /  "May"  /  "Jun" /  "Jul"  /  "Aug"
		 /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"

     time        =  hour zone                    ; ANSI and Military

     hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
						 ; 00:00:00 - 23:59:59

     zone        =  "UT"  / "GMT"                ; Universal Time
						 ; North American : UT
		 /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
		 /  "CST" / "CDT"                ;  Central:  - 6/ - 5
		 /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
		 /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
		 /  1ALPHA                       ; Military: Z = UT;
						 ;  A:-1; (J not used)
						 ;  M:-12; N:+1; Y:+12
		 / ( ("+" / "-") 4DIGIT )        ; Local differential
						 ;  hours+min. (HHMM)
*/

int parse_date_time(str,time_zone,size_time_zone,tz_offset, time_result)
     CONST char *str;
     char *time_zone; 
     int size_time_zone;
     time_t *tz_offset; 
     time_t *time_result; 
{
  /*
   * Parse a date field in either RFC-822 or Unix date(1) format.
   */

  char field[STRING], save_tz[STRING];
  int month, day, year, hours, mins, secs, tz, len, i;
  
  /*
   * Since this is an RFC-822 field, there might be parenthetical
   * comments.  Yank them out.  Note that strip_parens() returns
   * a pointer to static data.
   */
  str = strip_parens(str);

  /*
   * The first field is an optional day of the week.  If it exists
   * it is supposed to have a trailing comma by RFC-822, but we won't
   * complain if it doesn't.  If the date string was generated by
   * the Unix date(1) command then it won't have the comma.  We don't
   * do anything with this information, just skip over it if it exists.
   */
  if ((len = get_word(str, 0, field, sizeof(field))) < 0)
    goto failed;
  if (cvt_dayname_to_daynum(field, &i))
    str += len;

  /*
   * Peek at the next character to determine what format to
   * parse the rest of the line as.
   */
  while (isspace(*str))
    ++str;
  if (!isdigit(*str)) {
    
    /*
     * Parse the line in Unix date(1) format.  The syntax is:
     *
     *	month day hh:mm:ss [tz] year
     *
     * e.g. "Jun 21 06:45:44 CDT 1989".  The timezone is optional.
     */
    
    DPRINT(Debug,7,(&Debug,
		    "parse_date_time parsing \"%s\" in time(1) format\n", 
		    str));
    
    /* <month> */
    if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
	!cvt_monthname_to_monthnum(field, &month))
      goto failed;
    str += len;
    
    /* <day> */
    if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
	(day = atonum(field)) < 0)
      goto failed;
    str += len;
    
    /* <hh:mm:ss> */
    if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
	!cvt_timestr_to_hhmmss(field, &hours, &mins, &secs))
      goto failed;
    str += len;
    
    /* optional <tz> */
    save_tz[0] = save_tz[1] = '\0';
    tz = 0;
    while ((len = get_word(str, 0, field, sizeof(field))) > 0 &&
	   cvt_timezone_to_offset(field, &i, sizeof field)) {
      (void) strfcat(save_tz, " ", sizeof save_tz);
      (void) strfcat(save_tz, field, sizeof save_tz);
      tz += i;
      str += len;
    }
    
    /* <year> */
    if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
	(year = atonum(field)) < 0)
      goto failed;
    str += len;
    
    /* there might be more...but we ignore it */
    
  } else {
    
    /*
     * Parse the line in RFC-822 format.  The syntax is:
     *
     *	day month year hh:mm:ss zone
     *
     * e.g. "17 Nov 92 23:34:25 CST".
     */

    DPRINT(Debug,7,(&Debug,
		    "parse_date_time parsing \"%s\" in RFC-822 format\n", 
		    str));
    
    /* <day> */
    if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
	(day = atonum(field)) < 0)
      goto failed;
    str += len;
    
    /* <month> */
    if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
	!cvt_monthname_to_monthnum(field, &month))
      goto failed;
    str += len;

    /* <year> */
    if ((len = get_word(str, 0, field, sizeof(field))) < 0 ||
	!cvt_yearstr_to_yearnum(field, &year))
      goto failed;
    str += len;

    /* <hh:mm:ss> */
    if ((len = get_word(str, 0, field, sizeof(field))) < 0) {

      /* on expires header there is no necessary time -part */

      save_tz[0] = save_tz[1] = '\0';
      tz = 0;
      hours = 0;
      mins = 0;
      secs = 0;
      DPRINT(Debug,7,(&Debug,
		      "  only date given (no time and timezone)\n"));
      goto notime;
    }
    if (!cvt_timestr_to_hhmmss(field, &hours, &mins, &secs))
      goto failed;
    str += len;
    
    /* <tz> - silently ignore bogus or missing timezones */
    save_tz[0] = save_tz[1] = '\0';
    tz = 0;
    while ((len = get_word(str, 0, field, sizeof(field))) > 0 &&
	   cvt_timezone_to_offset(field, &i, sizeof field)) {
      (void) strfcat(save_tz, " ", sizeof save_tz);
      (void) strfcat(save_tz, field, sizeof save_tz);
      tz += i;
      str += len;
    }
    
    /* there might be more...but we ignore it */

  }

notime:

  if (time_zone)
    strfcpy(time_zone, save_tz+1, size_time_zone);
  if (tz_offset)
    *tz_offset = tz*60;
  *time_result = make_gmttime(year, month, day, hours, mins-tz, secs);

  DPRINT(Debug,7,(&Debug, "  year=%d month=%d day=%d\n", year, month, day));
  DPRINT(Debug,7,(&Debug, "  hours=%d mins=%d secs=%d tz=%d\n",
		  hours, mins, secs, tz));
  DPRINT(Debug,7,(&Debug, "  time_result=%ld\n",(long)*time_result));
  DPRINT(Debug,7,(&Debug, "  return success\n"));
  return TRUE;

failed:

  DPRINT(Debug,4,(&Debug, "parse_date_time failed at \"%s\"\n",
		  (len <= 0 ? "<premature eol>" : field)));
  return FALSE;

}

int parse_arpa_date(src, entry)
     char *src;
     struct header_rec *entry;
{
    /*
     * Parse a date field in either RFC-822 or Unix date(1) format.
     * We will fill in the "time_zone", "tz_offset", and "time_sent"
     * parts of the "entry" structure.  Return TRUE on success, FALSE
     * on failure.
     */


  if (parse_date_time(src,
		      entry->time_zone,sizeof(entry->time_zone),
		      &(entry->tz_offset), &(entry->time_sent))) {
    DPRINT(Debug,7,(&Debug,
		    "parse_arpa_date SUCCESS for %s\n",src));
    return TRUE;
  }
  DPRINT(Debug,7,(&Debug,
		  "parse_arpa_date failed for %s\n",src));
  return FALSE;
}

#ifdef _TEST
int debug = 9999;
main()
{
    struct header_rec hdr;
    char buf[1024];
    while (gets(buf) != NULL) {
	if (!parse_arpa_date(buf, &hdr))
	    fprintf(stderr, "FAIL %s\n", buf);
	else {
	    fprintf(stderr, "OK %s\n", buf);
	    fprintf(stderr, "time_zone=%s tz_offset=%d time_sent=%ld\n",
		hdr.time_zone, hdr.tz_offset, hdr.time_sent);
	}
	putc('\n', stderr);
    }
    exit(0);
}
#endif

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1