home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume24 / newsgate / part02 / rfc822.c < prev   
Encoding:
C/C++ Source or Header  |  1991-10-09  |  13.6 KB  |  606 lines

  1. /*
  2. **  Routines to read and write mail and news headers.  The code here
  3. **  is gross and complicated, and it would be nice if somebody rewrote
  4. **  it to be clean and simple, especially the CrackFrom routine.
  5. */
  6. #include "gate.h"
  7. #include <time.h>
  8. #ifdef    RCSID
  9. static char RCS[] =
  10.     "$Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/rfc822.c,v 1.5 91/02/12 14:54:00 rsalz Exp $";
  11. #endif    /* RCSID */
  12.  
  13. #ifdef    HAVE_TIMEB
  14. #include <sys/timeb.h>
  15. #else
  16. struct timeb {
  17.     time_t        time;
  18.     unsigned short    millitm;
  19.     short        timezone;
  20.     short        dstflag;
  21. };
  22. #endif    /* HAVE_TIMEB */
  23.  
  24. extern time_t        time();
  25. extern char        *asctime();
  26. extern struct tm    *gmtime();
  27.  
  28. #define HDR_OTHER        -1
  29. #define HDR_END            FALSE
  30. #define HDR_APPROVED         1
  31. #define HDR_CONTROL         2
  32. #define HDR_DATE         3
  33. #define HDR_DISTRIBUTION     4
  34. #define HDR_EXPIRE         5
  35. #define HDR_FOLLOWTO         6
  36. #define HDR_FROM          7
  37. #define HDR_KEYWORDS         8
  38. #define HDR_MESSAGEID         9
  39. #define HDR_NEWSGROUP         10
  40. #define HDR_ORGANIZATION    11
  41. #define HDR_REFERENCES        12
  42. #define HDR_REPLYTO        13
  43. #define HDR_SENDER        14
  44. #define HDR_SUMMARY        15
  45. #define HDR_TITLE         16
  46.  
  47.  
  48. /*
  49. **  The list of headers we recognize; all others are stripped.
  50. */
  51. typedef struct _HTYPE {
  52.     char    *Name;
  53.     int        Type;
  54. } HTYPE;
  55.  
  56. STATIC HTYPE    HeaderList[] = {
  57.     {    "Approved:",        HDR_APPROVED        },
  58.     {    "Control:",        HDR_CONTROL        },
  59.     {    "Date:",        HDR_DATE        },
  60.     {    "Posted:",        HDR_DATE        },
  61.     {    "Distribution:",    HDR_DISTRIBUTION    },
  62.     {    "Expires:",        HDR_EXPIRE        },
  63.     {    "Followup-To:",        HDR_FOLLOWTO        },
  64.     {    "From:",        HDR_FROM        },
  65.     {    "Keywords:",        HDR_KEYWORDS        },
  66.     {    "Message-ID:",        HDR_MESSAGEID        },
  67.     {    "Newsgroups:",        HDR_NEWSGROUP        },
  68.     {    "Organization:",    HDR_ORGANIZATION    },
  69.     {    "In-Reply-To:",        HDR_REFERENCES        },
  70.     {    "References:",        HDR_REFERENCES        },
  71.     {    "Reply-To:",        HDR_REPLYTO        },
  72.     {    "Sender:",        HDR_SENDER        },
  73.     {    "Summary:",        HDR_SUMMARY        },
  74.     {    "Subject:",        HDR_TITLE        },
  75.     {    "Title:",        HDR_TITLE        },
  76. };
  77.  
  78.  
  79.  
  80. /*
  81. **  Getline is like fgets, but deals with continuation lines.  It also
  82. **  ensures that even if a line that is too long is received, the
  83. **  remainder of the line is thrown away instead of treated like a second
  84. **  line.
  85. */
  86. STATIC char *
  87. Getline(buf, len, fp)
  88.     char        *buf;
  89.     int            len;
  90.     FILE        *fp;
  91. {
  92.     register char    *cp;
  93.     register int    c;
  94.     register int    n;
  95.  
  96.     for (n = 0, cp = buf; n < len && (c = getc(fp)) != EOF && c != '\n'; )
  97.     if (!iscntrl(c) || c == '\b' || c == '\t') {
  98.         *cp++ = c;
  99.         n++;
  100.     }
  101.     if (c == EOF && cp == buf)
  102.     return NULL;
  103.     *cp = '\0';
  104.  
  105.     if (c != '\n')
  106.     /* Line too long - part read didn't fit into a newline */
  107.     while ((c = getc(fp)) != '\n' && c != EOF)
  108.         ;
  109.     else if (cp == buf) {
  110.     /* Don't look for continuation of blank lines */
  111.     *cp++ = '\n';
  112.     *cp = '\0';
  113.     return buf;
  114.     }
  115.  
  116.     while ((c = getc(fp)) == ' ' || c == '\t') {
  117.     /* Continuation line. */
  118.     if ((n += 2) < len) {
  119.         *cp++ = '\n';
  120.         *cp++ = c;
  121.     }
  122.     while ((c = getc(fp)) != '\n' && c != EOF)
  123.         if ((!iscntrl(c) || c == '\b' || c == '\t') && n++ < len)
  124.         *cp++ = c;
  125.     }
  126.     if (n >= len - 1)
  127.     cp = buf + len - 2;
  128.     *cp++ = '\n';
  129.     *cp = '\0';
  130.     if (c != EOF)
  131.     /* push back first char of next header */
  132.     (void)ungetc(c, fp);
  133.     return buf;
  134. }
  135.  
  136.  
  137. /*
  138. **  I guess this is basically strncasecmp
  139. */
  140. STATIC int
  141. prefix(full, pref)
  142.     register char    *full;
  143.     register char    *pref;
  144. {
  145.     register char    fc;
  146.     register char    pc;
  147.  
  148.     while (pc = *pref++) {
  149.     fc = *full++;
  150.     if (isupper(fc))
  151.         fc = tolower(fc);
  152.     if (isupper(pc))
  153.         pc = tolower(pc);
  154.     if (fc != pc)
  155.         return FALSE;
  156.     }
  157.     return TRUE;
  158. }
  159.  
  160.  
  161. STATIC int
  162. HeaderType(p)
  163.     register char    *p;
  164. {
  165.     static int        save = HDR_END;
  166.     register HTYPE    *hp;
  167.     char        *colon;
  168.     char        *space;
  169.  
  170.     /* some consistency checks (i.e. is this really a header line?) */
  171.     if (p == NULL || !isascii(*p) || *p == '\n')
  172.     return save = HDR_END;
  173.  
  174.     if (WHITE(*p))
  175.     /* Continuation line. */
  176.     return save;
  177.  
  178.     /* If we don't get a "<no space> <colon> <space>", it's not a header. */
  179.     if ((colon = IDX(p, ':')) == NULL
  180.      || ((space = IDX(p, ' ')) && space < colon))
  181.     return save = HDR_END;
  182.  
  183.     for (hp = HeaderList; hp < ENDOF(HeaderList); hp++)
  184.     if (prefix(p, hp->Name))
  185.         return save = hp->Type;
  186.     return save = HDR_OTHER;
  187. }
  188.  
  189.  
  190. /*
  191. **  Get the contents of the field of the header line, appending it, with a
  192. **  space delimeter if it's a continuation line.  If there is already
  193. **  something in the header storage, skip this header line and the
  194. **  continuations.
  195. */
  196. STATIC void
  197. getfield(src, dest, size)
  198.     register char    *src;
  199.     register char    *dest;
  200.     register int    size;
  201. {
  202.     static int        skip = FALSE;
  203.     register char    *p;
  204.  
  205.     if (src == NULL || dest == NULL)
  206.     return;
  207.  
  208.     if (WHITE(*src)) {
  209.     /* Continuation line.  If skipping or no room, ignore. */
  210.     if (skip || (size -= strlen(dest)) <= 0)
  211.         return;
  212.     /* Munch all but one whitespace, append it to header. */
  213.     while (*src && WHITE(*src))
  214.         src++;
  215.     *--src = ' ';
  216.     (void)strncat(dest, src, size - 1);
  217.     }
  218.     else {
  219.     skip = FALSE;
  220.     if (*dest) {
  221.         /* Already got a value, so mark this as one to skip. */
  222.         skip = TRUE;
  223.         return;
  224.     }
  225.     if ((src = IDX(src, ':')) == NULL)
  226.         /* Can't happen! */
  227.         return;
  228.     /* Skip colon, eat whitespace. */
  229.     for (src++; *src && WHITE(*src); )
  230.         src++;
  231.     (void)strncpy(dest, src, size - 1);
  232.     }
  233.  
  234.     /* Munch trailing whitespace. */
  235.     for (p = dest + strlen(dest); --p >= dest && (*p == '\n' || WHITE(*p)); )
  236.     ;
  237.     p[1] = '\0';
  238. }
  239.  
  240.  
  241. STATIC time_t
  242. cgtdate(datestr)
  243.     char        *datestr;
  244. {
  245.     static time_t    lasttime;
  246.     static char        save[SM_SIZE];
  247.     char        junk[40];
  248.     char        month[40];
  249.     char        day[30];
  250.     char        tod[60];
  251.     char        year[50];
  252.     char        buf[SM_SIZE];
  253.     extern time_t    getdate();
  254.  
  255.     if (save[0] && EQ(datestr, save))
  256.     return lasttime;
  257.     lasttime = getdate(datestr, (struct timeb *)NULL);
  258.     if (lasttime < 0 &&
  259.       sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) {
  260.     (void)sprintf(buf, "%s %s, %s %s", month, day, year, tod);
  261.     lasttime = getdate(buf, (struct timeb *)NULL);
  262.     }
  263.     (void)strncpy(save, datestr, sizeof save);
  264.     return lasttime;
  265. }
  266.  
  267.  
  268. /*
  269. **  Print the date in ARPA format, not ctime(3) format.
  270. */
  271. STATIC void
  272. DoDate(fp, ud)
  273.     FILE        *fp;
  274.     register char    *ud;
  275. {
  276.     register char    *p;
  277.     register char    *q;
  278.     register int    i;
  279.     char        buff[SM_SIZE];
  280.  
  281.     q = buff;
  282.  
  283. #if    0
  284.     /* until every site installs the fix to getdate.y, the day
  285.      * of the week can cause time warps */
  286.     /* "Mon, " */
  287.     p = &ud[0]; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ','; *q++ = ' ';
  288. #endif    /* 0 */
  289.  
  290.     /* "16" */
  291.     p = &ud[8];
  292.     if (*p == ' ')
  293.     p++;
  294.     else
  295.     *q++ = *p++;
  296.     *q++ = *p++; *q++ = ' ';
  297.  
  298.     /* "Sep " */
  299.     p = &ud[4]; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' ';
  300.  
  301.     /* "79 " */
  302.     p = &ud[22]; *q++ = *p++; *q++ = *p++; *q++ = ' ';
  303.  
  304.     /* "01:03:52" */
  305.     for (p = &ud[11], i = 8; i > 0; i--)
  306.     *q++ = *p++;
  307.  
  308.     /* " GMT" */
  309.     *q++ = ' '; *q++ = 'G'; *q++ = 'M'; *q++ = 'T'; *q = '\0';
  310.  
  311.     Fprintf(fp, "Date: %s\n", buff);
  312. }
  313.  
  314.  
  315. /*
  316. **  Strip leading and trailing spaces; returns pointer to first non-space.
  317. */
  318. STATIC char *
  319. StripSpaces(s)
  320.     register char    *s;
  321. {
  322.     register char    *cp;
  323.  
  324.     if (s == NULL || *s == '\0')
  325.     return s;
  326.  
  327.     /* Skip leading spaces. */
  328.     while (*s && isspace(*s))
  329.     s++;
  330.  
  331.     /* zap trailing spaces */
  332.     for (cp = &s[strlen(s) - 1]; cp > s && isspace(*cp); )
  333.     cp--;
  334.     cp[1] = '\0';
  335.     return s;
  336. }
  337.  
  338.  
  339. /*
  340. **  Crack an RFC822 from header field into address and fullname.  We do
  341. **  this to make sure we write things out in official form.  "Be liberal
  342. **  in what you accept, conservative in what you generate."  Anyhow, we
  343. **  read things into three buffers, one for all <...> text, one for all
  344. **  (...) text, and a third for stuff not in either.  Either the first or
  345. **  third buffer will be the real address, depending on whether there is
  346. **  anything in buffer two or not.
  347. */
  348. int
  349. CrackFrom(addr, name, p)
  350.     char        *addr;
  351.     char        *name;
  352.     char        *p;
  353. {
  354.     register char    *ap;
  355.     register char    *cp;
  356.     register int    flag;
  357.     register int    comment;
  358.     register int    address;
  359.     register int    addrfound;
  360.     char        *comm;
  361.     char        commbuf[LG_SIZE];
  362.     char        addrbuf[LG_SIZE];
  363.  
  364.     /* Just to make sure. */
  365.     *name = '\0';
  366.     *addr = '\0';
  367.  
  368.     if (p == NULL)
  369.     return FALSE;
  370.  
  371.     /* Eat leading white space. */
  372.     while (*p && isspace(*p))
  373.     p++;
  374.  
  375.     /* Set defaults.  Start with an allocated copy of a comment string. */
  376.     comm = COPY("");
  377.     ap = addrbuf;
  378.     comment = 0;
  379.     addrfound = 0;
  380.     address = 0;
  381.     for (flag = 0; *p; p++) {
  382.     switch (*p) {
  383.     case '"':
  384.         if (flag) {
  385.         flag--;
  386.         goto EndComment;
  387.         }
  388.         flag++;
  389.         /* FALLTHROUGH */
  390.     case '(':
  391.         if (comment == 0) {
  392.         cp = commbuf;
  393.         *cp = '\0';
  394.         }
  395.         comment++;
  396.         break;
  397.     case ')':
  398.     EndComment:
  399.         if (comment > 0 && --comment == 0) {
  400.         if (*p != ')' && *p != '"')
  401.             *cp++ = *p;        /* Copy the comment-closer */
  402.         *cp = '\0';
  403.         if (*comm == '\0') {
  404.             free(comm);
  405.             comm = COPY(&commbuf[1]);
  406.         }
  407.         else {
  408.             cp = NEW(char, strlen(comm) + 2 + strlen(&commbuf[1]) + 1);
  409.             (void)sprintf(cp, "%s, %s", comm, &commbuf[1]);
  410.             free(comm);
  411.             comm = cp;
  412.         }
  413.         cp = NULL;
  414.         continue;
  415.         }
  416.         break;
  417.     case '<':
  418.         if (address) {
  419.         free(comm);
  420.         return FALSE;    /* AWK! Abort! */
  421.         }
  422.         if (!comment) {
  423.         address++;
  424.         *ap = '\0';
  425.         ap = addr;
  426.         }
  427.         break;
  428.     case '>':
  429.         if (!comment && address) {
  430.         address--;
  431.         addrfound++;
  432.         *ap = '\0';
  433.         ap = &addrbuf[strlen(addrbuf)];
  434.         p++;    /* skip the `>' */
  435.         }
  436.         break;
  437.     }
  438.  
  439.     if (comment)
  440.         *cp++ = *p;
  441.     else if (!address || *p != '<')
  442.         *ap++ = *p;
  443.     if (*p == '\0')
  444.         break;
  445.     }
  446.  
  447.     *ap++ = '\0';
  448.  
  449.     if (addrfound) {
  450.     Strcpy(name, StripSpaces(addrbuf));
  451.     Strcpy(addr, strcpy(commbuf, StripSpaces(addr)));
  452.     }
  453.     else {
  454.     (void)strcpy(addr, StripSpaces(addrbuf));
  455.     *name = '\0';
  456.     }
  457.     /* Just to be sure that we got the full name, we'll take all of
  458.      * the comments. */
  459.     if (*comm) {
  460.     if (*name)
  461.         Strcat(name, ", ");
  462.     Strcat(name, comm);
  463.     }
  464.     free(comm);
  465.     return TRUE;
  466. }
  467.  
  468.  
  469. /*
  470. **  Write out an RFC822 header, paying no attention to line limits.
  471. **  Ideally, we should do continuations in here...
  472. */
  473. int
  474. rfc822write(hp, fp)
  475.     register HBUF    *hp;
  476.     register FILE    *fp;
  477. {
  478.     time_t        t;
  479.  
  480.     if (hp->path[0])
  481.     Fprintf(fp, "Path: %s\n", hp->path);
  482.     if (hp->from[0])
  483.     Fprintf(fp, "From: %s\n", hp->from);
  484.     if (hp->nbuf[0])
  485.     Fprintf(fp, "Newsgroups: %s\n", hp->nbuf);
  486.     if (hp->title[0])
  487.     Fprintf(fp, "Subject: %s\n", hp->title);
  488.     if (hp->ident[0])
  489.     Fprintf(fp, "Message-ID: %s\n", hp->ident);
  490.     /* Get current time. This will be used resolve the timezone. */
  491.     t = hp->subdate[0] ? cgtdate(hp->subdate) : time((time_t *)NULL);
  492.     DoDate(fp, asctime(gmtime(&t)));
  493.     if (hp->expdate[0])
  494.     Fprintf(fp, "Expires: %s\n", hp->expdate);
  495.     if (hp->followid[0])
  496.     Fprintf(fp, "References: %s\n", hp->followid);
  497.     if (hp->ctlmsg[0])
  498.     Fprintf(fp, "Control: %s\n", hp->ctlmsg);
  499.     if (hp->sender[0])
  500.     Fprintf(fp, "Sender: %s\n", hp->sender);
  501.     if (hp->replyto[0])
  502.     Fprintf(fp, "Reply-To: %s\n", hp->replyto);
  503.     if (hp->followto[0])
  504.     Fprintf(fp, "Followup-To: %s\n", hp->followto);
  505.     if (hp->distribution[0])
  506.     Fprintf(fp, "Distribution: %s\n", hp->distribution);
  507.     if (hp->organization[0])
  508.     Fprintf(fp, "Organization: %s\n", hp->organization);
  509.     if (hp->keywords[0])
  510.     Fprintf(fp, "Keywords: %s\n", hp->keywords);
  511.     if (hp->summary[0])
  512.     Fprintf(fp, "Summary: %s\n", hp->summary);
  513.     if (hp->approved[0])
  514.     Fprintf(fp, "Approved: %s\n", hp->approved);
  515.     Fprintf(fp, "\n");
  516.     return !ferror(fp);
  517. }
  518.  
  519.  
  520. rfc822read(hp, fp, buff, buffsize)
  521.     register HBUF    *hp;
  522.     register FILE    *fp;
  523.     char        *buff;
  524.     int            buffsize;
  525. {
  526.     register int    i;
  527.     long        curpos;
  528.  
  529.     /* Zap out the headers. */
  530.     hp->approved[0] = '\0';
  531.     hp->ctlmsg[0] = '\0';
  532.     hp->subdate[0] = '\0';
  533.     hp->distribution[0] = '\0';
  534.     hp->expdate[0] = '\0';
  535.     hp->followto[0] = '\0';
  536.     hp->from[0] = '\0';
  537.     hp->followid[0] = '\0';
  538.     hp->keywords[0] = '\0';
  539.     hp->ident[0] = '\0';
  540.     hp->nbuf[0] = '\0';
  541.     hp->organization[0] = '\0';
  542.     hp->title[0] = '\0';
  543.     hp->replyto[0] = '\0';
  544.     hp->summary[0] = '\0';
  545.     hp->path[0] = '\0';
  546.     hp->sender[0] = '\0';
  547.  
  548.     i = HeaderType(buff);
  549.     do {
  550.     curpos = ftell(fp);
  551.     switch (i) {
  552.     case HDR_APPROVED:
  553.         getfield(buff, hp->approved, sizeof hp->approved);
  554.         break;
  555.     case HDR_CONTROL:
  556.         getfield(buff, hp->ctlmsg, sizeof hp->ctlmsg);
  557.         break;
  558.     case HDR_DATE:
  559.         getfield(buff, hp->subdate, sizeof hp->subdate);
  560.         break;
  561.     case HDR_DISTRIBUTION:
  562.         getfield(buff, hp->distribution, sizeof hp->distribution);
  563.         break;
  564.     case HDR_EXPIRE:
  565.         getfield(buff, hp->expdate, sizeof hp->expdate);
  566.         break;
  567.     case HDR_FOLLOWTO:
  568.         getfield(buff, hp->followto, sizeof hp->followto);
  569.         break;
  570.     case HDR_FROM:
  571.         getfield(buff, hp->from, sizeof hp->from);
  572.         break;
  573.     case HDR_KEYWORDS:
  574.         getfield(buff, hp->keywords, sizeof hp->keywords);
  575.         break;
  576.     case HDR_MESSAGEID:
  577.         getfield(buff, hp->ident, sizeof hp->ident);
  578.         break;
  579.     case HDR_NEWSGROUP:
  580.         getfield(buff, hp->nbuf, sizeof hp->nbuf);
  581.         break;
  582.     case HDR_ORGANIZATION:
  583.         getfield(buff, hp->organization, sizeof hp->organization);
  584.         break;
  585.     case HDR_REFERENCES:
  586.         getfield(buff, hp->followid, sizeof hp->followid);
  587.         break;
  588.     case HDR_REPLYTO:
  589.         getfield(buff, hp->replyto, sizeof hp->replyto);
  590.         break;
  591.     case HDR_SENDER:
  592.         getfield(buff, hp->sender, sizeof hp->sender);
  593.         break;
  594.     case HDR_SUMMARY:
  595.         getfield(buff, hp->summary, sizeof hp->summary);
  596.         break;
  597.     case HDR_TITLE:
  598.         getfield(buff, hp->title, sizeof hp->title);
  599.         break;
  600.     }
  601.     } while ((i = HeaderType(Getline(buff, buffsize, fp))) != HDR_END);
  602.  
  603.     if (*buff != '\n')
  604.     (void)fseek(fp, curpos, 0);
  605. }
  606.