+
+/*
+** 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 ctime format.
+**
+** Parameters:
+** s -- pointer to date string
+**
+** Returns:
+** pointer to a string in ctime format
+**
+** Side Effects:
+** Calls asctime() which modifies its static area.
+**
+** Syntax:
+** date-time field specification from RFC822
+** as amended by RFC 1123:
+**
+** [ day "," ] 1*2DIGIT month 2*4DIGIT \
+** 2DIGIT ":" 2DIGIT [ ":" 2DIGIT ] zone
+**
+** 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;
+{
+ char *p;
+ char *n;
+ int h_offset = 0; /* hours */
+ int m_offset = 0; /* minutes */
+ struct tm tm;
+ 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) == 0)
+ {
+ 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_t 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
+ if (LogLevel > 0)
+ 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
+ if (LogLevel > 0)
+ syslog(LOG_NOTICE, "%s: arpatounix: asctime failed: %s",
+ e->e_id, s);
+#endif /* LOG */
+ return(NULL);
+ }
+ if ((n = index(p, '\n')) != NULL)
+ *n = '\0';
+ return(p);
+}