home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / pine3.07 / imapd / imapd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-27  |  31.0 KB  |  1,138 lines

  1. /*
  2.  * Program:    IMAP2 server
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    5 November 1990
  13.  * Last Edited:    13 May 1992
  14.  *
  15.  * Copyright 1992 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. /* Parameter files */
  37.  
  38. #include <stdio.h>
  39. #include <ctype.h>
  40. #include <netdb.h>
  41. #include <errno.h>
  42. #include <signal.h>
  43. #include <pwd.h>
  44. #include <sys/types.h>
  45. #include <sys/file.h>
  46. #include <sys/stat.h>
  47. #include <sys/time.h>
  48. #include <sys/dir.h>        /* makes news.h happy */
  49. #include "osdep.h"
  50. #include "mail.h"
  51. #include "misc.h"
  52. #include "news.h"        /* moby sigh */
  53.  
  54.  
  55. /* Autologout timer */
  56. #define TIMEOUT 60*30
  57.  
  58.  
  59. /* Size of temporary buffers */
  60. #define TMPLEN 8192
  61.  
  62.  
  63. /* Server states */
  64.  
  65. #define LOGIN 0
  66. #define SELECT 1
  67. #define OPEN 2
  68. #define LOGOUT 3
  69.  
  70. /* Global storage */
  71.  
  72. char *version = "6.6(43)";    /* version number of this server */
  73. struct itimerval timer;        /* timeout state */
  74. int state = LOGIN;        /* server state */
  75. int mackludge = 0;        /* MacMS kludge */
  76. int anonymous = 0;        /* non-zero if anonymous */
  77. MAILSTREAM *stream = NIL;    /* mailbox stream */
  78. long nmsgs = 0;            /* number of messages */
  79. long recent = 0;        /* number of recent messages */
  80. char *host = NIL;        /* local host name */
  81. char *user = NIL;        /* user name */
  82. char *pass = NIL;        /* password */
  83. char *home = NIL;        /* home directory */
  84. char *flags = NIL;        /* flag text */
  85. char cmdbuf[TMPLEN];        /* command buffer */
  86. char *tag;            /* tag portion of command */
  87. char *cmd;            /* command portion of command */
  88. char *arg;            /* pointer to current argument of command */
  89. char *lsterr = NIL;        /* last error message from c-client */
  90. char *response = NIL;        /* command response */
  91.  
  92.  
  93. /* Response texts */
  94.  
  95. char *win = "%s OK %s completed\015\012";
  96. char *altwin = "%s OK %s\015\012";
  97. char *rowin = "%s OK [READ-ONLY] %s completed\015\012";
  98. char *rwwin = "%s OK [READ-WRITE] %s completed\015\012";
  99. char *lose = "%s NO %s failed: %s\015\012";
  100. char *misarg = "%s BAD Missing required argument to %s\015\012";
  101. char *badarg = "%s BAD Argument given to %s when none expected\015\012";
  102. char *badseq = "%s BAD Bogus sequence in %s\015\012";
  103. char *badatt = "%s BAD Bogus attribute list in %s\015\012";
  104. char *badlit = "%s BAD Bogus literal count in %s\015\012";
  105. char *toobig = "* BAD Command line too long\015\012";
  106. char *nulcmd = "* BAD Null command\015\012";
  107. char *argrdy = "+ Ready for argument\015\012";
  108.  
  109. /* Drivers we use */
  110.  
  111. extern DRIVER bezerkdriver,tenexdriver,imapdriver,newsdriver,dummydriver;
  112.  
  113.  
  114. /* Function prototypes */
  115.  
  116. void main  ();
  117. void clkint  ();
  118. char *snarf  ();
  119. void fetch  ();
  120. void fetch_body  ();
  121. void fetch_body_part  ();
  122. void fetch_envelope  ();
  123. void fetch_encoding  ();
  124. void changed_flags  ();
  125. void fetch_flags  ();
  126. void fetch_internaldate  ();
  127. void fetch_rfc822  ();
  128. void fetch_rfc822_header  ();
  129. void fetch_rfc822_size  ();
  130. void fetch_rfc822_text  ();
  131. void penv  ();
  132. void pbody  ();
  133. void pstring  ();
  134. void paddr  ();
  135. long cstring  ();
  136. long caddr  ();
  137.  
  138. extern char *crypt  ();
  139.  
  140. /* Main program */
  141.  
  142. void main (argc,argv)
  143.     int argc;
  144.     char *argv[];
  145. {
  146.   int i;
  147.   char *s,*t = "OK",*u,*v;
  148.   struct hostent *hst;
  149.   void (*f) () = NIL;
  150.   mail_link (&tenexdriver);    /* install the Tenex mail driver */
  151.   mail_link (&bezerkdriver);    /* install the Berkeley mail driver */
  152.   mail_link (&imapdriver);    /* install the IMAP driver */
  153.   mail_link (&newsdriver);    /* install the netnews driver */
  154.   mail_link (&dummydriver);    /* install the dummy driver */
  155.   gethostname (cmdbuf,TMPLEN-1);/* get local name */
  156.   host = cpystr ((hst = gethostbyname (cmdbuf)) ? hst->h_name : cmdbuf);
  157.   rfc822_date (cmdbuf);        /* get date/time now */
  158.   if ((i = getuid ()) > 0) {    /* logged in? */
  159.     if (!(s = (char *) getlogin ())) s = (getpwuid (i))->pw_name;
  160.     user = cpystr (s);        /* set up user name */
  161.     pass = cpystr ("*");    /* and fake password */
  162.     state = SELECT;        /* enter select state */
  163.     t = "PREAUTH";        /* pre-authorized */
  164.   }
  165.   printf ("* %s %s IMAP2+ Service %s at %s\015\012",t,host,version,cmdbuf);
  166.   fflush (stdout);        /* dump output buffer */
  167.   signal (SIGALRM,clkint);    /* prepare for clock interrupt */
  168.                 /* initialize timeout interval */
  169.   timer.it_interval.tv_sec = TIMEOUT;
  170.   timer.it_interval.tv_usec = 0;
  171.   do {                /* command processing loop */
  172.                 /* get a command under timeout */
  173.     timer.it_value.tv_sec = TIMEOUT; timer.it_value.tv_usec = 0;
  174.     setitimer (ITIMER_REAL,&timer,NIL);
  175.     if (!fgets (cmdbuf,TMPLEN-1,stdin)) _exit (1);
  176.                 /* make sure timeout disabled */
  177.     timer.it_value.tv_sec = timer.it_value.tv_usec = 0;
  178.     setitimer (ITIMER_REAL,&timer,NIL);
  179.     fs_give ((void **) &lsterr);/* no last error */
  180.                 /* find end of line */
  181.     if (!strchr (cmdbuf,'\012')) printf (toobig);
  182.     else if (!(tag = strtok (cmdbuf," \015\012"))) printf (nulcmd);
  183.     else if (!(cmd = strtok (NIL," \015\012")))
  184.       printf ("%s BAD Missing command\015\012",tag);
  185.     else {            /* parse command */
  186.       response = win;        /* set default response */
  187.       ucase (cmd);        /* canonicalize command case */
  188.                 /* snarf argument */
  189.       arg = strtok (NIL,"\015\012");
  190.                 /* LOGOUT command always valid */
  191.       if (!strcmp (cmd,"LOGOUT")) {
  192.     if (state == OPEN) mail_close (stream);
  193.     stream = NIL;
  194.     printf ("* BYE %s IMAP2 server terminating connection\015\012",host);
  195.     state = LOGOUT;
  196.       }
  197.  
  198.                 /* kludge for MacMS */
  199.       else if (!strcmp (cmd,"VERSION")) {
  200.                 /* single argument */
  201.     if (!(s = snarf (&arg))) response = misarg;
  202.     else if (arg) response = badarg;
  203.     else {
  204.       if (!((i = atoi (s)) && i > 0 && i <= 4))
  205.         response = "%s BAD Unknown version\015\012";
  206.       else if (mackludge = (i == 4))
  207.         printf ("* OK The MacMS kludge is enabled\015\012");
  208.     }
  209.       }
  210.       else if (!strcmp (cmd,"NOOP")) {
  211.     if ((state == OPEN) && !mail_ping (stream)) {
  212.       printf ("* BYE %s Fatal mailbox error: %s\015\012",host,
  213.           lsterr ? lsterr : "<unknown>");
  214.       state = LOGOUT;    /* go away */
  215.     }
  216.       }
  217.       else switch (state) {    /* dispatch depending upon state */
  218.       case LOGIN:        /* waiting to get logged in */
  219.     if (!strcmp (cmd,"LOGIN")) {
  220.       struct stat sbuf;
  221.       struct passwd *pwd;
  222.       fs_give ((void **) &user);
  223.       fs_give ((void **) &pass);
  224.                 /* two arguments */
  225.       if (!((user = cpystr (snarf (&arg))) &&
  226.         (pass = cpystr (snarf (&arg))))) response = misarg;
  227.       else if (arg) response = badarg;
  228.                 /* see if username and password are OK */
  229.       else if (server_login (user,pass,&home)) state = SELECT;
  230.                 /* nope, see if we allow anonymous */
  231.       else if (!stat ("/etc/anonymous.newsgroups",&sbuf) &&
  232.            !strcmp (user,"anonymous") && (pwd = getpwnam ("nobody"))) {
  233.         anonymous = T;    /* note we are anonymous, login as nobody */
  234.         setgid (pwd->pw_gid);
  235.         setuid (pwd->pw_uid);
  236.         state = SELECT;    /* make selected */
  237.       }
  238.       else response = "%s NO Bad %s user name and/or password\015\012";
  239.     }
  240.     else response = "%s BAD Command unrecognized/login please: %s\015\012";
  241.     break;
  242.  
  243.       case OPEN:        /* valid only when mailbox open */
  244.                 /* fetch mailbox attributes */
  245.     if (!strcmp (cmd,"FETCH")) {
  246.       if (!((s = strtok (arg," ")) && (t = strtok (NIL,"\015\012"))))
  247.         response = misarg;
  248.       else fetch (s,t);    /* do the fetch */
  249.     }
  250.                 /* store mailbox attributes */
  251.     else if (!strcmp (cmd,"STORE")) {
  252.                 /* must have three arguments */
  253.       if (!((s = strtok (arg," ")) && (cmd = strtok (NIL," ")) &&
  254.         (t = strtok (NIL,"\015\012")))) response = misarg;
  255.       else if (!strcmp (ucase (cmd),"+FLAGS")) f = mail_setflag;
  256.       else if (!strcmp (cmd,"-FLAGS")) f = mail_clearflag;
  257.       else if (!strcmp (cmd,"FLAGS")) {
  258.         mail_clearflag (stream,s,flags);
  259.         f = mail_setflag;    /* gross, but rarely if ever done */
  260.       }
  261.       else response = "%s BAD STORE %s not yet implemented\015\012";
  262.       if (f) {        /* if a function was selected */
  263.         (*f) (stream,s,t);    /* do it */
  264.         fetch (s,"FLAGS");    /* get the new flags status */
  265.       }
  266.     }
  267.                 /* check for new mail */
  268.     else if (!strcmp (cmd,"CHECK")) {
  269.                 /* no arguments */
  270.       if (arg) response = badarg;
  271.       else if (anonymous) mail_ping (stream);
  272.       else mail_check (stream);
  273.     }
  274.                 /* expunge deleted messages */
  275.     else if (!(anonymous || strcmp (cmd,"EXPUNGE"))) {
  276.                 /* no arguments */
  277.       if (arg) response = badarg;
  278.       else mail_expunge (stream);
  279.     }
  280.                 /* copy message(s) */
  281.     else if (!(anonymous || strcmp (cmd,"COPY"))) {
  282.       if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
  283.       else if (arg) response = badarg;
  284.       else mail_copy (stream,s,t);
  285.     }
  286.  
  287.                 /* search mailbox */
  288.     else if (!strcmp (cmd,"SEARCH")) {
  289.                 /* one or more arguments required */
  290.       if (!arg) response = misarg;
  291.       else {        /* zap search vector */
  292.                 /* end with a literal argument? */
  293.         while ((*(t = arg + strlen (arg) - 1) == '}') &&
  294.            (s = strrchr (arg,'{')) && response == win) {
  295.                 /* get length of literal, validate */
  296.           if (((i = atoi (++s)) < 1) || (TMPLEN - (t++ - cmdbuf) < i + 10))
  297.         response = badlit;
  298.           else {
  299.         *t++ = '\015';    /* reappend CR/LF */
  300.         *t++ = '\012';
  301.         printf (argrdy);/* tell client ready for argument */
  302.                 /* copy the literal */
  303.         while (i--) *t++ = getchar ();
  304.                 /* start a timeout */
  305.         timer.it_value.tv_sec = TIMEOUT; timer.it_value.tv_usec = 0;
  306.         setitimer (ITIMER_REAL,&timer,NIL);
  307.                 /* get new command tail */
  308.         if (!fgets (t,TMPLEN - (t-cmdbuf) - 1,stdin)) _exit (1);
  309.                 /* clear timeout */
  310.         timer.it_value.tv_sec = timer.it_value.tv_usec = 0;
  311.         setitimer (ITIMER_REAL,&timer,NIL);
  312.                 /* find end of line */
  313.         if (!(s = strchr (t,'\012'))) response = toobig;
  314.         else {
  315.           *s = NIL;    /* tie off line */
  316.           if (*--s == '\015') *s = NIL;
  317.         }
  318.           }
  319.         }
  320.                 /* punt if error */
  321.         if (response != win) break;
  322.                 /* do the search */
  323.         mail_search (stream,arg);
  324.         if (response == win || response == altwin) {
  325.                 /* output search results */
  326.           fputs ("* SEARCH",stdout);
  327.           for (i = 1; i <= nmsgs; ++i)
  328.         if (mail_elt (stream,i)->searched) printf (" %d",i);
  329.           fputs ("\015\012",stdout);
  330.         }
  331.       }
  332.     }
  333.     else            /* fall into select case */
  334.  
  335.       case SELECT:        /* valid whenever logged in */
  336.                 /* select new mailbox */
  337.     if ((!(anonymous || strcmp (cmd,"SELECT"))) ||
  338.         (!strcmp (cmd,"BBOARD"))) {
  339.                 /* single argument */
  340.       if (!(s = snarf (&arg))) response = misarg;
  341.       else if (arg) response = badarg;
  342.       else {
  343.         char tmp[MAILTMPLEN];
  344.         sprintf (tmp,"%s%s",(*cmd == 'B') ? "*" : "",s);
  345.         recent = -1;    /* make sure we get an update */
  346.         if ((stream = mail_open (stream,tmp,anonymous ? 69 : NIL)) &&
  347.         ((response == win) || (response == altwin))) {
  348.                 /* flush old list */
  349.           fs_give ((void **) &flags);
  350.           *s = '(';        /* start new flag list over old file name */
  351.           s[1] = '\0';
  352.           for (i = 0; i < NUSERFLAGS; i++)
  353.         if (t = stream->user_flags[i]) strcat (strcat (s,t)," ");
  354.                 /* append system flags to list */
  355.           strcat (s,"\\Answered \\Flagged \\Deleted \\Seen)");
  356.                 /* output list of flags */
  357.           printf ("* FLAGS %s\015\012",(flags = cpystr (s)));
  358.           state = OPEN;
  359.                 /* note readonly/readwrite */
  360.           response = stream->readonly ? rowin : rwwin;
  361.         }
  362.         else {        /* nuke if still open */
  363.           if (stream) mail_close (stream);
  364.           stream = NIL;
  365.           state = SELECT;    /* no mailbox open now */
  366.           response = lose;    /* open failed */
  367.         }
  368.       }
  369.     }
  370.  
  371.                 /* find mailboxes or bboards */
  372.     else if (!strcmp (cmd,"FIND")) {
  373.                 /* get subcommand and true argument */
  374.       if (!((s = strtok (arg," \015\012")) && (cmd = ucase (s)) &&
  375.         (arg = strtok (NIL,"\015\012")) && (s = snarf (&arg))))
  376.         response = misarg;    /* missing required argument */
  377.       else if (arg) response = badarg;
  378.       else {        /* dispatch */
  379.         if (!(anonymous || strcmp (cmd,"MAILBOXES"))) {
  380.           mail_find (NIL,s);/* default find action */
  381.           if (stream && *stream->mailbox == '{') mail_find (stream,s);
  382.         }
  383.                 /* find bulletin boards */
  384.         else if (!strcmp (cmd,"BBOARDS")) {
  385.           if (anonymous) {    /* special version for anonymous users */
  386.         struct stat sbuf;
  387.         if (!(stat (NEWSSPOOL,&sbuf) || stat (ACTIVEFILE,&sbuf)) &&
  388.             ((i = open (ACTIVEFILE,O_RDONLY,NIL)) >= 0)) {
  389.                 /* get file size and read data */
  390.           fstat (i,&sbuf);
  391.           read (i,v = (char *) fs_get (sbuf.st_size+1),sbuf.st_size);
  392.           close (i);    /* close file */
  393.                 /* tie off string */
  394.           v[sbuf.st_size] = '\0';
  395.           if (t = strtok (v,"\n")) do if (u = strchr (t,' ')) {
  396.             *u = '\0';    /* tie off at end of name */
  397.             if (pmatch (lcase (t),s)) mm_bboard (t);
  398.           } while (t = strtok (NIL,"\n"));
  399.           fs_give ((void **) &v);
  400.         }
  401.           }
  402.           else {        /* allow the ordinary driver version */
  403.         mail_find_bboards (NIL,s);
  404.         if (stream && *stream->mailbox == '{')
  405.           mail_find_bboards (stream,s);
  406.           }
  407.         }
  408.         else response = "%s BAD Command unrecognized: FIND %s\015\012";
  409.       }
  410.     }
  411.  
  412.     else response = "%s BAD Command unrecognized: %s\015\012";
  413.     break;
  414.       default:
  415.         response = "%s BAD Server in unknown state for %s command\015\012";
  416.     break;
  417.       }
  418.                 /* change in recent messages? */
  419.       if ((state == OPEN) && (recent != stream->recent))
  420.     printf ("* %d RECENT\015\012",(recent = stream->recent));
  421.                 /* get text for alternative win message now */
  422.       if (response == altwin) cmd = lsterr;
  423.                 /* output response */
  424.       printf (response,tag,cmd,lsterr);
  425.     }
  426.     fflush (stdout);        /* make sure output blatted */
  427.   } while (state != LOGOUT);    /* until logged out */
  428.   exit (0);            /* all done */
  429. }
  430.  
  431.  
  432. /* Clock interrupt
  433.  */
  434.  
  435. void clkint ()
  436. {
  437.   printf ("* BYE Autologout; idle for too long\015\012");
  438.   fflush (stdout);        /* make sure output blatted */
  439.                 /* try to gracefully close the stream */
  440.   if (state == OPEN) mail_close (stream);
  441.   stream = NIL;
  442.   exit (0);            /* die die die */
  443. }
  444.  
  445. /* Snarf an argument
  446.  * Accepts: pointer to argument text pointer
  447.  * Returns: argument
  448.  */
  449.  
  450. char *snarf (arg)
  451.     char **arg;
  452. {
  453.   long i;
  454.   char *c = *arg;
  455.   char *s = c + 1;
  456.   char *t = NIL;
  457.   if (!c) return NIL;        /* better be an argument */
  458.   switch (*c) {            /* see what the argument is */
  459.   case '\0':            /* catch bogons */
  460.   case ' ':
  461.     return NIL;
  462.   case '"':            /* quoted string */
  463.     if (!(strchr (s,'"') && (c = strtok (c,"\"")))) return NIL;
  464.     break;
  465.   case '{':            /* literal string */
  466.     if (isdigit (*s)) {        /* be sure about that */
  467.       i = strtol (s,&c,10);    /* get its length */
  468.                 /* validate end of literal */
  469.       if (*c++ != '}' || *c++) return NIL;
  470.       if ((i + 10) > (TMPLEN - (c - cmdbuf))) return NIL;
  471.       printf (argrdy);        /* tell client ready for argument */
  472.       t = c;            /* get it */
  473.       while (i--) *t++ = getchar ();
  474.       *t++ = NIL;        /* make sure string tied off */
  475.                 /* start timeout */
  476.       timer.it_value.tv_sec = TIMEOUT; timer.it_value.tv_usec = 0;
  477.       setitimer (ITIMER_REAL,&timer,NIL);
  478.                     /* get new command tail */
  479.       if (!fgets ((*arg = t),TMPLEN - (t - cmdbuf) - 1,stdin)) _exit (1);
  480.                 /* clear timeout */
  481.       timer.it_value.tv_sec = timer.it_value.tv_usec = 0;
  482.       setitimer (ITIMER_REAL,&timer,NIL);
  483.       if (!strchr (t,'\012')) {    /* have a newline there? */
  484.     response = toobig;    /* lost it seems */
  485.     return NIL;
  486.       }
  487.       break;
  488.     }
  489.                 /* otherwise fall through (third party IMAP) */
  490.   default:            /* atomic string */
  491.     c = strtok (*arg," \015\012");
  492.     break;
  493.   }
  494.                 /* remainder of arguments */
  495.   if ((*arg = strtok (t,"\015\012")) && **arg == ' ') ++*arg;
  496.   return c;
  497. }
  498.  
  499. /* Fetch message data
  500.  * Accepts: sequence as a string
  501.  *        string of data items to be fetched
  502.  */
  503.  
  504. #define MAXFETCH 100
  505.  
  506. void fetch (s,t)
  507.     char *s;
  508.     char *t;
  509. {
  510.   char c,*v;
  511.   long i,k;
  512.   void (*f[MAXFETCH]) ();
  513.   char *fa[MAXFETCH];
  514.   if (!mail_sequence (stream,s)) {
  515.     response = badseq;        /* punt if sequence bogus */
  516.     return;
  517.   }
  518.                 /* process macros */
  519.   if (!strcmp (ucase (t),"ALL"))
  520.     strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)");
  521.   else if (!strcmp (t,"FULL"))
  522.     strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)");
  523.   else if (!strcmp (t,"FAST")) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE)");
  524.   s = t + strlen (t) - 1;    /* last character in attribute string */
  525.                 /* if multiple items, make sure in list form */
  526.   if (strchr (t,' ') && ((*t != '(') || (*s != ')'))) {
  527.     response = badatt;
  528.     return;
  529.   }
  530.                 /* nuke the parens now */
  531.   if ((*t == '(') && (*s == ')') && t++) *s = '\0';
  532.   k = 0;            /* initial index */
  533.   if (s = strtok (t," ")) do {    /* parse attribute list */
  534.     if (*s == 'B' && s[1] == 'O' && s[2] == 'D' && s[3] == 'Y') switch (s[4]) {
  535.     case '\0':            /* entire body */
  536.       f[k++] = fetch_body;
  537.       break;
  538.     case '[':            /* body segment */
  539.       if ((v = strchr (s + 5,']')) && (!(*v = v[1]))) {
  540.     fa[k] = s + 5;        /* set argument */
  541.     f[k++] = fetch_body_part;
  542.     break;            /* valid body segment */
  543.       }                /* else fall into default case */
  544.     default:            /* bogus */
  545.       response = badatt;
  546.       return;
  547.     }
  548.     else
  549.     if (!strcmp (s,"ENVELOPE")) f[k++] = fetch_envelope;
  550.     else if (!strcmp (s,"FLAGS")) f[k++] = fetch_flags;
  551.     else if (!strcmp (s,"INTERNALDATE")) f[k++] = fetch_internaldate;
  552.     else if (!strcmp (s,"RFC822")) f[k++] = fetch_rfc822;
  553.     else if (!strcmp (s,"RFC822.HEADER")) f[k++] = fetch_rfc822_header;
  554.     else if (!strcmp (s,"RFC822.SIZE")) f[k++] = fetch_rfc822_size;
  555.     else if (!strcmp (s,"RFC822.TEXT")) f[k++] = fetch_rfc822_text;
  556.     else {            /* unknown attribute */
  557.       response = badatt;
  558.       return;
  559.     }
  560.   } while ((s = strtok (NIL," ")) && k < MAXFETCH);
  561.   else {
  562.     response = misarg;        /* missing attribute list */
  563.     return;
  564.   }
  565.   if (s) {            /* too many attributes? */
  566.     response = "%s BAD Excessively complex FETCH attribute list\015\012";
  567.     return;
  568.   }
  569.   f[k++] = NIL;            /* tie off attribute list */
  570.                 /* for each requested message */
  571.   for (i = 1; i <= nmsgs; i++) if (mail_elt (stream,i)->sequence) {
  572.                 /* parse envelope, set body, do warnings */
  573.     mail_fetchenvelope (stream,i);
  574.     printf ("* %d FETCH (",i);    /* leader */
  575.     (*f[0]) (i,fa[0]);        /* do first attribute */
  576.     for (k = 1; f[k]; k++) {    /* for each subsequent attribute */
  577.       putchar (' ');        /* delimit with space */
  578.       (*f[k]) (i,fa[k]);    /* do that attribute */
  579.     }
  580.     fputs (")\015\012",stdout);    /* trailer */
  581.   }
  582. }
  583.  
  584. /* Fetch message body structure
  585.  * Accepts: message number
  586.  *        string argument
  587.  */
  588.  
  589.  
  590. void fetch_body (i,s)
  591.     long i;
  592.     char *s;
  593. {
  594.   BODY *body;
  595.   mail_fetchenvelope (stream,i);/* make sure have envelope */
  596.   fputs ("BODY ",stdout);    /* output attribute */
  597.                 /* output body */
  598.   if (body = mail_elt (stream,i)->body) pbody (body);
  599.   else fputs ("NIL",stdout);    /* no body */
  600. }
  601.  
  602.  
  603. /* Fetch message body part
  604.  * Accepts: message number
  605.  *        string argument
  606.  */
  607.  
  608. void fetch_body_part (i,s)
  609.     long i;
  610.     char *s;
  611. {
  612.   unsigned long j;
  613.   long k = 0;
  614.   MESSAGECACHE *elt = mail_elt (stream,i);
  615.   int f = elt->seen;
  616.   printf ("BODY[%s] ",s);    /* output attribute */
  617.   if (elt->body && (s = mail_fetchbody (stream,i,s,&j))) {
  618.                 /* and literal string */
  619.     printf ("{%d}\015\012",j);
  620.     while (j -= k) k = fwrite (s += k,1,j,stdout);
  621.     changed_flags (i,f);    /* output changed flags */
  622.   }
  623.   else fputs ("NIL",stdout);    /* can't output anything at all */
  624. }
  625.  
  626. /* Fetch IMAP envelope
  627.  * Accepts: message number
  628.  *        string argument
  629.  */
  630.  
  631. void fetch_envelope (i,s)
  632.     long i;
  633.     char *s;
  634. {
  635.   ENVELOPE *env = mail_fetchenvelope (stream,i);
  636.   fputs ("ENVELOPE ",stdout);    /* output attribute */
  637.   if (env) {            /* have an envelope? */
  638.                 /* disgusting MacMS kludge */
  639.     if (mackludge) printf ("%d ",cstring (env->date) + cstring (env->subject) +
  640.                caddr (env->from) + caddr (env->sender) +
  641.                caddr (env->reply_to) + caddr (env->to) +
  642.                caddr (env->cc) + caddr (env->bcc) +
  643.                cstring (env->in_reply_to) +
  644.                cstring (env->message_id));
  645.     penv (env);            /* output envelope */
  646.   }
  647.   else fputs ("NIL",stdout);    /* no envelope */
  648. }
  649.  
  650. /* Fetch flags
  651.  * Accepts: message number
  652.  *        string argument
  653.  */
  654.  
  655. void fetch_flags (i,s)
  656.     long i;
  657.     char *s;
  658. {
  659.   char tmp[MAILTMPLEN];
  660.   long u;
  661.   char *t;
  662.   MESSAGECACHE *elt = mail_elt (stream,i);
  663.   s = tmp;
  664.   s[0] = s[1] = '\0';        /* start with empty flag string */
  665.                 /* output system flags */
  666.   if (elt->recent) strcat (s," \\Recent");
  667.   if (elt->seen) strcat (s," \\Seen");
  668.   if (elt->deleted) strcat (s," \\Deleted");
  669.   if (elt->flagged) strcat (s," \\Flagged");
  670.   if (elt->answered) strcat (s," \\Answered");
  671.   if (u = elt->user_flags) do    /* any user flags? */
  672.     if ((TMPLEN - ((s += strlen (s)) - tmp)) >
  673.     (2 + strlen (t = stream->user_flags[find_rightmost_bit (&u)]))) {
  674.       *s++ = ' ';        /* space delimiter */
  675.       strcpy (s,t);        /* copy the user flag */
  676.     }
  677.   while (u);            /* until no more user flags */
  678.   printf ("FLAGS (%s)",tmp+1);    /* output results, skip first char of list */
  679. }
  680.  
  681.  
  682. /* Output flags if was unseen
  683.  * Accepts: message number
  684.  *        prior value of Seen flag
  685.  */
  686.  
  687. void changed_flags (i,f)
  688.     long i;
  689.     int f;
  690. {
  691.   if (!f) {            /* was unseen? */
  692.     putchar (' ');        /* yes, delimit with space */
  693.     fetch_flags (i,NIL);    /* output flags */
  694.   }
  695. }
  696.  
  697.  
  698. /* Fetch message internal date
  699.  * Accepts: message number
  700.  *        string argument
  701.  */
  702.  
  703. void fetch_internaldate (i,s)
  704.     long i;
  705.     char *s;
  706. {
  707.   printf ("INTERNALDATE \"%s\"",mail_elt (stream,i)->internal_date);
  708. }
  709.  
  710. /* Fetch complete RFC-822 format message
  711.  * Accepts: message number
  712.  *        string argument
  713.  */
  714.  
  715. void fetch_rfc822 (i,s)
  716.     long i;
  717.     char *s;
  718. {
  719.   int f = mail_elt (stream,i)->seen;
  720.   printf ("RFC822 {%d}\015\012%s",mail_elt (stream,i)->rfc822_size,
  721.       mail_fetchheader (stream,i));
  722.   fputs (mail_fetchtext (stream,i),stdout);
  723.   changed_flags (i,f);        /* output changed flags */
  724. }
  725.  
  726.  
  727. /* Fetch RFC-822 header
  728.  * Accepts: message number
  729.  *        string argument
  730.  */
  731.  
  732. void fetch_rfc822_header (i,s)
  733.     long i;
  734.     char *s;
  735. {
  736.   fputs ("RFC822.HEADER ",stdout);
  737.   pstring (mail_fetchheader (stream,i));
  738. }
  739.  
  740.  
  741. /* Fetch RFC-822 message length
  742.  * Accepts: message number
  743.  *        string argument
  744.  */
  745.  
  746. void fetch_rfc822_size (i,s)
  747.     long i;
  748.     char *s;
  749. {
  750.   printf ("RFC822.SIZE %d",mail_elt (stream,i)->rfc822_size);
  751. }
  752.  
  753.  
  754. /* Fetch RFC-822 text only
  755.  * Accepts: message number
  756.  *        string argument
  757.  */
  758.  
  759. void fetch_rfc822_text (i,s)
  760.     long i;
  761.     char *s;
  762. {
  763.   int f = mail_elt (stream,i)->seen;
  764.   fputs ("RFC822.TEXT ",stdout);
  765.   pstring (mail_fetchtext (stream,i));
  766.   changed_flags (i,f);        /* output changed flags */
  767. }
  768.  
  769. /* Print envelope
  770.  * Accepts: body
  771.  */
  772.  
  773. void penv (env)
  774.     ENVELOPE *env;
  775. {
  776.   putchar ('(');        /* delimiter */
  777.   pstring (env->date);        /* output envelope fields */
  778.   putchar (' ');
  779.   pstring (env->subject);
  780.   putchar (' ');
  781.   paddr (env->from);
  782.   putchar (' ');
  783.   paddr (env->sender);
  784.   putchar (' ');
  785.   paddr (env->reply_to);
  786.   putchar (' ');
  787.   paddr (env->to);
  788.   putchar (' ');
  789.   paddr (env->cc);
  790.   putchar (' ');
  791.   paddr (env->bcc);
  792.   putchar (' ');
  793.   pstring (env->in_reply_to);
  794.   putchar (' ');
  795.   pstring (env->message_id);
  796.   putchar (')');        /* end of envelope */
  797. }
  798.  
  799. /* Print body
  800.  * Accepts: body
  801.  */
  802.  
  803. void pbody (body)
  804.     BODY *body;
  805. {
  806.   PARAMETER *param;
  807.   PART *part;
  808.   putchar ('(');        /* delimiter */
  809.                 /* multipart type? */
  810.   if (body->type == TYPEMULTIPART) {
  811.     for (part = body->contents.part; part; part = part->next)
  812.       pbody (&(part->body));    /* print each part */
  813.     putchar (' ');        /* space delimiter */
  814.     pstring (body->subtype);    /* and finally the subtype */
  815.   }
  816.   else {            /* non-multipart body type */
  817.     pstring ((char *) body_types[body->type]);
  818.     putchar (' ');
  819.     pstring (body->subtype);
  820.     if (param = body->parameter) {
  821.       fputs (" (",stdout);
  822.       do {
  823.     pstring (param->attribute);
  824.     putchar (' ');
  825.     pstring (param->value);
  826.     if (param = param->next) putchar (' ');
  827.       } while (param);
  828.       fputs (") ",stdout);
  829.     }
  830.     else fputs (" NIL ",stdout);
  831.     pstring (body->id);
  832.     putchar (' ');
  833.     pstring (body->description);
  834.     putchar (' ');
  835.     pstring ((char *) body_encodings[body->encoding]);
  836.     printf (" %d",body->size.bytes);
  837.     switch (body->type) {    /* extra stuff depends upon body type */
  838.     case TYPEMESSAGE:
  839.                 /* can't do this if not RFC822 */
  840.       if (strcmp (body->subtype,"RFC822")) break;
  841.       putchar (' ');
  842.       penv (body->contents.msg.env);
  843.       putchar (' ');
  844.       pbody (body->contents.msg.body);
  845.     case TYPETEXT:
  846.       printf (" %d",body->size.lines);
  847.       break;
  848.     default:
  849.       break;
  850.     }
  851.   }
  852.   putchar (')');        /* end of body */
  853. }
  854.  
  855. /* Print string
  856.  * Accepts: string
  857.  */
  858.  
  859. void pstring (s)
  860.     char *s;
  861. {
  862.   char c,*t;
  863.   if (t = s) {            /* is there a string? */
  864.                 /* yes, search for specials */
  865.     while (c = *t++) if (c == '"' || c == '{' || c == '\015' || c == '\012' ||
  866.              c == '%' || c == '\\') break;
  867.                 /* must use literal string */
  868.     if (c) printf ("{%d}\015\012%s",strlen (s),s);
  869.     else printf ("\"%s\"",s);    /* may use quoted string */
  870.   }
  871.   else fputs ("NIL",stdout);    /* empty string */
  872. }
  873.  
  874.  
  875. /* Print address list
  876.  * Accepts: address list
  877.  */
  878.  
  879. void paddr (a)
  880.     ADDRESS *a;
  881. {
  882.   if (a) {            /* have anything in address? */
  883.     putchar ('(');        /* open the address list */
  884.     do {            /* for each address */
  885.       putchar ('(');        /* open the address */
  886.       pstring (a->personal);    /* personal name */
  887.       putchar (' ');
  888.       pstring (a->adl);        /* at-domain-list */
  889.       putchar (' ');
  890.       pstring (a->mailbox);    /* mailbox */
  891.       putchar (' ');
  892.       pstring (a->host);    /* domain name of mailbox's host */
  893.       putchar (')');        /* terminate address */
  894.     } while (a = a->next);    /* until end of address */
  895.     putchar (')');        /* close address list */
  896.   }
  897.   else fputs ("NIL",stdout);    /* empty address */
  898. }
  899.  
  900. /* Count string and space afterwards
  901.  * Accepts: string
  902.  */
  903.  
  904. long cstring (s)
  905.     char *s;
  906. {
  907.   char tmp[20];
  908.   char c,*t;
  909.   long i;
  910.   if (t = s) {            /* is there a string? */
  911.                 /* yes, search for specials */
  912.     while (c = *t++) if (c == '"' || c == '{' || c == '\015' || c == '\012' ||
  913.              c == '%' || c == '\\') break;
  914.     if (c) {            /* must use literal string */
  915.       sprintf (tmp,"{%d}\015\012",i = strlen (s));
  916.       return strlen (tmp) + i + 1;
  917.     }
  918.     else return 3 + strlen (s);    /* quoted string */
  919.   }
  920.   else return 4;        /* NIL plus space */
  921. }
  922.  
  923.  
  924. /* Count address list and space afterwards
  925.  * Accepts: address list
  926.  */
  927.  
  928. long caddr (a)
  929.     ADDRESS *a;
  930. {
  931.   long i = 3;            /* open, close, and space */
  932.                 /* count strings in address list */
  933.   if (a) do i += 1 + cstring (a->personal) + cstring (a->adl) +
  934.     cstring (a->mailbox) + cstring (a->host);
  935.   while (a = a->next);        /* until end of address */
  936.   else i = 4;            /* NIL plus space */
  937.   return i;            /* return the count */
  938. }
  939.  
  940. /* Co-routines from MAIL library */
  941.  
  942.  
  943. /* Message matches a search
  944.  * Accepts: IMAP2 stream
  945.  *        message number
  946.  */
  947.  
  948. void mm_searched (stream,msgno)
  949.     MAILSTREAM *stream;
  950.     long msgno;
  951. {
  952.                 /* nothing to do here */
  953. }
  954.  
  955.  
  956. /* Message exists (mailbox)
  957.     i.e. there are that many messages in the mailbox;
  958.  * Accepts: IMAP2 stream
  959.  *        message number
  960.  */
  961.  
  962. void mm_exists (stream,number)
  963.     MAILSTREAM *stream;
  964.     long number;
  965. {
  966.                 /* note change in number of messages */
  967.   printf ("* %d EXISTS\015\012",(nmsgs = number));
  968.   recent = -1;            /* make sure fetch new recent count */
  969. }
  970.  
  971.  
  972. /* Message expunged
  973.  * Accepts: IMAP2 stream
  974.  *        message number
  975.  */
  976.  
  977. void mm_expunged (stream,number)
  978.     MAILSTREAM *stream;
  979.     long number;
  980. {
  981.   printf ("* %d EXPUNGE\015\012",number);
  982. }
  983.  
  984.  
  985. /* Mailbox found
  986.  * Accepts: Mailbox name
  987.  */
  988.  
  989. void mm_mailbox (string)
  990.     char *string;
  991. {
  992.   printf ("* MAILBOX %s\015\012",string);
  993. }
  994.  
  995.  
  996. /* BBoard found
  997.  * Accepts: BBoard name
  998.  */
  999.  
  1000. void mm_bboard (string)
  1001.     char *string;
  1002. {
  1003.   printf ("* BBOARD %s\015\012",string);
  1004. }
  1005.  
  1006. /* Notification event
  1007.  * Accepts: IMAP2 stream
  1008.  *        string to log
  1009.  *        error flag
  1010.  */
  1011.  
  1012. void mm_notify (stream,string,errflg)
  1013.     MAILSTREAM *stream;
  1014.     char *string;
  1015.     long errflg;
  1016. {
  1017.   mm_log (string,errflg);    /* just do mm_log action */
  1018. }
  1019.  
  1020.  
  1021. /* Log an event for the user to see
  1022.  * Accepts: string to log
  1023.  *        error flag
  1024.  */
  1025.  
  1026. void mm_log (string,errflg)
  1027.     char *string;
  1028.     long errflg;
  1029. {
  1030.   switch (errflg) {        /* action depends upon the error flag */
  1031.   case NIL:            /* information message, set as OK response */
  1032.     if (response == win) {    /* only if no other response yet */
  1033.       response = altwin;    /* switch to alternative win message */
  1034.       fs_give ((void **) &lsterr);
  1035.       lsterr = cpystr (string);    /* copy string for later use */
  1036.     }
  1037.     break;
  1038.   case PARSE:            /* parse glitch, output unsolicited OK */
  1039.     printf ("* OK %s\015\012",string);
  1040.     break;
  1041.   case WARN:            /* warning, output unsolicited NO (kludge!) */
  1042.     if (strcmp (string,"Mailbox is empty")) printf ("* NO %s\015\012",string);
  1043.     break;
  1044.   case ERROR:            /* error that broke command */
  1045.   default:            /* default should never happen */
  1046.     response = lose;        /* set fatality */
  1047.     fs_give ((void **) &lsterr);/* flush old error */
  1048.     lsterr = cpystr (string);    /* note last error */
  1049.     break;
  1050.   }
  1051. }
  1052.  
  1053.  
  1054. /* Log an event to debugging telemetry
  1055.  * Accepts: string to log
  1056.  */
  1057.  
  1058. void mm_dlog (string)
  1059.     char *string;
  1060. {
  1061.   mm_log (string,WARN);        /* shouldn't happen normally */
  1062. }
  1063.  
  1064. /* Get user name and password for this host
  1065.  * Accepts: host name
  1066.  *        where to return user name
  1067.  *        where to return password
  1068.  *        trial count
  1069.  */
  1070.  
  1071. void mm_login (host,username,password,trial)
  1072.     char *host;
  1073.     char *username;
  1074.     char *password;
  1075.     long trial;
  1076. {
  1077.   strcpy (username,user);    /* set user name */
  1078.   strcpy (password,pass);    /* and password */
  1079. }
  1080.  
  1081. /* About to enter critical code
  1082.  * Accepts: stream
  1083.  */
  1084.  
  1085. void mm_critical (stream)
  1086.     MAILSTREAM *stream;
  1087. {
  1088.   /* Not doing anything here for now */
  1089. }
  1090.  
  1091.  
  1092. /* About to exit critical code
  1093.  * Accepts: stream
  1094.  */
  1095.  
  1096. void mm_nocritical (stream)
  1097.     MAILSTREAM *stream;
  1098. {
  1099.   /* Not doing anything here for now */
  1100. }
  1101.  
  1102.  
  1103. /* Disk error found
  1104.  * Accepts: stream
  1105.  *        system error code
  1106.  *        flag indicating that mailbox may be clobbered
  1107.  * Returns: abort flag
  1108.  */
  1109.  
  1110. long mm_diskerror (stream,errcode,serious)
  1111.     MAILSTREAM *stream;
  1112.     long errcode;
  1113.     long serious;
  1114. {
  1115.   if (serious) {        /* try your damnest if clobberage likely */
  1116.     printf ("* BAD Retrying to fix probable mailbox damage!\015\012");
  1117.                 /* make damn sure timeout disabled */
  1118.     timer.it_value.tv_sec = timer.it_value.tv_usec = 0;
  1119.     setitimer (ITIMER_REAL,&timer,NIL);
  1120.     sleep (60);            /* give it some time to clear up */
  1121.     return NIL;
  1122.   }
  1123.                 /* otherwise die before more damage is done */
  1124.   printf ("* BYE Aborting due to disk error %s\015\012",strerror (errcode));
  1125.   return T;
  1126. }
  1127.  
  1128.  
  1129. /* Log a fatal error event
  1130.  * Accepts: string to log
  1131.  */
  1132.  
  1133. void mm_fatal (string)
  1134.     char *string;
  1135. {
  1136.   mm_log (string,ERROR);    /* shouldn't happen normally */
  1137. }
  1138.