|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T a
Length: 32714 (0x7fca) Types: TextFile Names: »addr_util.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit └─⟦4fd8323b9⟧ »EurOpenD3/mail/elm2.3.tar.Z« └─⟦698c4f91f⟧ └─⟦this⟧ »src/addr_util.c«
static char rcsid[] = "@(#)$Id: addr_util.c,v 4.1 90/04/28 22:42:21 syd Exp $"; /******************************************************************************* * The Elm Mail System - $Revision: 4.1 $ $State: Exp $ * * Copyright (c) 1986, 1987 Dave Taylor * Copyright (c) 1988, 1989, 1990 USENET Community Trust ******************************************************************************* * Bug reports, patches, comments, suggestions should be sent to: * * Syd Weinstein, Elm Coordinator * elm@DSI.COM dsinc!elm * ******************************************************************************* * $Log: addr_util.c,v $ * Revision 4.1 90/04/28 22:42:21 syd * checkin of Elm 2.3 as of Release PL0 * * ******************************************************************************/ /** This file contains addressing utilities **/ #include "headers.h" #include <sys/types.h> #include <sys/stat.h> #ifdef PWDINSYS # include <sys/pwd.h> #else # include <pwd.h> #endif #include <ctype.h> #ifdef BSD #undef tolower #undef toupper #endif char *get_alias_address(), *get_token(); char *strtok(), *strcpy(), *strcat(), *strncpy(), *index(), *rindex(); #define SKIP_WS(p) while (isspace(*p)) p++ #define SKIP_ALPHA(p) while (isalpha(*p)) p++ #define SKIP_DIGITS(p) while (isdigit(*p)) p++ static char *day_name[8] = { "sun", "mon", "tue", "wed", "thu", "fri", "sat", 0 }; static char *month_name[13] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec", 0 }; static int month_len[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; /* The following time zones are taken from a variety of sources. They * are by no means exhaustive, but seem to include most of those * in common usage. A comprehensive list is impossible, since the same * abbreviation is sometimes used to mean different things in different * parts of the world. */ static struct tzone { char *str; int offset; /* offset, in minutes, EAST of GMT */ } tzone_info[] = { /* the following are from rfc822 */ "ut", 0, "gmt", 0, "est", -5*60, "edt", -4*60, "cst", -6*60, "cdt", -5*60, "mst", -7*60, "mdt", -6*60, "pst", -8*60, "pdt", -7*60, "z", 0, /* zulu time (the rest of the military codes are bogus) */ /* these are also popular in Europe */ "wet", 0*60, "wet dst", 1*60, /* western european */ "met", 1*60, "met dst", 2*60, /* middle european */ "eet", 2*60, "eet dst", 3*60, /* eastern european */ "bst", 1*60, /* ??? british summer time (=+0100) */ /* ... and Canada */ "ast", -4*60, "adt", -3*60, /* atlantic */ "nst", -3*60-30, "ndt", -2*60-30, /* newfoundland */ "yst", -9*60, "ydt", -8*60, /* yukon */ "hst", -10*60, /* hawaii (not really canada) */ /* ... and Asia */ "jst", 9*60, /* japan */ "sst", 8*60, /* singapore */ /* ... and the South Pacific */ "nzst", 12*60, "nzdt", 13*60, /* new zealand */ "wst", 8*60, "wdt", 9*60, /* western australia */ /* there's also central and eastern australia, but they insist on using * cst, est, etc., which would be indistinguishable for the us zones */ (char *) 0, 0 }; char * gcos_name(gcos_field, logname) char *logname, *gcos_field; { /** Return the full name found in a passwd file gcos field **/ #ifdef BERKNAMES static char fullname[SLEN]; register char *fncp, *gcoscp, *lncp, *end; /* full name is all chars up to first ',' (or whole gcos, if no ',') */ /* replace any & with logname in upper case */ for(fncp = fullname, gcoscp= gcos_field, end = fullname + SLEN - 1; (*gcoscp != ',' && *gcoscp != '\0' && fncp != end); gcoscp++) { if(*gcoscp == '&') { for(lncp = logname; *lncp; fncp++, lncp++) *fncp = toupper(*lncp); } else { *fncp++ = *gcoscp; } } *fncp = '\0'; return(fullname); #else #ifdef USGNAMES char *firstcp, *lastcp; /* The last character of the full name is the one preceding the first * '('. If there is no '(', then the full name ends at the end of the * gcos field. */ if(lastcp = index(gcos_field, '(')) *lastcp = '\0'; /* The first character of the full name is the one following the * last '-' before that ending character. NOTE: that's why we * establish the ending character first! * If there is no '-' before the ending character, then the fullname * begins at the beginning of the gcos field. */ if(firstcp = rindex(gcos_field, '-')) firstcp++; else firstcp = gcos_field; return(firstcp); #else /* use full gcos field */ return(gcos_field); #endif #endif } char * get_full_name(logname) char *logname; { /* return a pointer to the full user name for the passed logname * or NULL if cannot be found * If PASSNAMES get it from the gcos field, otherwise get it * from ~/.fullname. */ #ifndef PASSNAMES FILE *fp; char fullnamefile[SLEN]; #endif static char fullname[SLEN]; struct passwd *getpwnam(), *pass; if((pass = getpwnam(logname)) == NULL) return(NULL); #ifdef PASSNAMES /* get full_username from gcos field */ strcpy(fullname, gcos_name(pass->pw_gecos, logname)); #else /* get full_username from ~/.fullname file */ sprintf(fullnamefile, "%s/.fullname", pass->pw_dir); if(can_access(fullnamefile, READ_ACCESS) != 0) return(NULL); /* fullname file not accessible to user */ if((fp = fopen(fullnamefile, "r")) == NULL) return(NULL); /* fullname file cannot be opened! */ if(fgets(fullname, SLEN, fp) == NULL) { fclose(fp); return(NULL); /* fullname file empty! */ } fclose(fp); no_ret(fullname); /* remove trailing '\n' */ #endif return(fullname); } int talk_to(sitename) char *sitename; { /** If we talk to the specified site, return true, else we're going to have to expand this baby out, so return false! **/ struct lsys_rec *sysname; sysname = talk_to_sys; if (sysname == NULL) { dprint(2, (debugfile, "Warning: talk_to_sys is currently set to NULL!\n")); return(0); } while (sysname != NULL) { if (strcmp(sysname->name, sitename) == 0) return(1); else sysname = sysname->next; } return(0); } add_site(buffer, site, lastsite) char *buffer, *site, *lastsite; { /** add site to buffer, unless site is 'uucp' or site is the same as lastsite. If not, set lastsite to site. **/ char local_buffer[SLEN], *stripped; char *strip_parens(); stripped = strip_parens(site); if (strcmp(stripped, "uucp") != 0) if (strcmp(stripped, lastsite) != 0) { if (buffer[0] == '\0') strcpy(buffer, stripped); /* first in list! */ else { sprintf(local_buffer,"%s!%s", buffer, stripped); strcpy(buffer, local_buffer); } strcpy(lastsite, stripped); /* don't want THIS twice! */ } } #ifdef USE_EMBEDDED_ADDRESSES get_address_from(prefix, line, buffer) char *prefix, *line, *buffer; { /** This routine extracts the address from either a 'From:' line or a 'Reply-To:' line. The strategy is as follows: if the line contains a '<', then the stuff enclosed is returned. Otherwise we go through the line and strip out comments and return that. White space will be elided from the result. **/ register char *s; /** Skip start of line over prefix, e.g. "From:". **/ line += strlen(prefix); /** If there is a '<' then copy from it to '>' into the buffer. **/ if ( (s = index(line,'<')) != NULL ) { while ( ++s , *s != '\0' && *s != '>' ) { if ( !isspace(*s) ) *buffer++ = *s; } *buffer = '\0'; return; } /** Otherwise, strip comments and get address with whitespace elided. **/ for ( s = strip_parens(line) ; *s != '\0' ; ++s ) { if ( !isspace(*s) ) *buffer++ = *s; } *buffer = '\0'; } #endif translate_return(addr, ret_addr) char *addr, *ret_addr; { /** Return ret_addr to be the same as addr, but with the login of the person sending the message replaced by '%s' for future processing... Fixed to make "%xx" "%%xx" (dumb 'C' system!) **/ register int loc, loc2, iindex = 0; loc2 = chloc(addr,'@'); if ((loc = chloc(addr, '%')) < loc2) loc2 = loc; if (loc2 != -1) { /* ARPA address. */ /* algorithm is to get to '@' sign and move backwards until we've hit the beginning of the word or another metachar. */ for (loc = loc2 - 1; loc > -1 && addr[loc] != '!'; loc--) ; } else { /* usenet address */ /* simple algorithm - find last '!' */ loc2 = strlen(addr); /* need it anyway! */ for (loc = loc2; loc > -1 && addr[loc] != '!'; loc--) ; } /** now copy up to 'loc' into destination... **/ while (iindex <= loc) { ret_addr[iindex] = addr[iindex]; iindex++; } /** now append the '%s'... **/ ret_addr[iindex++] = '%'; ret_addr[iindex++] = 's'; /** and, finally, if anything left, add that **/ loc = strlen(addr); while (loc2 < loc) { ret_addr[iindex++] = addr[loc2++]; if (addr[loc2-1] == '%') /* tweak for "printf" */ ret_addr[iindex++] = '%'; } ret_addr[iindex] = '\0'; } int build_address(to, full_to) char *to, *full_to; { /** loop on all words in 'to' line...append to full_to as we go along, until done or length > len. Modified to know that stuff in parens are comments...Returns non-zero if it changed the information as it copied it across... **/ register int i, j, changed = 0, in_parens = 0, expanded_information = 0; char word[SLEN], next_word[SLEN], *ptr, buffer[SLEN]; char new_to_list[SLEN]; char *strpbrk(), *strcat(), *gecos; #ifndef DONT_TOUCH_ADDRESSES char *expand_system(); #endif new_to_list[0] = '\0'; i = get_word(to, 0, word); full_to[0] = '\0'; while (i > 0) { j = get_word(to, i, next_word); try_new_word: if(word[0] == '(') in_parens++; if (in_parens) { if(word[strlen(word)-1] == ')') in_parens--; strcat(full_to, " "); strcat(full_to, word); } else if (strpbrk(word,"!@:") != NULL) { #ifdef DONT_TOUCH_ADDRESSES sprintf(full_to, "%s%s%s", full_to, full_to[0] != '\0'? ", " : "", word); #else sprintf(full_to, "%s%s%s", full_to, full_to[0] != '\0'? ", " : "", expand_system(word, 1)); #endif } else if ((ptr = get_alias_address(word, TRUE)) != NULL) { sprintf(full_to, "%s%s%s", full_to, full_to[0] != '\0'? ", " : "", ptr); expanded_information++; } else if (strlen(word) > 0) { if (valid_name(word)) { if (j > 0 && next_word[0] == '(') /* already has full name */ gecos = NULL; else /* needs a full name */ gecos=get_full_name(word); #if defined(INTERNET) & defined(USE_DOMAIN) sprintf(full_to, "%s%s%s@%s%s%s%s", full_to, (full_to[0] != '\0'? ", " : ""), word, hostfullname, (gecos ? " (" : ""), (gecos ? gecos : ""), (gecos ? ")" : "")); #else /* INTERNET and USE_DOMAIN */ sprintf(full_to, "%s%s%s%s%s%s", full_to, (full_to[0] != '\0'? ", " : ""), word, (gecos ? " (" : ""), (gecos ? gecos : ""), (gecos ? ")" : "")); #endif /* INTERNET and USE_DOMAIN */ } else if (check_only) { printf("(alias \"%s\" is unknown)\n\r", word); changed++; } else if (! isatty(fileno(stdin)) ) { /* batch mode error! */ fprintf(stderr,"Cannot expand alias '%s'!\n\r", word); fprintf(stderr,"Use \"checkalias\" to find valid addresses!\n\r"); dprint(1, (debugfile, "Can't expand alias %s - bailing out of build_address\n", word)); leave(0); } else { dprint(2,(debugfile,"Entered unknown address %s\n", word)); sprintf(buffer, "'%s' is an unknown address. Replace with: ", word); word[0] = '\0'; changed++; PutLine0(LINES, 0, buffer); (void)optionally_enter(word, LINES, strlen(buffer), FALSE, FALSE); clear_error(); if (strlen(word) > 0) { dprint(3,(debugfile, "Replaced with %s in build_address\n", word)); goto try_new_word; } else dprint(3,(debugfile, "Address removed from TO list by build_address\n")); continue; } } /* and this word to the new to list */ if(*new_to_list != '\0') strcat(new_to_list, " "); strcat(new_to_list, word); if((i = j) > 0) strcpy(word, next_word); } /* if new to list is different from original, update original */ if (changed) strcpy(to, new_to_list); return( expanded_information > 0 ? 1 : 0 ); } int real_from(buffer, entry) char *buffer; struct header_rec *entry; { /***** Returns true iff 's' has the seven 'from' fields, (or 8 - some machines include the TIME ZONE!!!) Initialize the date and from entries in the record and also the message received date/time if 'entry' is not NULL. *****/ struct header_rec temp_rec, *rec_ptr; char junk[STRING], timebuff[STRING], holding_from[SLEN], hold_tz[12]; char mybuf[BUFSIZ], *p, *q; int eight_fields = 0; int mday, month, year, minutes, seconds, tz, i; long gmttime; /* set rec_ptr according to whether the data is to be returned * in the second argument */ rec_ptr = (entry == NULL ? &temp_rec : entry); rec_ptr->year[0] = '\0'; timebuff[0] = '\0'; junk[0] = '\0'; hold_tz[0] = '\0'; /* From <user> <day> <month> <day> <hr:min:sec> <year> */ sscanf(buffer, "%*s %*s %*s %*s %*s %s %*s %s", timebuff, junk); if (strlen(timebuff) < 3) { dprint(3,(debugfile, "Real_from returns FAIL [no time field] on\n-> %s\n", buffer)); return(FALSE); } if (timebuff[1] != ':' && timebuff[2] != ':') { dprint(3,(debugfile, "Real_from returns FAIL [bad time field] on\n-> %s\n", buffer)); return(FALSE); } if (junk[0] != '\0') { /* try for 8 field entry */ junk[0] = '\0'; sscanf(buffer, "%*s %*s %*s %*s %*s %s %*s %*s %s", timebuff, junk); if (junk[0] != '\0') { dprint(3, (debugfile, "Real_from returns FAIL [too many fields] on\n-> %s\n", buffer)); return(FALSE); } eight_fields++; } /** now get the info out of the record! **/ if (eight_fields) sscanf(buffer, "%s %s %s %s %s %s %s %s", junk, holding_from, rec_ptr->dayname, rec_ptr->month, rec_ptr->day, rec_ptr->time, hold_tz, rec_ptr->year); else sscanf(buffer, "%s %s %s %s %s %s %s", junk, holding_from, rec_ptr->dayname, rec_ptr->month, rec_ptr->day, rec_ptr->time, rec_ptr->year); strncpy(rec_ptr->from, holding_from, STRING-1); rec_ptr->from[STRING-1] = '\0'; resolve_received(rec_ptr); /* first get everything into lower case */ for (p=mybuf, q=mybuf+sizeof mybuf; *buffer && p<q; p++, buffer++) { *p = isupper(*buffer) ? tolower(*buffer) : *buffer; } *p = 0; p = mybuf; while (!isspace(*p)) p++; /* skip "from" */ SKIP_WS(p); while (!isspace(*p)) p++; /* skip from address */ SKIP_WS(p); while (!isspace(*p)) p++; /* skip day of week */ SKIP_WS(p); month = prefix(month_name, p); get_unix_date(p,&year, &mday, &minutes, &seconds, &tz); month_len[1] = (year%4) ? 28 : 29; if (mday < 0 || mday>month_len[month]) { dprint(5,(debugfile, "ridiculous day %d of month %d\n",mday,month)); } minutes -= tz; if (tz > 0) { /* east of Greenwich */ if (minutes < 0) { if (--mday < 0) { if (--month < 0) { year--; /* don't worry about 1900! */ month = 11; } mday = month_len[month]; } minutes += 24*60; } } if (tz < 0) { /* west of Greenwich */ if (minutes <= 24*60) { if (++mday > month_len[month]) { if (++month >= 12) { year++; /* don't worry about 1999! yet?? */ month = 0; } mday = 0; } minutes -= 24*60; } } gmttime = year - 70; /* make base year */ if (gmttime < 0) gmttime += 100; gmttime = gmttime * 365 + (gmttime + 1) / 4; /* now we have days adjusted for leap years */ for (i = 0; i < month; i++) gmttime += month_len[i]; if (month > 1 && (year % 4) == 0) gmttime++; /* now to month adjusted for leap year if after feb */ gmttime += mday - 1; /* and now to the day */ gmttime *= 24 * 60; /* convert to minutes */ gmttime += minutes; rec_ptr->time_sent = gmttime * 60; /* now unix seconds since 1/1/70 00:00 GMT */ return(rec_ptr->year[0] != '\0'); } forwarded(buffer, entry) char *buffer; struct header_rec *entry; { /** Change 'from' and date fields to reflect the ORIGINATOR of the message by iteratively parsing the >From fields... Modified to deal with headers that include the time zone of the originating machine... **/ char machine[SLEN], buff[SLEN], holding_from[SLEN]; machine[0] = holding_from[0] = '\0'; sscanf(buffer, "%*s %s %s %s %s %s %s %*s %*s %s", holding_from, entry->dayname, entry->month, entry->day, entry->time, entry->year, machine); if (isdigit(entry->month[0])) { /* try for veeger address */ sscanf(buffer, "%*s %s %s%*c %s %s %s %s %*s %*s %s", holding_from, entry->dayname, entry->day, entry->month, entry->year, entry->time, machine); } if (isalpha(entry->year[0])) { /* try for address including tz */ sscanf(buffer, "%*s %s %s %s %s %s %*s %s %*s %*s %s", holding_from, entry->dayname, entry->month, entry->day, entry->time, entry->year, machine); } /* the following fix is to deal with ">From xyz ... forwarded by xyz" which occasionally shows up within AT&T. Thanks to Bill Carpenter for the fix! */ if (strcmp(machine, holding_from) == 0) machine[0] = '\0'; if (machine[0] == '\0') strcpy(buff, holding_from[0] ? holding_from : "anonymous"); else sprintf(buff,"%s!%s", machine, holding_from); strncpy(entry->from, buff, STRING-1); entry->from[STRING-1] = '\0'; } parse_arpa_who(buffer, newfrom, is_really_a_to) char *buffer, *newfrom; int is_really_a_to; { /** try to parse the 'From:' line given... It can be in one of two formats: From: Dave Taylor <hplabs!dat> or From: hplabs!dat (Dave Taylor) Added: removes quotes if name is quoted (12/12) Added: only copies STRING characters... Added: if no comment part, copy address instead! Added: if is_really_a_to, this is really a 'to' line and treat as if we allow embedded addresses **/ int use_embedded_addresses; char temp_buffer[SLEN], *temp; register int i, j = 0, in_parens; temp = (char *) temp_buffer; temp[0] = '\0'; no_ret(buffer); /* blow away '\n' char! */ if (lastch(buffer) == '>') { for (i=strlen("From: "); buffer[i] != '\0' && buffer[i] != '<' && buffer[i] != '('; i++) temp[j++] = buffer[i]; temp[j] = '\0'; } else if (lastch(buffer) == ')') { in_parens = 1; for (i=strlen(buffer)-2; buffer[i] != '\0' && buffer[i] != '<'; i--) { switch(buffer[i]) { case ')': in_parens++; break; case '(': in_parens--; break; } if(!in_parens) break; temp[j++] = buffer[i]; } temp[j] = '\0'; reverse(temp); } #ifdef USE_EMBEDDED_ADDRESSES use_embedded_addresses = TRUE; #else use_embedded_addresses = FALSE; #endif if(use_embedded_addresses || is_really_a_to) { /** if we have a null string at this point, we must just have a From: line that contains an address only. At this point we can have one of a few possibilities... From: address From: <address> From: address () **/ if (strlen(temp) == 0) { if (lastch(buffer) != '>') { for (i=strlen("From:");buffer[i] != '\0' && buffer[i] != '('; i++) temp[j++] = buffer[i]; temp[j] = '\0'; } else { /* get outta '<>' pair, please! */ for (i=strlen(buffer)-2;buffer[i] != '<' && buffer[i] != ':';i--) temp[j++] = buffer[i]; temp[j] = '\0'; reverse(temp); } } } if (strlen(temp) > 0) { /* mess with buffer... */ /* remove leading spaces and quotes... */ while (whitespace(temp[0]) || quote(temp[0])) temp = (char *) (temp + 1); /* increment address! */ /* remove trailing spaces and quotes... */ i = strlen(temp) - 1; while (whitespace(temp[i]) || quote(temp[i])) temp[i--] = '\0'; /* if anything is left, let's change 'from' value! */ if (strlen(temp) > 0) { strncpy(newfrom, temp, STRING-1); newfrom[STRING-1] = '\0'; } } } /* 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) */ /* Translate a symbolic timezone name (e.g. EDT or NZST) to a number of * minutes *east* of gmt (if the local time is t, the gmt equivalent is * t - tz_lookup(zone)). * Return 0 if the timezone is not recognized. */ static int tz_lookup(str) char *str; { struct tzone *p; for (p = tzone_info; p->str; p++) { if (strcmp(p->str,str)==0) return p->offset; } dprint(5,(debugfile,"unknown time zone %s\n",str)); return 0; } /* Return smallest i such that table[i] is a prefix of str. Return -1 if not * found. */ static int prefix(table, str) char **table; char *str; { int i; for (i=0;table[i];i++) if (strncmp(table[i],str,strlen(*table))==0) return i; return -1; } /* The following routines, get_XXX(p,...), expect p to point to a string * of the appropriate syntax. They return decoded values in result parameters, * and return p updated to point past the parsed substring (also stripping * trailing whitespace). * Return 0 on syntax errors. */ /* Parse a year: ['1' '9'] digit digit WS */ static char * get_year(p, result) char *p; int *result; { int year; if (!isdigit(*p)) { dprint(5,(debugfile,"missing year: %s\n",p)); return 0; } year = atoi(p); /* be nice and allow 19xx, althought that's not really kosher */ if (year>=1900 && year <=1999) year -= 1900; if (year<0 || year>99) { dprint(5,(debugfile,"ridiculous year %d\n",year)); return 0; } SKIP_DIGITS(p); SKIP_WS(p); *result = year; return p; } /* Parse a time: hours ':' minutes [ ':' seconds ] WS * Check that 0<=hours<24, 0<=minutes,seconds<60. * Also allow the syntax "digit digit digit digit" with implied ':' in the * middle. * Convert to minutes and seconds, with results in (*m,*s). */ static char * get_time(p,m,s) char *p; int *m, *s; { int hours, minutes, seconds; /* hour */ if (!isdigit(*p)) { dprint(5,(debugfile,"missing time: %s\n",p)); return 0; } hours = atoi(p); SKIP_DIGITS(p); if (*p++ != ':') { /* perhaps they just wrote hhmm instead of hh:mm */ minutes = hours % 60; hours /= 60; } else { if (hours<0 || hours>23) { dprint(5,(debugfile,"ridiculous hour: %d\n",hours)); return 0; } minutes = atoi(p); if (minutes<0 || minutes>59) { dprint(5,(debugfile,"ridiculous minutes: %d\n",minutes)); return 0; } } SKIP_DIGITS(p); if (*p == ':') { p++; seconds = atoi(p); if (seconds<0 || seconds>59) { dprint(5,(debugfile,"ridiculous seconds: %d\n",seconds)); return 0; } SKIP_DIGITS(p); } else seconds = 0; minutes += hours*60; SKIP_WS(p); *m = minutes; *s = seconds; return p; } /* Parse a Unix date from which the leading week-day has been stripped. * The syntax is "Jun 21 06:45:44 CDT 1989" with timezone optional. * i.e., month day time [ zone ] year * where day::=digit*, year and time are as defined above, * and month and zone are alpha strings starting with a known 3-char prefix. * The month has already been processed by the caller, so we just skip over * a leading alpha* WS. * * Unlike the preceding routines, the result is not an updated pointer, but * simply 1 for success and 0 for failure. */ static int get_unix_date(p,y,d,m,s,t) char *p; int *y, *d, *m, *s, *t; { SKIP_ALPHA(p); SKIP_WS(p); if (!isdigit(*p)) return 0; *d = atoi(p); /* check the value for sanity after we know the month */ SKIP_DIGITS(p); SKIP_WS(p); p = get_time(p,m,s); if (!p) return 0; if (isalpha(*p)) { *t = tz_lookup(p); SKIP_ALPHA(p); SKIP_WS(p); } else *t = 0; p = get_year(p,y); if (!p) return 0; return 1; } /* Parse an rfc822 (with extensions) date. Return 1 on success, 0 on failure. */ parse_arpa_date(string, entry) char *string; struct header_rec *entry; { char buffer[BUFSIZ], *p, *q; int mday, month, year, minutes, seconds, tz, i; long gmttime; /* first get everything into lower case */ for (p=buffer, q=buffer+sizeof buffer; *string && p<q; p++, string++) { *p = isupper(*string) ? tolower(*string) : *string; } *p = 0; p = buffer; SKIP_WS(p); if (prefix(day_name,p)>=0) { /* accept anything that *starts* with a valid day name */ /* also, don't check whether it's right! */ (void)strncpy(entry->dayname, p, 3); entry->dayname[3] = 0; SKIP_ALPHA(p); SKIP_WS(p); if (*p==',') { p++; SKIP_WS(p); } /* A comma is required here, but we'll be nice guys and look the other * way if it's missing. */ } /* date */ /* day of the month */ if (!isdigit(*p)) { /* Missing day. Maybe this is a Unix date? */ month = prefix(month_name,p); if (month >= 0 && get_unix_date(p, &year, &mday, &minutes, &seconds, &tz)) { goto got_date; } dprint(5,(debugfile,"missing day: %s\n",p)); return 0; } mday = atoi(p); /* check the value for sanity after we know the month */ SKIP_DIGITS(p); SKIP_WS(p); /* month name */ month = prefix(month_name,p); if (month < 0) { dprint(5,(debugfile,"missing month: %s\n",p)); return 0; } SKIP_ALPHA(p); SKIP_WS(p); /* year */ if (!(p = get_year(p,&year))) return 0; /* time */ if (!(p = get_time(p,&minutes,&seconds))) return 0; /* zone */ for (q=p; *q && !isspace(*q); q++) continue; *q = 0; if (*p=='-' || *p=='+') { char sign = *p++; if (isdigit(*p)) { for (i=0; i<4; i++) { if (!isdigit(p[i])) { dprint(5,(debugfile,"ridiculous numeric timezone: %s\n",p)); return 0; } p[i] -= '0'; } tz = (p[0]*10 + p[1])*60 + p[2]*10 + p[3]; if (sign=='-') tz = -tz; sprintf(entry->time_zone, "%d", tz); } else { /* some brain-damaged dates use a '-' before a symbolic time zone */ SKIP_WS(p); strncpy(entry->time_zone, p, sizeof(entry->time_zone) - 1); tz = tz_lookup(p); } } else { tz = tz_lookup(p); strncpy(entry->time_zone, p, sizeof(entry->time_zone) - 1); } got_date: month_len[1] = (year%4) ? 28 : 29; if (mday<0 || mday>month_len[month]) { dprint(5,(debugfile,"ridiculous day %d of month %d\n",mday,month)); return 0; } /* convert back to symbolic form (silly, but the rest of the program * expects it and I'm not about to change all that!) */ sprintf(entry->year, "%02d", year); sprintf(entry->month, "%s", month_name[month]); entry->month[0] = toupper(entry->month[0]); sprintf(entry->day, "%d", mday); sprintf(entry->time, "%02d:%02d:%02d",minutes/60,minutes%60,seconds); /* shift everything to UTC (aka GMT) before making long time for sorting */ minutes -= tz; if (tz > 0) { /* east of Greenwich */ if (minutes < 0) { if (--mday < 0) { if (--month < 0) { year--; /* don't worry about 1900! */ month = 11; } mday = month_len[month]; } minutes += 24*60; } } if (tz < 0) { /* west of Greenwich */ if (minutes >= 24*60) { if (++mday > month_len[month]) { if (++month >= 12) { year++; /* don't worry about 1999! */ month = 0; } mday = 0; } minutes -= 24*60; } } gmttime = year - 70; /* make base year */ if (gmttime < 0) gmttime += 100; gmttime = gmttime * 365 + (gmttime + 1) / 4; /* now we have days adjusted for leap years */ for (i = 0; i < month; i++) gmttime += month_len[i]; if (month > 1 && (year % 4) == 0) gmttime++; /* now to month adjusted for leap year if after feb */ gmttime += mday - 1; /* and now to the day */ gmttime *= 24 * 60; /* convert to minutes */ gmttime += minutes; entry->time_sent = gmttime * 60; /* now unix seconds since 1/1/70 00:00 GMT */ return 1; } fix_arpa_address(address) char *address; { /** Given a pure ARPA address, try to make it reasonable. This means that if you have something of the form a@b@b make it a@b. If you have something like a%b%c%b@x make it a%b@x... **/ register int host_count = 0, i; char hosts[MAX_HOPS][NLEN]; /* array of machine names */ char *host, *addrptr; /* break down into a list of machine names, checking as we go along */ addrptr = (char *) address; while ((host = get_token(addrptr, "%@", 2)) != NULL) { for (i = 0; i < host_count && ! equal(hosts[i], host); i++) ; if (i == host_count) { strcpy(hosts[host_count++], host); if (host_count == MAX_HOPS) { dprint(2, (debugfile, "Can't build return address - hit MAX_HOPS in fix_arpa_address\n")); error("Can't build return address - hit MAX_HOPS limit!"); return(1); } } else host_count = i + 1; addrptr = NULL; } /** rebuild the address.. **/ address[0] = '\0'; for (i = 0; i < host_count; i++) sprintf(address, "%s%s%s", address, address[0] == '\0'? "" : (i == host_count - 1 ? "@" : "%"), hosts[i]); return(0); } figure_out_addressee(buffer, mail_to) char *buffer; char *mail_to; { /** This routine steps through all the addresses in the "To:" list, initially setting it to the first entry (if mail_to is NULL) or, if the user is found (eg "alternatives") to the current "username". Modified to know how to read quoted names... also modified to look for a comma or eol token and then try to give the maximal useful information when giving the default "to" entry (e.g. "Dave Taylor <taylor@hpldat>" will now give "Dave Taylor" rather than just "Dave") **/ char *address, *bufptr, mybuf[SLEN]; register int index2 = 0; if (equal(mail_to, username)) return; /* can't be better! */ bufptr = (char *) buffer; /* use the string directly */ if (index(buffer,'"') != NULL) { /* we have a quoted string */ while (*bufptr != '"') bufptr++; bufptr++; /* skip the leading quote */ while (*bufptr != '"' && *bufptr) mail_to[index2++] = *bufptr++; mail_to[index2] = '\0'; } else { while ((address = strtok(bufptr, ",\t\n\r")) != NULL) { if (! okay_address(address, "don't match me!")) { strcpy(mail_to, username); /* it's to YOU! */ return; } else if (strlen(mail_to) == 0) { /* it's SOMEthing! */ /** this next bit is kinda gory, but allows us to use the existing routines to parse the address - by pretending it's a From: line and going from there... Ah well - you get what you pay for, right? **/ if (strlen(address) > (sizeof mybuf) - 7) /* ensure it ain't */ address[(sizeof mybuf)-7] = '\0'; /* too long mon! */ sprintf(mybuf, "From: %s", address); parse_arpa_who(mybuf, mail_to, TRUE); /** get_return_name(address, mail_to, FALSE); **/ } bufptr = (char *) NULL; /* set to null */ } } return; }