home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1983 Eric P. Allman
- * Copyright (c) 1988 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted provided
- * that: (1) source distributions retain this entire copyright notice and
- * comment, and (2) distributions including binaries display the following
- * acknowledgement: ``This product includes software developed by the
- * University of California, Berkeley and its contributors'' in the
- * documentation or other materials provided with the distribution and in
- * all advertising materials mentioning features or use of this software.
- * Neither the name of the University nor the names of its contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
- #ifndef lint
- static char sccsid[] = "@(#)arpadate.c 5.11 (Berkeley) 6/1/90";
- static char rcsid[] = "@(#)$Id: arpadate.c,v 5.11.0.10 1991/06/28 05:52:38 paul Exp $";
- #endif /* not lint */
-
- #include "sendmail.h"
-
- /*
- ** ARPADATE -- Create date in ARPANET format
- **
- ** Parameters:
- ** ud -- unix style date string. if NULL, one is created.
- **
- ** Returns:
- ** pointer to an ARPANET date field
- **
- ** Side Effects:
- ** none
- **
- ** WARNING:
- ** date is stored in a local buffer -- subsequent
- ** calls will overwrite.
- **
- ** Bugs:
- ** Timezone is computed from local time, rather than
- ** from whereever (and whenever) the message was sent.
- ** To do better is very hard.
- **
- ** Some sites are now inserting the timezone into the
- ** local date. This routine should figure out what
- ** the format is and work appropriately.
- */
-
- char *
- arpadate(ud)
- register char *ud;
- {
- register char *p;
- register char *q;
- register int off;
- register struct tm *lt;
- TIME_TYPE t;
- struct tm gmt;
- static char b[42];
-
- /*
- ** Get current time.
- ** This will be used if a null argument is passed and
- ** to resolve the timezone.
- */
-
- (void) time(&t);
- if (ud == NULL)
- ud = ctime(&t);
-
- /*
- ** Crack the UNIX date line in a singularly unoriginal way.
- */
-
- q = b;
-
- p = &ud[0]; /* Mon */
- *q++ = *p++;
- *q++ = *p++;
- *q++ = *p++;
- *q++ = ',';
- *q++ = ' ';
-
- p = &ud[8]; /* 16 */
- if (*p == ' ')
- p++;
- else
- *q++ = *p++;
- *q++ = *p++;
- *q++ = ' ';
-
- p = &ud[4]; /* Sep */
- *q++ = *p++;
- *q++ = *p++;
- *q++ = *p++;
- *q++ = ' ';
-
- p = &ud[20]; /* 1979 or _1979 */
- if (*p == ' ')
- p++;
- *q++ = *p++;
- *q++ = *p++;
- *q++ = *p++;
- *q++ = *p++;
- *q++ = ' ';
-
- p = &ud[11]; /* 01:03:52 or 01:03 EST */
- *q++ = *p++;
- *q++ = *p++;
- *q++ = *p++;
- *q++ = *p++;
- *q++ = *p++;
- if (*p == ':')
- {
- *q++ = *p++;
- *q++ = *p++;
- *q++ = *p++;
- }
-
- /*
- * should really get the timezone from the time in "ud" (which
- * is only different if a non-null arg was passed which is different
- * from the current time), but for all practical purposes, returning
- * the current local zone will do (its all that is ever needed).
- */
- gmt = *gmtime(&t);
- lt = localtime(&t);
-
- off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
-
- /* assume that offset isn't more than a day ... */
- if (lt->tm_year < gmt.tm_year)
- off -= 24 * 60;
- else if (lt->tm_year > gmt.tm_year)
- off += 24 * 60;
- else if (lt->tm_yday < gmt.tm_yday)
- off -= 24 * 60;
- else if (lt->tm_yday > gmt.tm_yday)
- off += 24 * 60;
-
- *q++ = ' ';
- if (off == 0)
- {
- *q++ = 'G';
- *q++ = 'M';
- *q++ = 'T';
- }
- else
- {
- if (off < 0)
- {
- off = -off;
- *q++ = '-';
- }
- else
- *q++ = '+';
-
- if (off >= 24*60) /* should be impossible */
- off = 23*60+59; /* if not, insert silly value */
-
- *q++ = (off / 600) + '0';
- *q++ = (off / 60) % 10 + '0';
- off %= 60;
- *q++ = (off / 10) + '0';
- *q++ = (off % 10) + '0';
- }
- *q = '\0';
-
- return(b);
- }
- /*
- ** NEXTATOM -- Return pointer to next atom in header
- ** (skip whitespace and comments)
- **
- ** Parameters:
- ** s -- pointer to header string
- **
- ** Returns:
- ** pointer advanced to next non-comment header atom
- **
- ** Side Effects:
- ** none
- */
-
- static char *
- nextatom(s)
- char *s;
- {
- char *p;
-
- for (p = s;
- *p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '(');
- p++)
- {
- if (*p == '(')
- {
- int nested = 0;
-
- /* ignore comments */
- p++;
- for (; *p; p++)
- {
- if (*p == '(')
- nested++;
- else if (*p == ')')
- if (!nested)
- break;
- else
- nested--;
- }
- }
- }
- return (p);
- }
-
- /*
- ** ARPATOUNIX -- Convert RFC-822/1123 date-time specification to
- ** UNIX(R) ctime format.
- **
- ** Parameters:
- ** s -- pointer to date string
- ** e -- pointer to envelope associated with date
- **
- ** Returns:
- ** pointer to a string in ctime format
- **
- ** Side Effects:
- ** Calls asctime() which modifies its static area.
- */
-
- /*
- ** date-time field specification from RFC822 as amended by RFC 1123:
- **
- **
- ** [ day "," ] 1*2DIGIT month 2*4DIGIT 2DIGIT ":" 2DIGIT [ ":" 2DIGIT ] zone
- ** date year hours minutes seconds
- **
- ** day can be "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun" (case-insensitive)
- **
- ** month can be "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct"
- ** "Nov" "Dec" (also case-insensitive)
- **
- ** zone can be "UT" "GMT" "EST" "EDT" "CST" "CDT" "MST" "MDT" "PST" "PDT"
- ** or "+"4*DIGIT or "-"4*DIGIT (case-insensitive; military zones not useful
- ** per RFC1123)
- **
- ** Additional whitespace or comments may occur.
- */
-
- static char MonthDays[] = {
- 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
- };
-
- char *
- arpatounix(s, e)
- char *s;
- ENVELOPE *e;
- {
- struct tm tm;
- char *p;
- int h_offset = 0; /* hours */
- int m_offset = 0; /* minutes */
- extern char *DowList[]; /* defined in collect.c */
- extern char *MonthList[]; /* defined in collect.c */
-
- bzero((char *) &tm, sizeof tm);
- tm.tm_wday = -1; /* impossible value */
- p = nextatom (s);
-
- /* next atom must be a day or a date */
- if (isalpha((int) *p))
- {
- /* day */
- for (tm.tm_wday = 0; DowList[tm.tm_wday]; tm.tm_wday++)
- if (strncasecmp (p, DowList[tm.tm_wday], 3))
- continue;
- else
- {
- p += 3;
- break;
- }
- p = nextatom(p); /* ',' */
- if (*p == ',')
- p = nextatom(++p);
- }
-
- /* now must have date */
- tm.tm_mday = atoi(p);
- while (isdigit((int) *p)) /* skip over date */
- p++;
- p = nextatom(p); /* point to month name */
- for (tm.tm_mon = 0; MonthList[tm.tm_mon]; tm.tm_mon++)
- if (strncasecmp(p, MonthList[tm.tm_mon], 3))
- continue;
- else {
- p += 3;
- break;
- }
-
- p = nextatom(p); /* year */
- tm.tm_year = atoi(p);
-
- /* if this was 4 digits, subtract 1900 */
- if (tm.tm_year > 999)
- tm.tm_year -= 1900;
- else
- {
- /* if 2 or 3 digits, guess which century and convert */
- TIME_TYPE now;
- struct tm *gmt;
-
- (void) time(&now);
- gmt = gmtime(&now);
-
- /* more likely +1 day than -100(0) years */
- if (gmt->tm_mon == 11 && gmt->tm_mday == 31 &&
- tm.tm_mon == 0 && tm.tm_mday == 1)
- gmt->tm_year++;
- if (tm.tm_year > 99)
- {
- /* 3 digits */
- tm.tm_year += ((gmt->tm_year + 900 - tm.tm_year) / 1000) * 1000;
- }
- else
- {
- /* 2 digits */
- tm.tm_year += ((gmt->tm_year - tm.tm_year) / 100) * 100;
- }
- }
- while (isdigit((int) *p)) /* skip over year */
- p++;
- p = nextatom(p); /* hours */
- tm.tm_hour = atoi(p);
- while (isdigit((int) *p)) /* skip over hours */
- p++;
- p = nextatom(p); /* colon */
- if (*p == ':')
- p = nextatom(++p);
- p = nextatom(p); /* minutes */
- tm.tm_min = atoi(p);
- while (isdigit((int) *p)) /* skip over minutes */
- p++;
- p = nextatom(p); /* colon or zone */
- if (*p == ':') /* have seconds field */
- {
- p = nextatom(++p);
- tm.tm_sec = atoi(p);
- while (isdigit((int) *p)) /* skip over seconds */
- p++;
- }
- p = nextatom(p); /* zone */
- if (!strncasecmp(p, "UT", 2) || !strncasecmp(p, "GMT", 3))
- ;
- else if (!strncasecmp(p, "EDT", 3))
- h_offset = -4;
- else if (!strncasecmp(p, "EST", 3))
- {
- h_offset = -5;
- tm.tm_isdst = 1;
- }
- else if (!strncasecmp(p, "CDT", 3))
- h_offset = -5;
- else if (!strncasecmp(p, "CST", 3))
- {
- h_offset = -6;
- tm.tm_isdst = 1;
- }
- else if (!strncasecmp(p, "MDT", 3))
- h_offset = -6;
- else if (!strncasecmp(p, "MST", 3))
- {
- h_offset = -7;
- tm.tm_isdst = 1;
- }
- else if (!strncasecmp(p, "PDT", 3))
- h_offset = -7;
- else if (!strncasecmp(p, "PST", 3))
- {
- h_offset = -8;
- tm.tm_isdst = 1;
- }
- else if (*p == '+')
- {
- int off;
-
- off = atoi(++p);
- h_offset = off / 100;
- m_offset = off % 100;
- }
- else if (*p == '-')
- {
- int off;
-
- off = atoi(++p);
- h_offset = off / -100;
- m_offset = -1 * (off % 100);
- }
- else
- {
- #ifdef LOG
- syslog(LOG_NOTICE, "%s: arpatounix: unparseable date: %s",
- e->e_id, s);
- #endif /* LOG */
- return(NULL);
- }
-
- /* is the year a leap year? */
- if ((tm.tm_year % 4 == 0) &&
- ((tm.tm_year % 100 != 0) || (tm.tm_year % 400 == 0)))
- MonthDays[2] = 29;
- else
- MonthDays[2] = 28;
-
- /* apply offset */
- if (h_offset || m_offset)
- {
- tm.tm_min += m_offset;
- tm.tm_hour += h_offset;
-
- /* normalize */
- if (tm.tm_min < 0)
- {
- tm.tm_hour--;
- tm.tm_min += 60;
- }
- else if (tm.tm_min > 59)
- {
- tm.tm_hour++;
- tm.tm_min -= 60;
- }
- if (tm.tm_hour < 0)
- {
- tm.tm_mday--;
- tm.tm_wday--;
- tm.tm_hour += 24;
- }
- else if (tm.tm_hour > 23)
- {
- tm.tm_mday++;
- tm.tm_wday++;
- tm.tm_hour -= 24;
- }
- if (tm.tm_mday < 1)
- {
- if (--tm.tm_mon == -1)
- {
- tm.tm_mon = 11;
- tm.tm_year--;
-
- /* is the year a leap year? */
- if ((tm.tm_year % 4 == 0) &&
- ((tm.tm_year % 100 != 0) || (tm.tm_year % 400 == 0)))
- MonthDays[2] = 29;
- else
- MonthDays[2] = 28;
- }
- tm.tm_mday += MonthDays[tm.tm_mon];
- }
- else if (tm.tm_mday > MonthDays[tm.tm_mon])
- {
- tm.tm_mday -= MonthDays[tm.tm_mon++];
- if (tm.tm_mon > 11)
- {
- tm.tm_mon = 0;
- tm.tm_year++;
-
- /*
- * Don't have to worry about leap years in
- * January.
- */
- }
- }
- }
-
- /* determine day of week if not set from RFC822/1123 line */
- if (tm.tm_wday < 0)
- {
- int i;
-
- for (i = 0; i < tm.tm_mon; i++)
- tm.tm_yday += MonthDays[i];
- tm.tm_yday += tm.tm_mday;
-
- /* I wouldn't change these constants if I were you... */
- tm.tm_wday = (int) (((((tm.tm_year + 699L) * 146097L) / 400L) + tm.tm_yday) % 7);
- }
-
- /* now get UT */
- if ((p = asctime(&tm)) == NULL || *p == '\0' || strlen(p) < 25)
- {
- #ifdef LOG
- syslog(LOG_NOTICE, "%s: arpatounix: asctime failed: %s",
- e->e_id, s);
- #endif /* LOG */
- return(NULL);
- }
- return(p);
- }
-