1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)arpadate.c 6.2 (Berkeley) 01/18/93"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 # include <sys/types.h> 15 16 /* 17 ** ARPADATE -- Create date in ARPANET format 18 ** 19 ** Parameters: 20 ** ud -- unix style date string. if NULL, one is created. 21 ** 22 ** Returns: 23 ** pointer to an ARPANET date field 24 ** 25 ** Side Effects: 26 ** none 27 ** 28 ** WARNING: 29 ** date is stored in a local buffer -- subsequent 30 ** calls will overwrite. 31 ** 32 ** Bugs: 33 ** Timezone is computed from local time, rather than 34 ** from whereever (and whenever) the message was sent. 35 ** To do better is very hard. 36 ** 37 ** Some sites are now inserting the timezone into the 38 ** local date. This routine should figure out what 39 ** the format is and work appropriately. 40 */ 41 42 char * 43 arpadate(ud) 44 register char *ud; 45 { 46 register char *p; 47 register char *q; 48 register int off; 49 register int i; 50 register struct tm *lt; 51 time_t t; 52 struct tm gmt; 53 static char b[40]; 54 extern struct tm *localtime(), *gmtime(); 55 extern char *ctime(); 56 extern time_t time(); 57 58 /* 59 ** Get current time. 60 ** This will be used if a null argument is passed and 61 ** to resolve the timezone. 62 */ 63 64 (void) time(&t); 65 if (ud == NULL) 66 ud = ctime(&t); 67 68 /* 69 ** Crack the UNIX date line in a singularly unoriginal way. 70 */ 71 72 q = b; 73 74 p = &ud[0]; /* Mon */ 75 *q++ = *p++; 76 *q++ = *p++; 77 *q++ = *p++; 78 *q++ = ','; 79 *q++ = ' '; 80 81 p = &ud[8]; /* 16 */ 82 if (*p == ' ') 83 p++; 84 else 85 *q++ = *p++; 86 *q++ = *p++; 87 *q++ = ' '; 88 89 p = &ud[4]; /* Sep */ 90 *q++ = *p++; 91 *q++ = *p++; 92 *q++ = *p++; 93 *q++ = ' '; 94 95 p = &ud[20]; /* 1979 */ 96 *q++ = *p++; 97 *q++ = *p++; 98 *q++ = *p++; 99 *q++ = *p++; 100 *q++ = ' '; 101 102 p = &ud[11]; /* 01:03:52 */ 103 for (i = 8; i > 0; i--) 104 *q++ = *p++; 105 106 /* 107 * should really get the timezone from the time in "ud" (which 108 * is only different if a non-null arg was passed which is different 109 * from the current time), but for all practical purposes, returning 110 * the current local zone will do (its all that is ever needed). 111 */ 112 gmt = *gmtime(&t); 113 lt = localtime(&t); 114 115 off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min; 116 117 /* assume that offset isn't more than a day ... */ 118 if (lt->tm_year < gmt.tm_year) 119 off -= 24 * 60; 120 else if (lt->tm_year > gmt.tm_year) 121 off += 24 * 60; 122 else if (lt->tm_yday < gmt.tm_yday) 123 off -= 24 * 60; 124 else if (lt->tm_yday > gmt.tm_yday) 125 off += 24 * 60; 126 127 *q++ = ' '; 128 if (off == 0) { 129 *q++ = 'G'; 130 *q++ = 'M'; 131 *q++ = 'T'; 132 } else { 133 if (off < 0) { 134 off = -off; 135 *q++ = '-'; 136 } else 137 *q++ = '+'; 138 139 if (off >= 24*60) /* should be impossible */ 140 off = 23*60+59; /* if not, insert silly value */ 141 142 *q++ = (off / 600) + '0'; 143 *q++ = (off / 60) % 10 + '0'; 144 off %= 60; 145 *q++ = (off / 10) + '0'; 146 *q++ = (off % 10) + '0'; 147 } 148 *q = '\0'; 149 150 return (b); 151 } 152 153 /* 154 ** NEXTATOM -- Return pointer to next atom in header 155 ** (skip whitespace and comments) 156 ** 157 ** Parameters: 158 ** s -- pointer to header string 159 ** 160 ** Returns: 161 ** pointer advanced to next non-comment header atom 162 ** 163 ** Side Effects: 164 ** none 165 */ 166 167 static char * 168 nextatom(s) 169 char *s; 170 { 171 char *p; 172 173 for (p = s; 174 *p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '('); 175 p++) 176 { 177 if (*p == '(') 178 { 179 int nested = 0; 180 181 /* ignore comments */ 182 p++; 183 for (; *p; p++) 184 { 185 if (*p == '(') 186 nested++; 187 else if (*p == ')') 188 if (!nested) 189 break; 190 else 191 nested--; 192 } 193 } 194 } 195 return (p); 196 } 197 198 /* 199 ** ARPATOUNIX -- Convert RFC-822/1123 date-time specification to ctime format. 200 ** 201 ** Parameters: 202 ** s -- pointer to date string 203 ** 204 ** Returns: 205 ** pointer to a string in ctime format 206 ** 207 ** Side Effects: 208 ** Calls asctime() which modifies its static area. 209 ** 210 ** Syntax: 211 ** date-time field specification from RFC822 212 ** as amended by RFC 1123: 213 ** 214 ** [ day "," ] 1*2DIGIT month 2*4DIGIT \ 215 ** 2DIGIT ":" 2DIGIT [ ":" 2DIGIT ] zone 216 ** 217 ** Day can be "Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun" 218 ** (case-insensitive) 219 ** Month can be "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" 220 ** "Aug" "Sep" "Oct" "Nov" "Dec" (also case-insensitive) 221 ** Zone can be "UT" "GMT" "EST" "EDT" "CST" "CDT" "MST" "MDT" 222 ** "PST" "PDT" or "+"4*DIGIT or "-"4*DIGIT 223 ** (case-insensitive; military zones not useful 224 ** per RFC1123) 225 ** Additional whitespace or comments may occur. 226 */ 227 228 static char MonthDays[] = { 229 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 230 }; 231 232 char * 233 arpatounix(s, e) 234 char *s; 235 ENVELOPE *e; 236 { 237 char *p; 238 char *n; 239 int h_offset = 0; /* hours */ 240 int m_offset = 0; /* minutes */ 241 struct tm tm; 242 extern char *DowList[]; /* defined in collect.c */ 243 extern char *MonthList[]; /* defined in collect.c */ 244 245 bzero((char *) &tm, sizeof tm); 246 tm.tm_wday = -1; /* impossible value */ 247 p = nextatom (s); 248 249 /* next atom must be a day or a date */ 250 if (isalpha((int) *p)) 251 { 252 /* day */ 253 for (tm.tm_wday = 0; DowList[tm.tm_wday]; tm.tm_wday++) 254 { 255 if (strncasecmp (p, DowList[tm.tm_wday], 3)) 256 continue; 257 else 258 { 259 p += 3; 260 break; 261 } 262 } 263 p = nextatom(p); /* ',' */ 264 if (*p == ',') 265 p = nextatom(++p); 266 } 267 268 /* now must have date */ 269 tm.tm_mday = atoi(p); 270 while (isdigit((int) *p)) /* skip over date */ 271 p++; 272 p = nextatom(p); /* point to month name */ 273 for (tm.tm_mon = 0; MonthList[tm.tm_mon]; tm.tm_mon++) 274 { 275 if (strncasecmp(p, MonthList[tm.tm_mon], 3) == 0) 276 { 277 p += 3; 278 break; 279 } 280 } 281 p = nextatom(p); /* year */ 282 tm.tm_year = atoi(p); 283 284 /* if this was 4 digits, subtract 1900 */ 285 if (tm.tm_year > 999) 286 tm.tm_year -= 1900; 287 else 288 { 289 /* if 2 or 3 digits, guess which century and convert */ 290 time_t now; 291 struct tm *gmt; 292 293 (void) time(&now); 294 gmt = gmtime(&now); 295 296 /* more likely +1 day than -100(0) years */ 297 if (gmt->tm_mon == 11 && gmt->tm_mday == 31 && 298 tm.tm_mon == 0 && tm.tm_mday == 1) 299 gmt->tm_year++; 300 if (tm.tm_year > 99) 301 { 302 /* 3 digits */ 303 tm.tm_year += ((gmt->tm_year + 900 - tm.tm_year) / 1000) * 1000; 304 } 305 else 306 { 307 /* 2 digits */ 308 tm.tm_year += ((gmt->tm_year - tm.tm_year) / 100) * 100; 309 } 310 } 311 while (isdigit((int) *p)) /* skip over year */ 312 p++; 313 p = nextatom(p); /* hours */ 314 tm.tm_hour = atoi(p); 315 while (isdigit((int) *p)) /* skip over hours */ 316 p++; 317 p = nextatom(p); /* colon */ 318 if (*p == ':') 319 p = nextatom(++p); 320 p = nextatom(p); /* minutes */ 321 tm.tm_min = atoi(p); 322 while (isdigit((int) *p)) /* skip over minutes */ 323 p++; 324 p = nextatom(p); /* colon or zone */ 325 if (*p == ':') /* have seconds field */ 326 { 327 p = nextatom(++p); 328 tm.tm_sec = atoi(p); 329 while (isdigit((int) *p)) /* skip over seconds */ 330 p++; 331 } 332 p = nextatom(p); /* zone */ 333 if (!strncasecmp(p, "UT", 2) || !strncasecmp(p, "GMT", 3)) 334 ; 335 else if (!strncasecmp(p, "EDT", 3)) 336 h_offset = -4; 337 else if (!strncasecmp(p, "EST", 3)) 338 { 339 h_offset = -5; 340 tm.tm_isdst = 1; 341 } 342 else if (!strncasecmp(p, "CDT", 3)) 343 h_offset = -5; 344 else if (!strncasecmp(p, "CST", 3)) 345 { 346 h_offset = -6; 347 tm.tm_isdst = 1; 348 } 349 else if (!strncasecmp(p, "MDT", 3)) 350 h_offset = -6; 351 else if (!strncasecmp(p, "MST", 3)) 352 { 353 h_offset = -7; 354 tm.tm_isdst = 1; 355 } 356 else if (!strncasecmp(p, "PDT", 3)) 357 h_offset = -7; 358 else if (!strncasecmp(p, "PST", 3)) 359 { 360 h_offset = -8; 361 tm.tm_isdst = 1; 362 } 363 else if (*p == '+') 364 { 365 int off; 366 367 off = atoi(++p); 368 h_offset = off / 100; 369 m_offset = off % 100; 370 } 371 else if (*p == '-') 372 { 373 int off; 374 375 off = atoi(++p); 376 h_offset = off / -100; 377 m_offset = -1 * (off % 100); 378 } 379 else 380 { 381 #ifdef LOG 382 if (LogLevel > 0) 383 syslog(LOG_NOTICE, "%s: arpatounix: unparseable date: %s", 384 e->e_id, s); 385 #endif /* LOG */ 386 return(NULL); 387 } 388 389 /* is the year a leap year? */ 390 if ((tm.tm_year % 4 == 0) && 391 ((tm.tm_year % 100 != 0) || (tm.tm_year % 400 == 0))) 392 MonthDays[2] = 29; 393 else 394 MonthDays[2] = 28; 395 396 /* apply offset */ 397 if (h_offset || m_offset) 398 { 399 tm.tm_min += m_offset; 400 tm.tm_hour += h_offset; 401 402 /* normalize */ 403 if (tm.tm_min < 0) 404 { 405 tm.tm_hour--; 406 tm.tm_min += 60; 407 } 408 else if (tm.tm_min > 59) 409 { 410 tm.tm_hour++; 411 tm.tm_min -= 60; 412 } 413 if (tm.tm_hour < 0) 414 { 415 tm.tm_mday--; 416 tm.tm_wday--; 417 tm.tm_hour += 24; 418 } 419 else if (tm.tm_hour > 23) 420 { 421 tm.tm_mday++; 422 tm.tm_wday++; 423 tm.tm_hour -= 24; 424 } 425 if (tm.tm_mday < 1) 426 { 427 if (--tm.tm_mon == -1) 428 { 429 tm.tm_mon = 11; 430 tm.tm_year--; 431 432 /* is the year a leap year? */ 433 if ((tm.tm_year % 4 == 0) && 434 ((tm.tm_year % 100 != 0) || (tm.tm_year % 400 == 0))) 435 MonthDays[2] = 29; 436 else 437 MonthDays[2] = 28; 438 } 439 tm.tm_mday += MonthDays[tm.tm_mon]; 440 } 441 else if (tm.tm_mday > MonthDays[tm.tm_mon]) 442 { 443 tm.tm_mday -= MonthDays[tm.tm_mon++]; 444 if (tm.tm_mon > 11) 445 { 446 tm.tm_mon = 0; 447 tm.tm_year++; 448 449 /* 450 * Don't have to worry about leap years in 451 * January. 452 */ 453 } 454 } 455 } 456 457 /* determine day of week if not set from RFC822/1123 line */ 458 if (tm.tm_wday < 0) 459 { 460 int i; 461 462 for (i = 0; i < tm.tm_mon; i++) 463 tm.tm_yday += MonthDays[i]; 464 tm.tm_yday += tm.tm_mday; 465 466 /* I wouldn't change these constants if I were you... */ 467 tm.tm_wday = (int) (((((tm.tm_year + 699L) * 146097L) / 400L) + tm.tm_yday) % 7); 468 } 469 470 /* now get UT */ 471 if ((p = asctime(&tm)) == NULL || *p == '\0' || strlen(p) < 25) 472 { 473 #ifdef LOG 474 if (LogLevel > 0) 475 syslog(LOG_NOTICE, "%s: arpatounix: asctime failed: %s", 476 e->e_id, s); 477 #endif /* LOG */ 478 return(NULL); 479 } 480 if ((n = index(p, '\n')) != NULL) 481 *n = '\0'; 482 return(p); 483 } 484