home *** CD-ROM | disk | FTP | other *** search
/ ftp.uv.es / 2014.11.ftp.uv.es.tar / ftp.uv.es / pub / unix / aix-rs6000 / elm2.3.11.AIX3.1.5.Z / elm2.3.11.AIX3.1.5 / src / addr_util.c next >
C/C++ Source or Header  |  1991-11-26  |  33KB  |  1,223 lines

  1.  
  2. static char rcsid[] = "@(#)$Id: addr_util.c,v 4.1.1.1 90/10/07 20:44:56 syd Exp $";
  3.  
  4. /*******************************************************************************
  5.  *  The Elm Mail System  -  $Revision: 4.1.1.1 $   $State: Exp $
  6.  *
  7.  *             Copyright (c) 1986, 1987 Dave Taylor
  8.  *             Copyright (c) 1988, 1989, 1990 USENET Community Trust
  9.  *******************************************************************************
  10.  * Bug reports, patches, comments, suggestions should be sent to:
  11.  *
  12.  *    Syd Weinstein, Elm Coordinator
  13.  *    elm@DSI.COM            dsinc!elm
  14.  *
  15.  *******************************************************************************
  16.  * $Log:    addr_util.c,v $
  17.  * Revision 4.1.1.1  90/10/07  20:44:56  syd
  18.  * Make time to seconds
  19.  * From: rhg@cpscom
  20.  * 
  21.  * Revision 4.1  90/04/28  22:42:21  syd
  22.  * checkin of Elm 2.3 as of Release PL0
  23.  * 
  24.  *
  25.  ******************************************************************************/
  26.  
  27. /** This file contains addressing utilities 
  28.  
  29. **/
  30.  
  31. #include "headers.h"
  32.  
  33. #include <sys/types.h>
  34. #include <sys/stat.h>
  35. #ifdef PWDINSYS
  36. #  include <sys/pwd.h>
  37. #else
  38. #  include <pwd.h>
  39. #endif
  40.  
  41. #include <ctype.h>
  42.  
  43. #ifdef BSD 
  44. #undef tolower
  45. #undef toupper
  46. #endif
  47.  
  48. char *get_alias_address(), *get_token();
  49. /* char *strtok(), *strcpy(), *strcat(), *strncpy(), *index(), *rindex(); */
  50.  
  51.  
  52. #define SKIP_WS(p) while (isspace(*p)) p++
  53. #define SKIP_ALPHA(p) while (isalpha(*p)) p++
  54. #define SKIP_DIGITS(p) while (isdigit(*p)) p++
  55.  
  56. static char *day_name[8] = {
  57.     "sun", "mon", "tue", "wed", "thu", "fri", "sat", 0
  58. };
  59.  
  60. static char *month_name[13] = {
  61.     "jan", "feb", "mar", "apr",
  62.     "may", "jun", "jul", "aug",
  63.     "sep", "oct", "nov", "dec", 0
  64. };
  65.  
  66. static int month_len[12] = {
  67.     31, 28, 31, 30, 31, 30, 31,
  68.     31, 30, 31, 30, 31 };
  69.  
  70. /* The following time zones are taken from a variety of sources.  They
  71.  * are by no means exhaustive, but seem to include most of those
  72.  * in common usage.  A comprehensive list is impossible, since the same
  73.  * abbreviation is sometimes used to mean different things in different
  74.  * parts of the world.
  75.  */
  76. static struct tzone {
  77.     char *str;
  78.     int offset; /* offset, in minutes, EAST of GMT */
  79. } tzone_info[] = {
  80.     /* the following are from rfc822 */
  81.     "ut", 0, "gmt", 0,
  82.     "est", -5*60, "edt", -4*60,
  83.     "cst", -6*60, "cdt", -5*60,
  84.     "mst", -7*60, "mdt", -6*60,
  85.     "pst", -8*60, "pdt", -7*60,
  86.     "z", 0, /* zulu time (the rest of the military codes are bogus) */
  87.  
  88.     /* these are also popular in Europe */
  89.     "wet", 0*60, "wet dst", 1*60, /* western european */
  90.     "met", 1*60, "met dst", 2*60, /* middle european */
  91.     "eet", 2*60, "eet dst", 3*60, /* eastern european */
  92.     "bst", 1*60, /* ??? british summer time (=+0100) */
  93.  
  94.     /* ... and Canada */
  95.     "ast", -4*60, "adt", -3*60, /* atlantic */
  96.     "nst", -3*60-30, "ndt", -2*60-30, /* newfoundland */
  97.     "yst", -9*60, "ydt", -8*60, /* yukon */
  98.     "hst", -10*60, /* hawaii (not really canada) */
  99.  
  100.     /* ... and Asia */
  101.     "jst", 9*60, /* japan */
  102.     "sst", 8*60, /* singapore */
  103.  
  104.     /* ... and the South Pacific */
  105.     "nzst", 12*60, "nzdt", 13*60, /* new zealand */
  106.     "wst", 8*60, "wdt", 9*60, /* western australia */
  107.     /* there's also central and eastern australia, but they insist on using
  108.      * cst, est, etc., which would be indistinguishable for the us zones */
  109.      (char *) 0, 0
  110. };
  111.  
  112. char *
  113. gcos_name(gcos_field, logname)
  114. char *logname, *gcos_field;
  115. {
  116.     /** Return the full name found in a passwd file gcos field **/
  117.  
  118. #ifdef BERKNAMES
  119.  
  120.     static char fullname[SLEN];
  121.     register char *fncp, *gcoscp, *lncp, *end;
  122.  
  123.  
  124.     /* full name is all chars up to first ',' (or whole gcos, if no ',') */
  125.     /* replace any & with logname in upper case */
  126.  
  127.     for(fncp = fullname, gcoscp= gcos_field, end = fullname + SLEN - 1;
  128.         (*gcoscp != ',' && *gcoscp != '\0' && fncp != end);
  129.     gcoscp++) {
  130.  
  131.     if(*gcoscp == '&') {
  132.         for(lncp = logname; *lncp; fncp++, lncp++)
  133.         *fncp = toupper(*lncp);
  134.     } else {
  135.         *fncp++ = *gcoscp;
  136.     }
  137.     }
  138.     
  139.     *fncp = '\0';
  140.     return(fullname);
  141. #else
  142. #ifdef USGNAMES
  143.  
  144.     char *firstcp, *lastcp;
  145.  
  146.     /* The last character of the full name is the one preceding the first
  147.      * '('. If there is no '(', then the full name ends at the end of the
  148.      * gcos field.
  149.      */
  150.     if(lastcp = index(gcos_field, '('))
  151.     *lastcp = '\0';
  152.  
  153.     /* The first character of the full name is the one following the 
  154.      * last '-' before that ending character. NOTE: that's why we
  155.      * establish the ending character first!
  156.      * If there is no '-' before the ending character, then the fullname
  157.      * begins at the beginning of the gcos field.
  158.      */
  159.     if(firstcp = rindex(gcos_field, '-'))
  160.     firstcp++;
  161.     else
  162.     firstcp = gcos_field;
  163.  
  164.     return(firstcp);
  165.  
  166. #else
  167.     /* use full gcos field */
  168.     return(gcos_field);
  169. #endif
  170. #endif
  171. }
  172.         
  173. char *
  174. get_full_name(logname)
  175. char *logname;
  176. {
  177.     /* return a pointer to the full user name for the passed logname
  178.      * or NULL if cannot be found
  179.      * If PASSNAMES get it from the gcos field, otherwise get it
  180.      * from ~/.fullname.
  181.      */
  182.  
  183. #ifndef PASSNAMES
  184.     FILE *fp;
  185.     char fullnamefile[SLEN];
  186. #endif
  187.     static char fullname[SLEN];
  188.     struct passwd *getpwnam(), *pass;
  189.  
  190.     if((pass = getpwnam(logname)) == NULL)
  191.       return(NULL);
  192. #ifdef PASSNAMES    /* get full_username from gcos field */
  193.     strcpy(fullname, gcos_name(pass->pw_gecos, logname));
  194. #else            /* get full_username from ~/.fullname file */
  195.     sprintf(fullnamefile, "%s/.fullname", pass->pw_dir);
  196.  
  197.     if(can_access(fullnamefile, READ_ACCESS) != 0)
  198.       return(NULL);        /* fullname file not accessible to user */
  199.     if((fp = fopen(fullnamefile, "r")) == NULL)
  200.       return(NULL);        /* fullname file cannot be opened! */
  201.     if(fgets(fullname, SLEN, fp) == NULL) {
  202.       fclose(fp);
  203.       return(NULL);        /* fullname file empty! */
  204.     }
  205.     fclose(fp);
  206.     no_ret(fullname);    /* remove trailing '\n' */
  207. #endif
  208.     return(fullname);
  209. }
  210.  
  211. int
  212. talk_to(sitename)
  213. char *sitename;
  214. {
  215.     /** If we talk to the specified site, return true, else
  216.         we're going to have to expand this baby out, so 
  217.         return false! **/
  218.  
  219.     struct lsys_rec  *sysname;
  220.  
  221.     sysname = talk_to_sys;
  222.  
  223.     if (sysname == NULL) {
  224.      dprint(2, (debugfile, 
  225.         "Warning: talk_to_sys is currently set to NULL!\n"));
  226.      return(0);
  227.     }
  228.  
  229.     while (sysname != NULL) {
  230.       if (strcmp(sysname->name, sitename) == 0)
  231.         return(1);
  232.       else
  233.         sysname = sysname->next;
  234.     }
  235.  
  236.     return(0);
  237. }
  238.  
  239. add_site(buffer, site, lastsite)
  240. char *buffer, *site, *lastsite;
  241. {
  242.     /** add site to buffer, unless site is 'uucp' or site is
  243.         the same as lastsite.   If not, set lastsite to site.
  244.     **/
  245.  
  246.     char local_buffer[SLEN], *stripped;
  247.     char *strip_parens();
  248.  
  249.     stripped = strip_parens(site);
  250.  
  251.     if (strcmp(stripped, "uucp") != 0)
  252.       if (strcmp(stripped, lastsite) != 0) {
  253.         if (buffer[0] == '\0')
  254.           strcpy(buffer, stripped);         /* first in list! */
  255.         else {
  256.           sprintf(local_buffer,"%s!%s", buffer, stripped);
  257.           strcpy(buffer, local_buffer);
  258.         }
  259.         strcpy(lastsite, stripped); /* don't want THIS twice! */
  260.       }
  261. }
  262.  
  263. #ifdef USE_EMBEDDED_ADDRESSES
  264.  
  265. get_address_from(prefix, line, buffer)
  266. char *prefix, *line, *buffer;
  267. {
  268.     /** This routine extracts the address from either a 'From:' line
  269.         or a 'Reply-To:' line.  The strategy is as follows:  if the
  270.         line contains a '<', then the stuff enclosed is returned.
  271.         Otherwise we go through the line and strip out comments
  272.         and return that.  White space will be elided from the result.
  273.     **/
  274.  
  275.     register char *s;
  276.  
  277.     /**  Skip start of line over prefix, e.g. "From:".  **/
  278.     line += strlen(prefix);
  279.  
  280.     /**  If there is a '<' then copy from it to '>' into the buffer.  **/
  281.     if ( (s = index(line,'<')) != NULL ) {
  282.     while ( ++s , *s != '\0' && *s != '>' ) {
  283.         if ( !isspace(*s) )
  284.         *buffer++ = *s;
  285.     }
  286.     *buffer = '\0';
  287.     return;
  288.     }
  289.  
  290.     /**  Otherwise, strip comments and get address with whitespace elided.  **/
  291.     for ( s = strip_parens(line) ; *s != '\0' ; ++s ) {
  292.     if ( !isspace(*s) )
  293.         *buffer++ = *s;
  294.     }
  295.     *buffer = '\0';
  296.  
  297. }
  298.  
  299. #endif
  300.  
  301. translate_return(addr, ret_addr)
  302. char *addr, *ret_addr;
  303. {
  304.     /** Return ret_addr to be the same as addr, but with the login 
  305.             of the person sending the message replaced by '%s' for 
  306.             future processing... 
  307.         Fixed to make "%xx" "%%xx" (dumb 'C' system!) 
  308.     **/
  309.  
  310.     register int loc, loc2, iindex = 0;
  311.     
  312.     loc2 = chloc(addr,'@');
  313.     if ((loc = chloc(addr, '%')) < loc2)
  314.       loc2 = loc;
  315.  
  316.     if (loc2 != -1) {    /* ARPA address. */
  317.       /* algorithm is to get to '@' sign and move backwards until
  318.          we've hit the beginning of the word or another metachar.
  319.       */
  320.       for (loc = loc2 - 1; loc > -1 && addr[loc] != '!'; loc--)
  321.          ;
  322.     }
  323.     else {            /* usenet address */
  324.       /* simple algorithm - find last '!' */
  325.  
  326.       loc2 = strlen(addr);    /* need it anyway! */
  327.  
  328.       for (loc = loc2; loc > -1 && addr[loc] != '!'; loc--)
  329.           ;
  330.     }
  331.     
  332.     /** now copy up to 'loc' into destination... **/
  333.  
  334.     while (iindex <= loc) {
  335.       ret_addr[iindex] = addr[iindex];
  336.       iindex++;
  337.     }
  338.  
  339.     /** now append the '%s'... **/
  340.  
  341.     ret_addr[iindex++] = '%';
  342.     ret_addr[iindex++] = 's';
  343.  
  344.     /** and, finally, if anything left, add that **/
  345.  
  346.     loc = strlen(addr);
  347.     while (loc2 < loc) {
  348.       ret_addr[iindex++] = addr[loc2++];
  349.       if (addr[loc2-1] == '%')    /* tweak for "printf" */
  350.         ret_addr[iindex++] = '%';
  351.     }
  352.     
  353.     ret_addr[iindex] = '\0';
  354. }
  355.  
  356. int
  357. build_address(to, full_to)
  358. char *to, *full_to;
  359. {
  360.     /** loop on all words in 'to' line...append to full_to as
  361.         we go along, until done or length > len.  Modified to
  362.         know that stuff in parens are comments...Returns non-zero
  363.         if it changed the information as it copied it across...
  364.     **/
  365.  
  366.     register int i, j, changed = 0, in_parens = 0, expanded_information = 0;
  367.     char word[SLEN], next_word[SLEN], *ptr, buffer[SLEN];
  368.     char new_to_list[SLEN];
  369.     char *strpbrk(), *strcat(), *gecos;
  370. #ifndef DONT_TOUCH_ADDRESSES
  371.     char *expand_system();
  372. #endif
  373.  
  374.     new_to_list[0] = '\0';
  375.  
  376.     i = get_word(to, 0, word);
  377.  
  378.     full_to[0] = '\0';
  379.  
  380.     while (i > 0) {
  381.  
  382.       j = get_word(to, i, next_word);
  383.  
  384. try_new_word:
  385.       if(word[0] == '(')
  386.         in_parens++;
  387.  
  388.       if (in_parens) {
  389.         if(word[strlen(word)-1] == ')')
  390.           in_parens--;
  391.         strcat(full_to, " ");
  392.         strcat(full_to, word);
  393.       }
  394.       else if (strpbrk(word,"!@:") != NULL) {
  395. #ifdef DONT_TOUCH_ADDRESSES
  396.         sprintf(full_to, "%s%s%s", full_to,
  397.                     full_to[0] != '\0'? ", " : "", word);
  398. #else
  399.         sprintf(full_to, "%s%s%s", full_to,
  400.                     full_to[0] != '\0'? ", " : "", expand_system(word, 1));
  401. #endif
  402.       }
  403.       else if ((ptr = get_alias_address(word, TRUE)) != NULL) {
  404.         sprintf(full_to, "%s%s%s", full_to, 
  405.                     full_to[0] != '\0'? ", " : "", ptr);
  406.         expanded_information++;
  407.       }
  408.       else if (strlen(word) > 0) {
  409.         if (valid_name(word)) {
  410.           if (j > 0 && next_word[0] == '(')    /* already has full name */
  411.         gecos = NULL;
  412.           else                /* needs a full name */
  413.         gecos=get_full_name(word);
  414. #if defined(INTERNET) & defined(USE_DOMAIN)
  415.           sprintf(full_to, "%s%s%s@%s%s%s%s",
  416.               full_to,
  417.               (full_to[0] != '\0'? ", " : ""),
  418.               word,
  419.               hostfullname,
  420.               (gecos ? " (" : ""),
  421.               (gecos ? gecos : ""),
  422.               (gecos ? ")" : ""));
  423. #else /* INTERNET and USE_DOMAIN */
  424.           sprintf(full_to, "%s%s%s%s%s%s",
  425.               full_to,
  426.               (full_to[0] != '\0'? ", " : ""),
  427.               word,
  428.               (gecos ? " (" : ""),
  429.               (gecos ? gecos : ""),
  430.               (gecos ? ")" : ""));
  431. #endif /* INTERNET and USE_DOMAIN */
  432.         } else if (check_only) {
  433.           printf("(alias \"%s\" is unknown)\n\r", word);
  434.           changed++;
  435.         }
  436.         else if (! isatty(fileno(stdin)) ) {    /* batch mode error! */
  437.           fprintf(stderr,"Cannot expand alias '%s'!\n\r", word);
  438.           fprintf(stderr,"Use \"checkalias\" to find valid addresses!\n\r");
  439.           dprint(1, (debugfile,
  440.               "Can't expand alias %s - bailing out of build_address\n", 
  441.               word));
  442.           leave(0);
  443.         }
  444.         else {
  445.           dprint(2,(debugfile,"Entered unknown address %s\n", word));
  446.           sprintf(buffer, "'%s' is an unknown address.  Replace with: ", 
  447.                   word);
  448.           word[0] = '\0';
  449.           changed++;
  450.  
  451.           PutLine0(LINES, 0, buffer);
  452.         
  453.           (void)optionally_enter(word, LINES, strlen(buffer), FALSE, FALSE);
  454.           clear_error();
  455.           if (strlen(word) > 0) {
  456.             dprint(3,(debugfile, "Replaced with %s in build_address\n", 
  457.              word));
  458.         goto try_new_word;
  459.           }
  460.           else
  461.         dprint(3,(debugfile, 
  462.             "Address removed from TO list by build_address\n"));
  463.           continue;
  464.         }
  465.       }
  466.  
  467.       /* and this word to the new to list */
  468.       if(*new_to_list != '\0')
  469.         strcat(new_to_list, " ");
  470.       strcat(new_to_list, word);
  471.  
  472.       if((i = j) > 0)
  473.         strcpy(word, next_word);
  474.     }
  475.  
  476.     /* if new to list is different from original, update original */
  477.     if (changed)
  478.       strcpy(to, new_to_list);
  479.  
  480.     return( expanded_information > 0 ? 1 : 0 );
  481. }
  482.  
  483. int
  484. real_from(buffer, entry)
  485. char *buffer;
  486. struct header_rec *entry;
  487. {
  488.     /***** Returns true iff 's' has the seven 'from' fields, (or
  489.            8 - some machines include the TIME ZONE!!!)
  490.            Initialize the date and from entries in the record 
  491.            and also the message received date/time if 'entry'
  492.            is not NULL.  *****/
  493.  
  494.     struct header_rec temp_rec, *rec_ptr;
  495.     char junk[STRING], timebuff[STRING], holding_from[SLEN], hold_tz[12];
  496.     char mybuf[BUFSIZ], *p, *q;
  497.     int  eight_fields = 0;
  498.         int mday, month, year, minutes, seconds, tz, i;
  499.         long gmttime;
  500.  
  501.     /* set rec_ptr according to whether the data is to be returned
  502.      * in the second argument */
  503.     rec_ptr = (entry == NULL ? &temp_rec : entry);
  504.  
  505.     rec_ptr->year[0] = '\0';
  506.     timebuff[0] = '\0';
  507.     junk[0] = '\0';
  508.     hold_tz[0] = '\0';
  509.  
  510.     /* From <user> <day> <month> <day> <hr:min:sec> <year> */
  511.  
  512.     sscanf(buffer, "%*s %*s %*s %*s %*s %s %*s %s", timebuff, junk);
  513.  
  514.     if (strlen(timebuff) < 3) {
  515.       dprint(3,(debugfile, 
  516.         "Real_from returns FAIL [no time field] on\n-> %s\n", 
  517.         buffer));
  518.       return(FALSE);
  519.     }
  520.  
  521.     if (timebuff[1] != ':' && timebuff[2] != ':') { 
  522.       dprint(3,(debugfile, 
  523.         "Real_from returns FAIL [bad time field] on\n-> %s\n", 
  524.         buffer));
  525.       return(FALSE);
  526.     }
  527.     if (junk[0] != '\0') {    /* try for 8 field entry */
  528.       junk[0] = '\0';
  529.       sscanf(buffer, "%*s %*s %*s %*s %*s %s %*s %*s %s", timebuff, junk);
  530.       if (junk[0] != '\0') {
  531.         dprint(3, (debugfile, 
  532.           "Real_from returns FAIL [too many fields] on\n-> %s\n", 
  533.           buffer));
  534.         return(FALSE);
  535.       }
  536.       eight_fields++;
  537.     }
  538.  
  539.     /** now get the info out of the record! **/
  540.  
  541.     if (eight_fields) 
  542.       sscanf(buffer, "%s %s %s %s %s %s %s %s",
  543.                 junk, holding_from, rec_ptr->dayname, rec_ptr->month, 
  544.                     rec_ptr->day, rec_ptr->time, hold_tz, rec_ptr->year);
  545.     else
  546.       sscanf(buffer, "%s %s %s %s %s %s %s",
  547.                 junk, holding_from, rec_ptr->dayname, rec_ptr->month, 
  548.                     rec_ptr->day, rec_ptr->time, rec_ptr->year);
  549.     
  550.     strncpy(rec_ptr->from, holding_from, STRING-1);
  551.     rec_ptr->from[STRING-1] = '\0';
  552.     resolve_received(rec_ptr);
  553.  
  554.         /* first get everything into lower case */
  555.         for (p=mybuf, q=mybuf+sizeof mybuf; 
  556.          *buffer && p<q; 
  557.          p++, buffer++) {
  558.       *p = isupper(*buffer) ? tolower(*buffer) : *buffer;
  559.         }
  560.     *p = 0;
  561.     p = mybuf;
  562.     while (!isspace(*p)) p++;    /* skip "from" */
  563.     SKIP_WS(p);
  564.     while (!isspace(*p)) p++;    /* skip from address */
  565.     SKIP_WS(p);
  566.     while (!isspace(*p)) p++;    /* skip day of week */
  567.     SKIP_WS(p);
  568.     month = prefix(month_name, p);
  569.     get_unix_date(p,&year, &mday, &minutes, &seconds, &tz);
  570.     month_len[1] = (year%4) ? 28 : 29;
  571.     if (mday < 0 || mday>month_len[month]) {
  572.       dprint(5,(debugfile, "ridiculous day %d of month %d\n",mday,month));
  573.     }
  574.  
  575.     minutes -= tz;
  576.     if (tz > 0) { /* east of Greenwich */
  577.       if (minutes < 0) {
  578.         if (--mday < 0) {
  579.           if (--month < 0) {
  580.         year--; /* don't worry about 1900! */
  581.         month = 11;
  582.           }
  583.           mday = month_len[month];
  584.         }
  585.         minutes += 24*60;
  586.       }
  587.     }
  588.     if (tz < 0) { /* west of Greenwich */
  589.       if (minutes <= 24*60) {
  590.         if (++mday > month_len[month]) {
  591.           if (++month >= 12) {
  592.         year++; /* don't worry about 1999! yet?? */
  593.         month = 0;
  594.           }
  595.           mday = 0;
  596.         }
  597.         minutes -= 24*60;
  598.       }
  599.     }
  600.         gmttime = year - 70;         /* make base year */
  601.         if (gmttime < 0)
  602.         gmttime += 100;
  603.         gmttime = gmttime * 365 + (gmttime + 1) / 4;  /* now we have days adjusted for leap years */
  604.         for (i = 0; i < month; i++)
  605.         gmttime += month_len[i];
  606.         if (month > 1 && (year % 4) == 0)
  607.         gmttime++;            /* now to month adjusted for leap year if after feb */
  608.         gmttime += mday - 1;        /* and now to the day */
  609.         gmttime *= 24 * 60;            /* convert to minutes */
  610.         gmttime += minutes;
  611.         rec_ptr->time_sent = gmttime * 60 + seconds;    /* now unix seconds since 1/1/70 00:00 GMT */
  612.  
  613.     return(rec_ptr->year[0] != '\0');
  614. }
  615.  
  616. forwarded(buffer, entry)
  617. char *buffer;
  618. struct header_rec *entry;
  619. {
  620.     /** Change 'from' and date fields to reflect the ORIGINATOR of 
  621.         the message by iteratively parsing the >From fields... 
  622.         Modified to deal with headers that include the time zone
  623.         of the originating machine... **/
  624.  
  625.     char machine[SLEN], buff[SLEN], holding_from[SLEN];
  626.  
  627.     machine[0] = holding_from[0] = '\0';
  628.  
  629.     sscanf(buffer, "%*s %s %s %s %s %s %s %*s %*s %s",
  630.                 holding_from, entry->dayname, entry->month, 
  631.                     entry->day, entry->time, entry->year, machine);
  632.  
  633.     if (isdigit(entry->month[0])) { /* try for veeger address */
  634.       sscanf(buffer, "%*s %s %s%*c %s %s %s %s %*s %*s %s",
  635.                 holding_from, entry->dayname, entry->day, entry->month, 
  636.                     entry->year, entry->time, machine);
  637.     }
  638.     if (isalpha(entry->year[0])) { /* try for address including tz */
  639.       sscanf(buffer, "%*s %s %s %s %s %s %*s %s %*s %*s %s",
  640.                 holding_from, entry->dayname, entry->month, 
  641.                     entry->day, entry->time, entry->year, machine);
  642.     }
  643.  
  644.     /* the following fix is to deal with ">From xyz ... forwarded by xyz"
  645.        which occasionally shows up within AT&T.  Thanks to Bill Carpenter
  646.        for the fix! */
  647.  
  648.     if (strcmp(machine, holding_from) == 0)
  649.       machine[0] = '\0';
  650.  
  651.     if (machine[0] == '\0')
  652.       strcpy(buff, holding_from[0] ? holding_from : "anonymous");
  653.     else
  654.       sprintf(buff,"%s!%s", machine, holding_from);
  655.  
  656.     strncpy(entry->from, buff, STRING-1);
  657.     entry->from[STRING-1] = '\0';
  658. }
  659.  
  660. parse_arpa_who(buffer, newfrom, is_really_a_to)
  661. char *buffer, *newfrom;
  662. int is_really_a_to;
  663. {
  664.     /** try to parse the 'From:' line given... It can be in one of
  665.         two formats:
  666.         From: Dave Taylor <hplabs!dat>
  667.         or  From: hplabs!dat (Dave Taylor)
  668.  
  669.         Added: removes quotes if name is quoted (12/12)
  670.         Added: only copies STRING characters...
  671.         Added: if no comment part, copy address instead! 
  672.         Added: if is_really_a_to, this is really a 'to' line
  673.            and treat as if we allow embedded addresses
  674.     **/
  675.  
  676.     int use_embedded_addresses;
  677.     char temp_buffer[SLEN], *temp;
  678.     register int i, j = 0, in_parens;
  679.  
  680.     temp = (char *) temp_buffer;
  681.     temp[0] = '\0';
  682.  
  683.     no_ret(buffer);        /* blow away '\n' char! */
  684.  
  685.     if (lastch(buffer) == '>') {
  686.       for (i=strlen("From: "); buffer[i] != '\0' && buffer[i] != '<' &&
  687.            buffer[i] != '('; i++)
  688.         temp[j++] = buffer[i];
  689.       temp[j] = '\0';
  690.     }
  691.     else if (lastch(buffer) == ')') {
  692.       in_parens = 1;
  693.       for (i=strlen(buffer)-2; buffer[i] != '\0' && buffer[i] != '<'; i--) {
  694.         switch(buffer[i]) {
  695.         case ')':    in_parens++;
  696.             break;
  697.         case '(':    in_parens--;
  698.             break;
  699.         }
  700.         if(!in_parens) break;
  701.         temp[j++] = buffer[i];
  702.       }
  703.       temp[j] = '\0';
  704.       reverse(temp);
  705.     }
  706.  
  707. #ifdef USE_EMBEDDED_ADDRESSES
  708.     use_embedded_addresses = TRUE;
  709. #else
  710.     use_embedded_addresses = FALSE;
  711. #endif
  712.  
  713.     if(use_embedded_addresses || is_really_a_to) {
  714.       /** if we have a null string at this point, we must just have a 
  715.           From: line that contains an address only.  At this point we
  716.           can have one of a few possibilities...
  717.  
  718.           From: address
  719.           From: <address>
  720.           From: address ()
  721.       **/
  722.         
  723.       if (strlen(temp) == 0) {
  724.         if (lastch(buffer) != '>') {       
  725.           for (i=strlen("From:");buffer[i] != '\0' && buffer[i] != '('; i++)
  726.         temp[j++] = buffer[i];
  727.           temp[j] = '\0';
  728.         }
  729.         else {    /* get outta '<>' pair, please! */
  730.           for (i=strlen(buffer)-2;buffer[i] != '<' && buffer[i] != ':';i--)
  731.         temp[j++] = buffer[i];
  732.           temp[j] = '\0';
  733.           reverse(temp);
  734.         }
  735.       }
  736.     }
  737.       
  738.     if (strlen(temp) > 0) {        /* mess with buffer... */
  739.  
  740.       /* remove leading spaces and quotes... */
  741.  
  742.       while (whitespace(temp[0]) || quote(temp[0]))
  743.         temp = (char *) (temp + 1);        /* increment address! */
  744.  
  745.       /* remove trailing spaces and quotes... */
  746.  
  747.       i = strlen(temp) - 1;
  748.  
  749.       while (whitespace(temp[i]) || quote(temp[i]))
  750.        temp[i--] = '\0';
  751.  
  752.       /* if anything is left, let's change 'from' value! */
  753.  
  754.       if (strlen(temp) > 0) {
  755.         strncpy(newfrom, temp, STRING-1);
  756.         newfrom[STRING-1] = '\0';
  757.       }
  758.     }
  759. }
  760.  
  761. /*
  762. Quoting from RFC 822:
  763.      5.  DATE AND TIME SPECIFICATION
  764.  
  765.      5.1.  SYNTAX
  766.  
  767.      date-time   =  [ day "," ] date time        ; dd mm yy
  768.                          ;  hh:mm:ss zzz
  769.  
  770.      day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
  771.          /  "Fri"  / "Sat" /  "Sun"
  772.  
  773.      date        =  1*2DIGIT month 2DIGIT        ; day month year
  774.                          ;  e.g. 20 Jun 82
  775.  
  776.      month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
  777.          /  "May"  /  "Jun" /  "Jul"  /  "Aug"
  778.          /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
  779.  
  780.      time        =  hour zone                    ; ANSI and Military
  781.  
  782.      hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
  783.                          ; 00:00:00 - 23:59:59
  784.  
  785.      zone        =  "UT"  / "GMT"                ; Universal Time
  786.                          ; North American : UT
  787.          /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
  788.          /  "CST" / "CDT"                ;  Central:  - 6/ - 5
  789.          /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
  790.          /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
  791.          /  1ALPHA                       ; Military: Z = UT;
  792.                          ;  A:-1; (J not used)
  793.                          ;  M:-12; N:+1; Y:+12
  794.          / ( ("+" / "-") 4DIGIT )        ; Local differential
  795.                          ;  hours+min. (HHMM)
  796. */
  797.  
  798. /* Translate a symbolic timezone name (e.g. EDT or NZST) to a number of
  799.  * minutes *east* of gmt (if the local time is t, the gmt equivalent is
  800.  * t - tz_lookup(zone)).
  801.  * Return 0 if the timezone is not recognized.
  802.  */
  803. static int tz_lookup(str)
  804. char *str;
  805. {
  806.     struct tzone *p; 
  807.  
  808.     for (p = tzone_info; p->str; p++) {
  809.     if (strcmp(p->str,str)==0) return p->offset;
  810.     }
  811.     dprint(5,(debugfile,"unknown time zone %s\n",str));
  812.     return 0;
  813. }
  814.  
  815. /* Return smallest i such that table[i] is a prefix of str.  Return -1 if not
  816.  * found.
  817.  */
  818. static int prefix(table, str)
  819. char **table;
  820. char *str;
  821. {
  822.     int i;
  823.  
  824.     for (i=0;table[i];i++)
  825.     if (strncmp(table[i],str,strlen(*table))==0)
  826.         return i;
  827.     return -1;
  828. }
  829.  
  830. /* The following routines, get_XXX(p,...), expect p to point to a string
  831.  * of the appropriate syntax.  They return decoded values in result parameters,
  832.  * and return p updated to point past the parsed substring (also stripping
  833.  * trailing whitespace).
  834.  * Return 0 on syntax errors.
  835.  */
  836.  
  837. /* Parse a year: ['1' '9'] digit digit WS
  838.  */
  839. static char *
  840. get_year(p, result)
  841. char *p;
  842. int *result;
  843. {
  844.     int year;
  845.  
  846.     if (!isdigit(*p)) {
  847.     dprint(5,(debugfile,"missing year: %s\n",p));
  848.     return 0;
  849.     }
  850.     year = atoi(p);
  851.     /* be nice and allow 19xx, althought that's not really kosher */
  852.     if (year>=1900 && year <=1999) year -= 1900;
  853.     if (year<0 || year>99) {
  854.     dprint(5,(debugfile,"ridiculous year %d\n",year));
  855.     return 0;
  856.     }
  857.     SKIP_DIGITS(p);
  858.     SKIP_WS(p);
  859.     *result = year;
  860.     return p;
  861. }
  862.  
  863. /* Parse a time: hours ':' minutes [ ':' seconds ] WS
  864.  * Check that 0<=hours<24, 0<=minutes,seconds<60.
  865.  * Also allow the syntax "digit digit digit digit" with implied ':' in the
  866.  * middle.
  867.  * Convert to minutes and seconds, with results in (*m,*s).
  868.  */
  869. static char *
  870. get_time(p,m,s)
  871. char *p;
  872. int *m, *s;
  873. {
  874.     int hours, minutes, seconds;
  875.  
  876.     /* hour */
  877.     if (!isdigit(*p)) {
  878.     dprint(5,(debugfile,"missing time: %s\n",p));
  879.     return 0;
  880.     }
  881.     hours = atoi(p);
  882.     SKIP_DIGITS(p);
  883.     if (*p++ != ':') {
  884.     /* perhaps they just wrote hhmm instead of hh:mm */
  885.     minutes = hours % 60;
  886.     hours /= 60;
  887.     }
  888.     else {
  889.     if (hours<0 || hours>23) {
  890.         dprint(5,(debugfile,"ridiculous hour: %d\n",hours));
  891.         return 0;
  892.     }
  893.     minutes = atoi(p);
  894.     if (minutes<0 || minutes>59) {
  895.         dprint(5,(debugfile,"ridiculous minutes: %d\n",minutes));
  896.         return 0;
  897.     }
  898.     }
  899.     SKIP_DIGITS(p);
  900.     if (*p == ':') {
  901.     p++;
  902.     seconds = atoi(p);
  903.     if (seconds<0 || seconds>59) {
  904.         dprint(5,(debugfile,"ridiculous seconds: %d\n",seconds));
  905.         return 0;
  906.     }
  907.     SKIP_DIGITS(p);
  908.     }
  909.     else seconds = 0;
  910.     minutes += hours*60;
  911.     SKIP_WS(p);
  912.     *m = minutes;
  913.     *s = seconds;
  914.     return p;
  915. }
  916.  
  917. /* Parse a Unix date from which the leading week-day has been stripped.
  918.  * The syntax is "Jun 21 06:45:44 CDT 1989" with timezone optional.
  919.  * i.e., month day time [ zone ] year
  920.  * where day::=digit*, year and time are as defined above,
  921.  * and month and zone are alpha strings starting with a known 3-char prefix.
  922.  * The month has already been processed by the caller, so we just skip over
  923.  * a leading alpha* WS.
  924.  *
  925.  * Unlike the preceding routines, the result is not an updated pointer, but
  926.  * simply 1 for success and 0 for failure.
  927.  */
  928. static int
  929. get_unix_date(p,y,d,m,s,t)
  930. char *p;
  931. int *y, *d, *m, *s, *t;
  932. {
  933.  
  934.     SKIP_ALPHA(p);
  935.     SKIP_WS(p);
  936.     if (!isdigit(*p)) return 0;
  937.     *d = atoi(p);  /* check the value for sanity after we know the month */
  938.     SKIP_DIGITS(p);
  939.     SKIP_WS(p);
  940.     p = get_time(p,m,s);
  941.     if (!p) return 0;
  942.     if (isalpha(*p)) {
  943.     *t = tz_lookup(p);
  944.     SKIP_ALPHA(p);
  945.     SKIP_WS(p);
  946.     }
  947.     else *t = 0;
  948.     p = get_year(p,y);
  949.     if (!p) return 0;
  950.     return 1;
  951. }
  952.  
  953.  
  954. /* Parse an rfc822 (with extensions) date.  Return 1 on success, 0 on failure.
  955.  */
  956. parse_arpa_date(string, entry)
  957. char *string;
  958. struct header_rec *entry;
  959. {
  960.     char buffer[BUFSIZ], *p, *q;
  961.     int mday, month, year, minutes, seconds, tz, i;
  962.     long gmttime;
  963.  
  964.     /* first get everything into lower case */
  965.     for (p=buffer, q=buffer+sizeof buffer; *string && p<q; p++, string++) {
  966.     *p = isupper(*string) ? tolower(*string) : *string;
  967.     }
  968.     *p = 0;
  969.     p = buffer;
  970.     SKIP_WS(p);
  971.  
  972.     if (prefix(day_name,p)>=0) {
  973.     /* accept anything that *starts* with a valid day name */
  974.     /* also, don't check whether it's right! */
  975.  
  976.     (void)strncpy(entry->dayname, p, 3);
  977.     entry->dayname[3] = 0;
  978.     SKIP_ALPHA(p);
  979.     SKIP_WS(p);
  980.  
  981.     if (*p==',') {
  982.         p++;
  983.         SKIP_WS(p);
  984.     }
  985.     /* A comma is required here, but we'll be nice guys and look the other
  986.      * way if it's missing.
  987.      */
  988.     }
  989.  
  990.     /* date */
  991.  
  992.     /* day of the month */
  993.     if (!isdigit(*p)) {
  994.     /* Missing day.  Maybe this is a Unix date?
  995.      */
  996.     month = prefix(month_name,p);
  997.     if (month >= 0 &&
  998.         get_unix_date(p, &year, &mday, &minutes, &seconds, &tz)) {
  999.         goto got_date;
  1000.     }
  1001.     dprint(5,(debugfile,"missing day: %s\n",p));
  1002.     return 0;
  1003.     }
  1004.     mday = atoi(p);  /* check the value for sanity after we know the month */
  1005.     SKIP_DIGITS(p);
  1006.     SKIP_WS(p);
  1007.  
  1008.     /* month name */
  1009.     month = prefix(month_name,p);
  1010.     if (month < 0) {
  1011.     dprint(5,(debugfile,"missing month: %s\n",p));
  1012.     return 0;
  1013.     }
  1014.     SKIP_ALPHA(p);
  1015.     SKIP_WS(p);
  1016.  
  1017.     /* year */
  1018.     if (!(p = get_year(p,&year))) return 0;
  1019.  
  1020.     /* time */
  1021.     if (!(p = get_time(p,&minutes,&seconds))) return 0;
  1022.  
  1023.     /* zone */
  1024.     for (q=p; *q && !isspace(*q); q++) continue;
  1025.     *q = 0;
  1026.     if (*p=='-' || *p=='+') {
  1027.     char sign = *p++;
  1028.  
  1029.     if (isdigit(*p)) {
  1030.         for (i=0; i<4; i++) {
  1031.         if (!isdigit(p[i])) {
  1032.             dprint(5,(debugfile,"ridiculous numeric timezone: %s\n",p));
  1033.             return 0;
  1034.         }
  1035.         p[i] -= '0';
  1036.         }
  1037.         tz = (p[0]*10 + p[1])*60 + p[2]*10 + p[3];
  1038.         if (sign=='-') tz = -tz;
  1039.         sprintf(entry->time_zone, "%d", tz);
  1040.     }
  1041.     else {
  1042.         /* some brain-damaged dates use a '-' before a symbolic time zone */
  1043.         SKIP_WS(p);
  1044.         strncpy(entry->time_zone, p, sizeof(entry->time_zone) - 1);
  1045.         tz = tz_lookup(p);
  1046.     }
  1047.     }
  1048.     else {
  1049.     tz = tz_lookup(p);
  1050.     strncpy(entry->time_zone, p, sizeof(entry->time_zone) - 1);
  1051.     }
  1052.  
  1053. got_date:
  1054.     month_len[1] = (year%4) ? 28 : 29;
  1055.     if (mday<0 || mday>month_len[month]) {
  1056.     dprint(5,(debugfile,"ridiculous day %d of month %d\n",mday,month));
  1057.     return 0;
  1058.     }
  1059.  
  1060.     /* convert back to symbolic form (silly, but the rest of the program
  1061.      * expects it and I'm not about to change all that!)
  1062.      */
  1063.     sprintf(entry->year, "%02d", year);
  1064.     sprintf(entry->month, "%s", month_name[month]);
  1065.     entry->month[0] = toupper(entry->month[0]);
  1066.     sprintf(entry->day, "%d", mday);
  1067.     sprintf(entry->time, "%02d:%02d:%02d",minutes/60,minutes%60,seconds);
  1068.  
  1069.     /* shift everything to UTC (aka GMT) before making long time for sorting */
  1070.     minutes -= tz;
  1071.     if (tz > 0) { /* east of Greenwich */
  1072.     if (minutes < 0) {
  1073.         if (--mday < 0) {
  1074.         if (--month < 0) {
  1075.             year--; /* don't worry about 1900! */
  1076.             month = 11;
  1077.         }
  1078.         mday = month_len[month];
  1079.         }
  1080.         minutes += 24*60;
  1081.     }
  1082.     }
  1083.     if (tz < 0) { /* west of Greenwich */
  1084.     if (minutes >= 24*60) {
  1085.         if (++mday > month_len[month]) {
  1086.         if (++month >= 12) {
  1087.             year++; /* don't worry about 1999! */
  1088.             month = 0;
  1089.         }
  1090.         mday = 0;
  1091.         }
  1092.         minutes -= 24*60;
  1093.     }
  1094.     }
  1095.     gmttime = year - 70;         /* make base year */
  1096.     if (gmttime < 0)
  1097.     gmttime += 100;
  1098.     gmttime = gmttime * 365 + (gmttime + 1) / 4;  /* now we have days adjusted for leap years */
  1099.     for (i = 0; i < month; i++)
  1100.     gmttime += month_len[i];
  1101.     if (month > 1 && (year % 4) == 0)
  1102.     gmttime++;            /* now to month adjusted for leap year if after feb */
  1103.     gmttime += mday - 1;        /* and now to the day */
  1104.     gmttime *= 24 * 60;            /* convert to minutes */
  1105.     gmttime += minutes;
  1106.     entry->time_sent = gmttime * 60;    /* now unix seconds since 1/1/70 00:00 GMT */
  1107.  
  1108.     return 1;
  1109. }
  1110.  
  1111. fix_arpa_address(address)
  1112. char *address;
  1113. {
  1114.     /** Given a pure ARPA address, try to make it reasonable.
  1115.  
  1116.         This means that if you have something of the form a@b@b make 
  1117.             it a@b.  If you have something like a%b%c%b@x make it a%b@x...
  1118.     **/
  1119.  
  1120.     register int host_count = 0, i;
  1121.     char     hosts[MAX_HOPS][NLEN];    /* array of machine names */
  1122.     char     *host, *addrptr;
  1123.  
  1124.     /*  break down into a list of machine names, checking as we go along */
  1125.     
  1126.     addrptr = (char *) address;
  1127.  
  1128.     while ((host = get_token(addrptr, "%@", 2)) != NULL) {
  1129.       for (i = 0; i < host_count && ! equal(hosts[i], host); i++)
  1130.           ;
  1131.  
  1132.       if (i == host_count) {
  1133.         strcpy(hosts[host_count++], host);
  1134.         if (host_count == MAX_HOPS) {
  1135.            dprint(2, (debugfile, 
  1136.            "Can't build return address - hit MAX_HOPS in fix_arpa_address\n"));
  1137.            error("Can't build return address - hit MAX_HOPS limit!");
  1138.            return(1);
  1139.         }
  1140.       }
  1141.       else 
  1142.         host_count = i + 1;
  1143.       addrptr = NULL;
  1144.     }
  1145.  
  1146.     /** rebuild the address.. **/
  1147.  
  1148.     address[0] = '\0';
  1149.  
  1150.     for (i = 0; i < host_count; i++)
  1151.       sprintf(address, "%s%s%s", address, 
  1152.               address[0] == '\0'? "" : 
  1153.              (i == host_count - 1 ? "@" : "%"),
  1154.               hosts[i]);
  1155.  
  1156.     return(0);
  1157. }
  1158.  
  1159. figure_out_addressee(buffer, mail_to)
  1160. char *buffer;
  1161. char *mail_to;
  1162. {
  1163.     /** This routine steps through all the addresses in the "To:"
  1164.         list, initially setting it to the first entry (if mail_to
  1165.         is NULL) or, if the user is found (eg "alternatives") to
  1166.         the current "username".
  1167.  
  1168.         Modified to know how to read quoted names...
  1169.         also modified to look for a comma or eol token and then
  1170.         try to give the maximal useful information when giving the
  1171.         default "to" entry (e.g. "Dave Taylor <taylor@hpldat>"
  1172.         will now give "Dave Taylor" rather than just "Dave")
  1173.     **/
  1174.  
  1175.     char *address, *bufptr, mybuf[SLEN];
  1176.     register int index2 = 0;
  1177.     
  1178.     if (equal(mail_to, username)) return;    /* can't be better! */
  1179.  
  1180.     bufptr = (char *) buffer;           /* use the string directly   */
  1181.  
  1182.     if (index(buffer,'"') != NULL) {    /* we have a quoted string */
  1183.       while (*bufptr != '"')
  1184.         bufptr++;
  1185.       bufptr++;    /* skip the leading quote */
  1186.       while (*bufptr != '"' && *bufptr)
  1187.         mail_to[index2++] = *bufptr++;
  1188.       mail_to[index2] = '\0';
  1189.     }
  1190.  
  1191.     else  {
  1192.  
  1193.       while ((address = strtok(bufptr, ",\t\n\r")) != NULL) {
  1194.  
  1195.         if (! okay_address(address, "don't match me!")) {
  1196.           strcpy(mail_to, username);    /* it's to YOU! */
  1197.           return;
  1198.         }
  1199.         else if (strlen(mail_to) == 0) {    /* it's SOMEthing! */
  1200.     
  1201.           /** this next bit is kinda gory, but allows us to use the
  1202.           existing routines to parse the address - by pretending
  1203.           it's a From: line and going from there...
  1204.               Ah well - you get what you pay for, right?
  1205.           **/
  1206.  
  1207.           if (strlen(address) > (sizeof mybuf) - 7)    /* ensure it ain't */
  1208.         address[(sizeof mybuf)-7] = '\0';    /*  too long mon!  */
  1209.  
  1210.           sprintf(mybuf, "From: %s", address);
  1211.           parse_arpa_who(mybuf, mail_to, TRUE);
  1212. /**
  1213.           get_return_name(address, mail_to, FALSE);
  1214. **/
  1215.         }
  1216.  
  1217.         bufptr = (char *) NULL;    /* set to null */
  1218.       }
  1219.     }
  1220.  
  1221.     return;
  1222. }
  1223.