122695Sdist /* 234920Sbostic * Copyright (c) 1983 Eric P. Allman 333728Sbostic * Copyright (c) 1988 Regents of the University of California. 433728Sbostic * All rights reserved. 533728Sbostic * 642824Sbostic * %sccs.include.redist.c% 733728Sbostic */ 822695Sdist 922695Sdist #ifndef lint 10*57589Seric static char sccsid[] = "@(#)arpadate.c 6.2 (Berkeley) 01/18/93"; 1133728Sbostic #endif /* not lint */ 1222695Sdist 13*57589Seric # include "sendmail.h" 142897Seric # include <sys/types.h> 15293Seric 16293Seric /* 17293Seric ** ARPADATE -- Create date in ARPANET format 18293Seric ** 19293Seric ** Parameters: 202897Seric ** ud -- unix style date string. if NULL, one is created. 21293Seric ** 22293Seric ** Returns: 23293Seric ** pointer to an ARPANET date field 24293Seric ** 25293Seric ** Side Effects: 26293Seric ** none 27293Seric ** 28293Seric ** WARNING: 29293Seric ** date is stored in a local buffer -- subsequent 30293Seric ** calls will overwrite. 313185Seric ** 323185Seric ** Bugs: 333185Seric ** Timezone is computed from local time, rather than 343185Seric ** from whereever (and whenever) the message was sent. 353185Seric ** To do better is very hard. 364315Seric ** 374315Seric ** Some sites are now inserting the timezone into the 384315Seric ** local date. This routine should figure out what 394315Seric ** the format is and work appropriately. 40293Seric */ 41293Seric 421586Seric char * 432897Seric arpadate(ud) 442897Seric register char *ud; 45293Seric { 46293Seric register char *p; 472897Seric register char *q; 4836529Sbostic register int off; 4936529Sbostic register int i; 5036529Sbostic register struct tm *lt; 5136529Sbostic time_t t; 5236529Sbostic struct tm gmt; 53293Seric static char b[40]; 5436529Sbostic extern struct tm *localtime(), *gmtime(); 551586Seric extern char *ctime(); 5636529Sbostic extern time_t time(); 57293Seric 584315Seric /* 594315Seric ** Get current time. 604315Seric ** This will be used if a null argument is passed and 614315Seric ** to resolve the timezone. 624315Seric */ 634315Seric 644315Seric (void) time(&t); 652897Seric if (ud == NULL) 662897Seric ud = ctime(&t); 67293Seric 684315Seric /* 694315Seric ** Crack the UNIX date line in a singularly unoriginal way. 704315Seric */ 714315Seric 722897Seric q = b; 732897Seric 7413932Seric p = &ud[0]; /* Mon */ 7513932Seric *q++ = *p++; 7613932Seric *q++ = *p++; 7713932Seric *q++ = *p++; 7813932Seric *q++ = ','; 7913932Seric *q++ = ' '; 8013932Seric 81293Seric p = &ud[8]; /* 16 */ 82293Seric if (*p == ' ') 83293Seric p++; 842897Seric else 852897Seric *q++ = *p++; 862897Seric *q++ = *p++; 8710251Seric *q++ = ' '; 882897Seric 895184Seric p = &ud[4]; /* Sep */ 905184Seric *q++ = *p++; 915184Seric *q++ = *p++; 925184Seric *q++ = *p++; 9310251Seric *q++ = ' '; 942897Seric 9556304Seric p = &ud[20]; /* 1979 */ 965184Seric *q++ = *p++; 975184Seric *q++ = *p++; 9856304Seric *q++ = *p++; 9956304Seric *q++ = *p++; 1002897Seric *q++ = ' '; 1012897Seric 1022897Seric p = &ud[11]; /* 01:03:52 */ 1035184Seric for (i = 8; i > 0; i--) 1042897Seric *q++ = *p++; 1052897Seric 10636529Sbostic /* 10736529Sbostic * should really get the timezone from the time in "ud" (which 10836529Sbostic * is only different if a non-null arg was passed which is different 10936529Sbostic * from the current time), but for all practical purposes, returning 11036529Sbostic * the current local zone will do (its all that is ever needed). 11136529Sbostic */ 11236529Sbostic gmt = *gmtime(&t); 11336529Sbostic lt = localtime(&t); 1142897Seric 11536529Sbostic off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min; 11615109Skarels 11736529Sbostic /* assume that offset isn't more than a day ... */ 11836529Sbostic if (lt->tm_year < gmt.tm_year) 11936529Sbostic off -= 24 * 60; 12036529Sbostic else if (lt->tm_year > gmt.tm_year) 12136529Sbostic off += 24 * 60; 12236529Sbostic else if (lt->tm_yday < gmt.tm_yday) 12336529Sbostic off -= 24 * 60; 12436529Sbostic else if (lt->tm_yday > gmt.tm_yday) 12536529Sbostic off += 24 * 60; 12615109Skarels 12736529Sbostic *q++ = ' '; 12836529Sbostic if (off == 0) { 12936529Sbostic *q++ = 'G'; 13036529Sbostic *q++ = 'M'; 13136529Sbostic *q++ = 'T'; 13236529Sbostic } else { 13336529Sbostic if (off < 0) { 13436529Sbostic off = -off; 13536529Sbostic *q++ = '-'; 13636529Sbostic } else 13736529Sbostic *q++ = '+'; 13815109Skarels 13936529Sbostic if (off >= 24*60) /* should be impossible */ 14036529Sbostic off = 23*60+59; /* if not, insert silly value */ 14115137Seric 14236529Sbostic *q++ = (off / 600) + '0'; 14336529Sbostic *q++ = (off / 60) % 10 + '0'; 14436529Sbostic off %= 60; 14536529Sbostic *q++ = (off / 10) + '0'; 14636529Sbostic *q++ = (off % 10) + '0'; 14715137Seric } 14836529Sbostic *q = '\0'; 14936529Sbostic 15036529Sbostic return (b); 15115109Skarels } 152*57589Seric 153*57589Seric /* 154*57589Seric ** NEXTATOM -- Return pointer to next atom in header 155*57589Seric ** (skip whitespace and comments) 156*57589Seric ** 157*57589Seric ** Parameters: 158*57589Seric ** s -- pointer to header string 159*57589Seric ** 160*57589Seric ** Returns: 161*57589Seric ** pointer advanced to next non-comment header atom 162*57589Seric ** 163*57589Seric ** Side Effects: 164*57589Seric ** none 165*57589Seric */ 166*57589Seric 167*57589Seric static char * 168*57589Seric nextatom(s) 169*57589Seric char *s; 170*57589Seric { 171*57589Seric char *p; 172*57589Seric 173*57589Seric for (p = s; 174*57589Seric *p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '('); 175*57589Seric p++) 176*57589Seric { 177*57589Seric if (*p == '(') 178*57589Seric { 179*57589Seric int nested = 0; 180*57589Seric 181*57589Seric /* ignore comments */ 182*57589Seric p++; 183*57589Seric for (; *p; p++) 184*57589Seric { 185*57589Seric if (*p == '(') 186*57589Seric nested++; 187*57589Seric else if (*p == ')') 188*57589Seric if (!nested) 189*57589Seric break; 190*57589Seric else 191*57589Seric nested--; 192*57589Seric } 193*57589Seric } 194*57589Seric } 195*57589Seric return (p); 196*57589Seric } 197*57589Seric 198*57589Seric /* 199*57589Seric ** ARPATOUNIX -- Convert RFC-822/1123 date-time specification to ctime format. 200*57589Seric ** 201*57589Seric ** Parameters: 202*57589Seric ** s -- pointer to date string 203*57589Seric ** 204*57589Seric ** Returns: 205*57589Seric ** pointer to a string in ctime format 206*57589Seric ** 207*57589Seric ** Side Effects: 208*57589Seric ** Calls asctime() which modifies its static area. 209*57589Seric ** 210*57589Seric ** Syntax: 211*57589Seric ** date-time field specification from RFC822 212*57589Seric ** as amended by RFC 1123: 213*57589Seric ** 214*57589Seric ** [ day "," ] 1*2DIGIT month 2*4DIGIT \ 215*57589Seric ** 2DIGIT ":" 2DIGIT [ ":" 2DIGIT ] zone 216*57589Seric ** 217*57589Seric ** Day can be "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun" 218*57589Seric ** (case-insensitive) 219*57589Seric ** Month can be "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" 220*57589Seric ** "Aug" "Sep" "Oct" "Nov" "Dec" (also case-insensitive) 221*57589Seric ** Zone can be "UT" "GMT" "EST" "EDT" "CST" "CDT" "MST" "MDT" 222*57589Seric ** "PST" "PDT" or "+"4*DIGIT or "-"4*DIGIT 223*57589Seric ** (case-insensitive; military zones not useful 224*57589Seric ** per RFC1123) 225*57589Seric ** Additional whitespace or comments may occur. 226*57589Seric */ 227*57589Seric 228*57589Seric static char MonthDays[] = { 229*57589Seric 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 230*57589Seric }; 231*57589Seric 232*57589Seric char * 233*57589Seric arpatounix(s, e) 234*57589Seric char *s; 235*57589Seric ENVELOPE *e; 236*57589Seric { 237*57589Seric char *p; 238*57589Seric char *n; 239*57589Seric int h_offset = 0; /* hours */ 240*57589Seric int m_offset = 0; /* minutes */ 241*57589Seric struct tm tm; 242*57589Seric extern char *DowList[]; /* defined in collect.c */ 243*57589Seric extern char *MonthList[]; /* defined in collect.c */ 244*57589Seric 245*57589Seric bzero((char *) &tm, sizeof tm); 246*57589Seric tm.tm_wday = -1; /* impossible value */ 247*57589Seric p = nextatom (s); 248*57589Seric 249*57589Seric /* next atom must be a day or a date */ 250*57589Seric if (isalpha((int) *p)) 251*57589Seric { 252*57589Seric /* day */ 253*57589Seric for (tm.tm_wday = 0; DowList[tm.tm_wday]; tm.tm_wday++) 254*57589Seric { 255*57589Seric if (strncasecmp (p, DowList[tm.tm_wday], 3)) 256*57589Seric continue; 257*57589Seric else 258*57589Seric { 259*57589Seric p += 3; 260*57589Seric break; 261*57589Seric } 262*57589Seric } 263*57589Seric p = nextatom(p); /* ',' */ 264*57589Seric if (*p == ',') 265*57589Seric p = nextatom(++p); 266*57589Seric } 267*57589Seric 268*57589Seric /* now must have date */ 269*57589Seric tm.tm_mday = atoi(p); 270*57589Seric while (isdigit((int) *p)) /* skip over date */ 271*57589Seric p++; 272*57589Seric p = nextatom(p); /* point to month name */ 273*57589Seric for (tm.tm_mon = 0; MonthList[tm.tm_mon]; tm.tm_mon++) 274*57589Seric { 275*57589Seric if (strncasecmp(p, MonthList[tm.tm_mon], 3) == 0) 276*57589Seric { 277*57589Seric p += 3; 278*57589Seric break; 279*57589Seric } 280*57589Seric } 281*57589Seric p = nextatom(p); /* year */ 282*57589Seric tm.tm_year = atoi(p); 283*57589Seric 284*57589Seric /* if this was 4 digits, subtract 1900 */ 285*57589Seric if (tm.tm_year > 999) 286*57589Seric tm.tm_year -= 1900; 287*57589Seric else 288*57589Seric { 289*57589Seric /* if 2 or 3 digits, guess which century and convert */ 290*57589Seric time_t now; 291*57589Seric struct tm *gmt; 292*57589Seric 293*57589Seric (void) time(&now); 294*57589Seric gmt = gmtime(&now); 295*57589Seric 296*57589Seric /* more likely +1 day than -100(0) years */ 297*57589Seric if (gmt->tm_mon == 11 && gmt->tm_mday == 31 && 298*57589Seric tm.tm_mon == 0 && tm.tm_mday == 1) 299*57589Seric gmt->tm_year++; 300*57589Seric if (tm.tm_year > 99) 301*57589Seric { 302*57589Seric /* 3 digits */ 303*57589Seric tm.tm_year += ((gmt->tm_year + 900 - tm.tm_year) / 1000) * 1000; 304*57589Seric } 305*57589Seric else 306*57589Seric { 307*57589Seric /* 2 digits */ 308*57589Seric tm.tm_year += ((gmt->tm_year - tm.tm_year) / 100) * 100; 309*57589Seric } 310*57589Seric } 311*57589Seric while (isdigit((int) *p)) /* skip over year */ 312*57589Seric p++; 313*57589Seric p = nextatom(p); /* hours */ 314*57589Seric tm.tm_hour = atoi(p); 315*57589Seric while (isdigit((int) *p)) /* skip over hours */ 316*57589Seric p++; 317*57589Seric p = nextatom(p); /* colon */ 318*57589Seric if (*p == ':') 319*57589Seric p = nextatom(++p); 320*57589Seric p = nextatom(p); /* minutes */ 321*57589Seric tm.tm_min = atoi(p); 322*57589Seric while (isdigit((int) *p)) /* skip over minutes */ 323*57589Seric p++; 324*57589Seric p = nextatom(p); /* colon or zone */ 325*57589Seric if (*p == ':') /* have seconds field */ 326*57589Seric { 327*57589Seric p = nextatom(++p); 328*57589Seric tm.tm_sec = atoi(p); 329*57589Seric while (isdigit((int) *p)) /* skip over seconds */ 330*57589Seric p++; 331*57589Seric } 332*57589Seric p = nextatom(p); /* zone */ 333*57589Seric if (!strncasecmp(p, "UT", 2) || !strncasecmp(p, "GMT", 3)) 334*57589Seric ; 335*57589Seric else if (!strncasecmp(p, "EDT", 3)) 336*57589Seric h_offset = -4; 337*57589Seric else if (!strncasecmp(p, "EST", 3)) 338*57589Seric { 339*57589Seric h_offset = -5; 340*57589Seric tm.tm_isdst = 1; 341*57589Seric } 342*57589Seric else if (!strncasecmp(p, "CDT", 3)) 343*57589Seric h_offset = -5; 344*57589Seric else if (!strncasecmp(p, "CST", 3)) 345*57589Seric { 346*57589Seric h_offset = -6; 347*57589Seric tm.tm_isdst = 1; 348*57589Seric } 349*57589Seric else if (!strncasecmp(p, "MDT", 3)) 350*57589Seric h_offset = -6; 351*57589Seric else if (!strncasecmp(p, "MST", 3)) 352*57589Seric { 353*57589Seric h_offset = -7; 354*57589Seric tm.tm_isdst = 1; 355*57589Seric } 356*57589Seric else if (!strncasecmp(p, "PDT", 3)) 357*57589Seric h_offset = -7; 358*57589Seric else if (!strncasecmp(p, "PST", 3)) 359*57589Seric { 360*57589Seric h_offset = -8; 361*57589Seric tm.tm_isdst = 1; 362*57589Seric } 363*57589Seric else if (*p == '+') 364*57589Seric { 365*57589Seric int off; 366*57589Seric 367*57589Seric off = atoi(++p); 368*57589Seric h_offset = off / 100; 369*57589Seric m_offset = off % 100; 370*57589Seric } 371*57589Seric else if (*p == '-') 372*57589Seric { 373*57589Seric int off; 374*57589Seric 375*57589Seric off = atoi(++p); 376*57589Seric h_offset = off / -100; 377*57589Seric m_offset = -1 * (off % 100); 378*57589Seric } 379*57589Seric else 380*57589Seric { 381*57589Seric #ifdef LOG 382*57589Seric if (LogLevel > 0) 383*57589Seric syslog(LOG_NOTICE, "%s: arpatounix: unparseable date: %s", 384*57589Seric e->e_id, s); 385*57589Seric #endif /* LOG */ 386*57589Seric return(NULL); 387*57589Seric } 388*57589Seric 389*57589Seric /* is the year a leap year? */ 390*57589Seric if ((tm.tm_year % 4 == 0) && 391*57589Seric ((tm.tm_year % 100 != 0) || (tm.tm_year % 400 == 0))) 392*57589Seric MonthDays[2] = 29; 393*57589Seric else 394*57589Seric MonthDays[2] = 28; 395*57589Seric 396*57589Seric /* apply offset */ 397*57589Seric if (h_offset || m_offset) 398*57589Seric { 399*57589Seric tm.tm_min += m_offset; 400*57589Seric tm.tm_hour += h_offset; 401*57589Seric 402*57589Seric /* normalize */ 403*57589Seric if (tm.tm_min < 0) 404*57589Seric { 405*57589Seric tm.tm_hour--; 406*57589Seric tm.tm_min += 60; 407*57589Seric } 408*57589Seric else if (tm.tm_min > 59) 409*57589Seric { 410*57589Seric tm.tm_hour++; 411*57589Seric tm.tm_min -= 60; 412*57589Seric } 413*57589Seric if (tm.tm_hour < 0) 414*57589Seric { 415*57589Seric tm.tm_mday--; 416*57589Seric tm.tm_wday--; 417*57589Seric tm.tm_hour += 24; 418*57589Seric } 419*57589Seric else if (tm.tm_hour > 23) 420*57589Seric { 421*57589Seric tm.tm_mday++; 422*57589Seric tm.tm_wday++; 423*57589Seric tm.tm_hour -= 24; 424*57589Seric } 425*57589Seric if (tm.tm_mday < 1) 426*57589Seric { 427*57589Seric if (--tm.tm_mon == -1) 428*57589Seric { 429*57589Seric tm.tm_mon = 11; 430*57589Seric tm.tm_year--; 431*57589Seric 432*57589Seric /* is the year a leap year? */ 433*57589Seric if ((tm.tm_year % 4 == 0) && 434*57589Seric ((tm.tm_year % 100 != 0) || (tm.tm_year % 400 == 0))) 435*57589Seric MonthDays[2] = 29; 436*57589Seric else 437*57589Seric MonthDays[2] = 28; 438*57589Seric } 439*57589Seric tm.tm_mday += MonthDays[tm.tm_mon]; 440*57589Seric } 441*57589Seric else if (tm.tm_mday > MonthDays[tm.tm_mon]) 442*57589Seric { 443*57589Seric tm.tm_mday -= MonthDays[tm.tm_mon++]; 444*57589Seric if (tm.tm_mon > 11) 445*57589Seric { 446*57589Seric tm.tm_mon = 0; 447*57589Seric tm.tm_year++; 448*57589Seric 449*57589Seric /* 450*57589Seric * Don't have to worry about leap years in 451*57589Seric * January. 452*57589Seric */ 453*57589Seric } 454*57589Seric } 455*57589Seric } 456*57589Seric 457*57589Seric /* determine day of week if not set from RFC822/1123 line */ 458*57589Seric if (tm.tm_wday < 0) 459*57589Seric { 460*57589Seric int i; 461*57589Seric 462*57589Seric for (i = 0; i < tm.tm_mon; i++) 463*57589Seric tm.tm_yday += MonthDays[i]; 464*57589Seric tm.tm_yday += tm.tm_mday; 465*57589Seric 466*57589Seric /* I wouldn't change these constants if I were you... */ 467*57589Seric tm.tm_wday = (int) (((((tm.tm_year + 699L) * 146097L) / 400L) + tm.tm_yday) % 7); 468*57589Seric } 469*57589Seric 470*57589Seric /* now get UT */ 471*57589Seric if ((p = asctime(&tm)) == NULL || *p == '\0' || strlen(p) < 25) 472*57589Seric { 473*57589Seric #ifdef LOG 474*57589Seric if (LogLevel > 0) 475*57589Seric syslog(LOG_NOTICE, "%s: arpatounix: asctime failed: %s", 476*57589Seric e->e_id, s); 477*57589Seric #endif /* LOG */ 478*57589Seric return(NULL); 479*57589Seric } 480*57589Seric if ((n = index(p, '\n')) != NULL) 481*57589Seric *n = '\0'; 482*57589Seric return(p); 483*57589Seric } 484