home *** CD-ROM | disk | FTP | other *** search
/ ftp.uv.es / 2014.11.ftp.uv.es.tar / ftp.uv.es / pub / unix / pine4.10.tar.gz / pine4.10.tar / pine4.10 / imap / src / ipopd / ipop3d.c < prev    next >
C/C++ Source or Header  |  1999-01-28  |  27KB  |  912 lines

  1. /*
  2.  * Program:    IPOP3D - IMAP to POP3 conversion 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:    1 November 1990
  13.  * Last Edited:    27 January 1999
  14.  *
  15.  * Copyright 1999 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. #define PBIN getchar
  37. #define PSIN(s,n) fgets (s,n,stdin)
  38. #define PBOUT(c) putchar (c)
  39. #define PSOUT(s) fputs (s,stdout)
  40. #define PFLUSH fflush (stdout)
  41. #define CRLF PSOUT ("\015\012")
  42.  
  43. /* Parameter files */
  44.  
  45. #include "mail.h"
  46. #include "osdep.h"
  47. #include "rfc822.h"
  48. #include <stdio.h>
  49. #include <ctype.h>
  50. #include <errno.h>
  51. extern int errno;        /* just in case */
  52. #include <signal.h>
  53. #include "misc.h"
  54.  
  55.  
  56. /* Autologout timer */
  57. #define KODTIMEOUT 60*5
  58. #define LOGINTIMEOUT 60*3
  59. #define TIMEOUT 60*30
  60.  
  61.  
  62. /* Size of temporary buffers */
  63. #define TMPLEN 1024
  64.  
  65.  
  66. /* Server states */
  67.  
  68. #define AUTHORIZATION 0
  69. #define TRANSACTION 1
  70. #define UPDATE 2
  71. #define LOGOUT 3
  72.  
  73. /* Eudora food */
  74.  
  75. #define STATUS "Status: %s%s\015\012"
  76. #define SLEN (sizeof (STATUS)-3)
  77.  
  78.  
  79. /* Global storage */
  80.  
  81. char *version = "7.59";        /* server version */
  82. short state = AUTHORIZATION;    /* server state */
  83. short critical = NIL;        /* non-zero if in critical code */
  84. MAILSTREAM *stream = NIL;    /* mailbox stream */
  85. long idletime = 0;        /* time we went idle */
  86. unsigned long nmsgs = 0;    /* current number of messages */
  87. unsigned long ndele = 0;    /* number of deletes */
  88. unsigned long last = 0;        /* highest message accessed */
  89. unsigned long il = 0;        /* initial last message */
  90. char challenge[128];        /* challenge */
  91. #ifndef DISABLE_POP_PROXY
  92. char *host = NIL;        /* remote host name */
  93. #endif
  94. char *user = NIL;        /* user name */
  95. char *pass = NIL;        /* password */
  96. char *initial = NIL;        /* initial response */
  97. long *msg = NIL;        /* message translation vector */
  98. char *sayonara = "+OK Sayonara\015\012";
  99.  
  100.  
  101. /* Function prototypes */
  102.  
  103. int main (int argc,char *argv[]);
  104. void clkint ();
  105. void kodint ();
  106. void hupint ();
  107. void trmint ();
  108. int login (char *t,int argc,char *argv[]);
  109. char *apop_login (char *chal,char *user,char *md5,int argc,char *argv[]);
  110. char *responder (void *challenge,unsigned long clen,unsigned long *rlen);
  111. int mbxopen (char *mailbox);
  112. long blat (char *text,long lines,unsigned long size);
  113. void rset ();
  114.  
  115. /* Main program */
  116.  
  117. int main (int argc,char *argv[])
  118. {
  119.   unsigned long i,j,k;
  120.   char *s,*t;
  121.   char tmp[TMPLEN];
  122.                 /* initialize server */
  123.   server_init (argv[0],"pop3","pop3s","pop",clkint,kodint,hupint,trmint);
  124. #include "linkage.c"
  125.   challenge[0] = '\0';        /* find the CRAM-MD5 authenticator */
  126.   if (i = mail_lookup_auth_name ("CRAM-MD5",NIL)) {
  127.     AUTHENTICATOR *a = mail_lookup_auth (i);
  128.     if (a->server) {        /* have an MD5 enable file? */
  129.                 /* build challenge -- less than 128 chars */
  130.       sprintf (challenge,"<%lx.%lx@%.64s>",(long) getpid (),(long) time (0),
  131.            tcp_serverhost ());
  132.     }
  133.   }
  134.   /* There are reports of POP3 clients which get upset if anything appears
  135.    * between the "+OK" and the "POP3" in the greeting.
  136.    */
  137.   PSOUT ("+OK POP3");
  138.   if (!challenge[0]) {        /* if no MD5 enable, output host name */
  139.     PBOUT (' ');
  140.     PSOUT (tcp_serverhost ());
  141.   }
  142.   PSOUT (" v");
  143.   PSOUT (version);
  144.   PSOUT (" server ready");
  145.   if (challenge[0]) {        /* if MD5 enable, output challenge here */
  146.     PBOUT (' ');
  147.     PSOUT (challenge);
  148.   }
  149.   CRLF;
  150.   PFLUSH;            /* dump output buffer */
  151.                 /* command processing loop */
  152.   while ((state != UPDATE) && (state != LOGOUT)) {
  153.     idletime = time (0);    /* get a command under timeout */
  154.     alarm ((state == TRANSACTION) ? TIMEOUT : LOGINTIMEOUT);
  155.     while (!PSIN (tmp,TMPLEN)) {
  156.       if (errno==EINTR) errno=0;/* ignore if some interrupt */
  157.       else {
  158.     char *e = errno ? strerror (errno) : "Command stream end of file";
  159.     alarm (0);        /* disable all interrupts */
  160.     syslog (LOG_INFO,"%s while reading line user=%.80s host=%.80s",
  161.         e,user ? user : "???",tcp_clienthost ());
  162.     rset ();        /* try to gracefully close the stream */
  163.     if (state == TRANSACTION) mail_close (stream);
  164.     stream = NIL;
  165.     state = LOGOUT;
  166.     _exit (1);
  167.       }
  168.     }
  169.     alarm (0);            /* make sure timeout disabled */
  170.     idletime = 0;        /* no longer idle */
  171.  
  172.     if (!strchr (tmp,'\012'))    /* find end of line */
  173.       PSOUT ("-ERR Command line too long\015\012");
  174.     else if (!(s = strtok (tmp," \015\012")))
  175.       PSOUT ("-ERR Null command\015\012");
  176.     else {            /* dispatch based on command */
  177.       ucase (s);        /* canonicalize case */
  178.                 /* snarf argument */
  179.       t = strtok (NIL,"\015\012");
  180.                 /* QUIT command always valid */
  181.       if (!strcmp (s,"QUIT")) state = UPDATE;
  182.       else switch (state) {    /* else dispatch based on state */
  183.       case AUTHORIZATION:    /* waiting to get logged in */
  184.     if (!strcmp (s,"USER")) {
  185. #ifndef DISABLE_POP_PROXY
  186.       if (host) fs_give ((void **) &host);
  187. #endif
  188.       if (user) fs_give ((void **) &user);
  189.       if (pass) fs_give ((void **) &pass);
  190.       if (t && *t) {    /* if user name given */
  191.                 /* skip leading whitespace (bogus clients!) */
  192.         while (*t == ' ') ++t;
  193. #ifndef DISABLE_POP_PROXY
  194.                 /* remote user name? */
  195.         if (s = strchr (t,':')) {
  196.           *s++ = '\0';    /* tie off host name */
  197.           host = cpystr (t);/* copy host name */
  198.           user = cpystr (s);/* copy user name */
  199.         }
  200.                 /* local user name */
  201.         else user = cpystr (t);
  202. #else
  203.         user = cpystr (t);    /* local user name */
  204. #endif
  205.         PSOUT ("+OK User name accepted, password please\015\012");
  206.       }
  207.       else PSOUT ("-ERR Missing username argument\015\012");
  208.     }
  209.     else if (user && *user && !strcmp (s,"PASS")) {
  210.       if ((state = login (t,argc,argv)) == TRANSACTION)
  211.         syslog (LOG_INFO,"Login user=%.80s host=%.80s nmsgs=%ld/%ld",
  212.             user,tcp_clienthost (),nmsgs,stream->nmsgs);
  213.     }
  214.  
  215.     else if (!strcmp (s,"AUTH")) {
  216.       if (t && *t) {    /* mechanism given? */
  217. #ifndef DISABLE_POP_PROXY
  218.         if (host) fs_give ((void **) &host);
  219. #endif
  220.         if (user) fs_give ((void **) &user);
  221.         if (pass) fs_give ((void **) &pass);
  222.         s = strtok (t," ");    /* get mechanism name */
  223.                 /* get initial response */
  224.         initial = strtok (NIL,"\015\012");
  225.         if (!(user = cpystr (mail_auth (s,responder,argc,argv)))) {
  226.           PSOUT ("-ERR Bad authentication\015\012");
  227.           syslog (LOG_INFO,"AUTHENTICATE %s failure host=%.80s",s,
  228.               tcp_clienthost ());
  229.         }
  230.         else if ((state = mbxopen ("INBOX")) == TRANSACTION)
  231.           syslog (LOG_INFO,"Auth user=%.80s host=%.80s nmsgs=%ld/%ld",
  232.               user,tcp_clienthost (),nmsgs,stream->nmsgs);
  233.       }
  234.       else {
  235.         AUTHENTICATOR *auth = mail_lookup_auth (1);
  236.         PSOUT ("+OK Supported authentication mechanisms:\015\012");
  237.         while (auth) {
  238.           if (auth->server) {
  239.         PSOUT (auth->name);
  240.         CRLF;
  241.           }
  242.           auth = auth->next;
  243.         }
  244.         PBOUT ('.');
  245.         CRLF;
  246.       }
  247.     }
  248.     else if (!strcmp (s,"APOP")) {
  249.       if (challenge[0]) {    /* can do it if have an MD5 challenge */
  250. #ifndef DISABLE_POP_PROXY
  251.         if (host) fs_give ((void **) &host);
  252. #endif
  253.         if (user) fs_give ((void **) &user);
  254.         if (pass) fs_give ((void **) &pass);
  255.                 /* get user name */
  256.         if (!(t && *t && (s = strtok (t," ")) && (t = strtok(NIL,"\012"))))
  257.           PSOUT ("-ERR Missing APOP argument\015\012");
  258.         else if (!(user = apop_login (challenge,s,t,argc,argv)))
  259.           PSOUT ("-ERR Bad APOP\015\012");
  260.         else if ((state = mbxopen ("INBOX")) == TRANSACTION)
  261.           syslog (LOG_INFO,"APOP user=%.80s host=%.80s nmsgs=%ld/%ld",
  262.               user,tcp_clienthost (),nmsgs,stream->nmsgs);
  263.       }
  264.       else PSOUT ("-ERR Not supported\015\012");
  265.     }
  266.                 /* (chuckle) */
  267.     else if (!strcmp (s,"RPOP"))
  268.       PSOUT ("-ERR Nice try, bunkie\015\012");
  269.     else PSOUT ("-ERR Unknown AUTHORIZATION state command\015\012");
  270.     break;
  271.  
  272.       case TRANSACTION:        /* logged in */
  273.     if (!strcmp (s,"STAT")) {
  274.       for (i = 1,j = 0,k = 0; i <= nmsgs; i++)
  275.         if (msg[i] > 0) {    /* message still exists? */
  276.           j++;        /* count one more undeleted message */
  277.           k += mail_elt (stream,msg[i])->rfc822_size + SLEN;
  278.         }
  279.       sprintf (tmp,"+OK %lu %lu\015\012",j,k);
  280.       PSOUT (tmp);
  281.     }
  282.     else if (!strcmp (s,"LIST")) {
  283.       if (t && *t) {    /* argument do single message */
  284.         if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) {
  285.           sprintf (tmp,"+OK %lu %lu\015\012",i,
  286.                mail_elt(stream,msg[i])->rfc822_size + SLEN);
  287.           PSOUT (tmp);
  288.         }
  289.         else PSOUT ("-ERR No such message\015\012");
  290.       }
  291.       else {        /* entire mailbox */
  292.         PSOUT ("+OK Mailbox scan listing follows\015\012");
  293.         for (i = 1,j = 0,k = 0; i <= nmsgs; i++) if (msg[i] > 0) {
  294.           sprintf (tmp,"%lu %lu\015\012",i,
  295.                mail_elt (stream,msg[i])->rfc822_size + SLEN);
  296.           PSOUT (tmp);
  297.         }
  298.         PBOUT ('.');    /* end of list */
  299.         CRLF;
  300.       }
  301.     }
  302.     else if (!strcmp (s,"UIDL")) {
  303.       if (t && *t) {    /* argument do single message */
  304.         if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) {
  305.           sprintf (tmp,"+OK %ld %08lx%08lx\015\012",i,stream->uid_validity,
  306.                mail_uid (stream,msg[i]));
  307.           PSOUT (tmp);
  308.         }
  309.         else PSOUT ("-ERR No such message\015\012");
  310.       }
  311.       else {        /* entire mailbox */
  312.         PSOUT ("+OK Unique-ID listing follows\015\012");
  313.         for (i = 1,j = 0,k = 0; i <= nmsgs; i++) if (msg[i] > 0) {
  314.           sprintf (tmp,"%ld %08lx%08lx\015\012",i,stream->uid_validity,
  315.                mail_uid (stream,msg[i]));
  316.           PSOUT (tmp);
  317.         }
  318.         PBOUT ('.');    /* end of list */
  319.         CRLF;
  320.       }
  321.     }
  322.  
  323.     else if (!strcmp (s,"RETR")) {
  324.       if (t && *t) {    /* must have an argument */
  325.         if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) {
  326.           MESSAGECACHE *elt;
  327.                 /* update highest message accessed */
  328.           if (i > last) last = i;
  329.           sprintf (tmp,"+OK %lu octets\015\012",
  330.                (elt = mail_elt (stream,msg[i]))->rfc822_size + SLEN);
  331.           PSOUT (tmp);
  332.                 /* output header */
  333.           t = mail_fetch_header (stream,msg[i],NIL,NIL,&k,FT_PEEK);
  334.           blat (t,-1,k);
  335.                 /* output status */
  336.           sprintf (tmp,STATUS,elt->seen ? "R" : " ",
  337.                elt->recent ? " " : "O");
  338.           PSOUT (tmp);
  339.           CRLF;        /* delimit header and text */
  340.                 /* output text */
  341.           t = mail_fetch_text (stream,msg[i],NIL,&k,NIL);
  342.           blat (t,-1,k);
  343.           CRLF;        /* end of list */
  344.           PBOUT ('.');
  345.           CRLF;
  346.         }
  347.         else PSOUT ("-ERR No such message\015\012");
  348.       }
  349.       else PSOUT ("-ERR Missing message number argument\015\012");
  350.     }
  351.     else if (!strcmp (s,"DELE")) {
  352.       if (t && *t) {    /* must have an argument */
  353.         if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) {
  354.                 /* update highest message accessed */
  355.           if (i > last) last = i;
  356.                 /* delete message */
  357.           sprintf (tmp,"%ld",msg[i]);
  358.           mail_setflag (stream,tmp,"\\Deleted");
  359.           msg[i] = -msg[i];    /* note that we deleted this message */
  360.           PSOUT ("+OK Message deleted\015\012");
  361.           ndele++;        /* one more message deleted */
  362.         }
  363.         else PSOUT ("-ERR No such message\015\012");
  364.       }
  365.       else PSOUT ("-ERR Missing message number argument\015\012");
  366.     }
  367.  
  368.     else if (!strcmp (s,"NOOP"))
  369.       PSOUT ("+OK No-op to you too!\015\012");
  370.     else if (!strcmp (s,"LAST")) {
  371.       sprintf (tmp,"+OK %lu\015\012",last);
  372.       PSOUT (tmp);
  373.     }
  374.     else if (!strcmp (s,"RSET")) {
  375.       rset ();        /* reset the mailbox */
  376.       PSOUT ("+OK Reset state\015\012");
  377.     }
  378.     else if (!strcmp (s,"TOP")) {
  379.       if (t && *t && (i =strtoul (t,&s,10)) && (i <= nmsgs) &&
  380.           (msg[i] > 0)) {
  381.                 /* skip whitespace */
  382.         while (isspace (*s)) *s++;
  383.         if (isdigit (*s)) {    /* make sure line count argument good */
  384.           MESSAGECACHE *elt = mail_elt (stream,msg[i]);
  385.           j = strtoul (s,NIL,10);
  386.                 /* update highest message accessed */
  387.           if (i > last) last = i;
  388.           PSOUT ("+OK Top of message follows\015\012");
  389.                 /* output header */
  390.           t = mail_fetch_header (stream,msg[i],NIL,NIL,&k,FT_PEEK);
  391.           blat (t,-1,k);
  392.                 /* output status */
  393.           sprintf (tmp,STATUS,elt->seen ? "R" : " ",
  394.                elt->recent ? " " : "O");
  395.           PSOUT (tmp);
  396.           CRLF;        /* delimit header and text */
  397.           if (j) {        /* want any text lines? */
  398.                 /* output text */
  399.         t = mail_fetch_text (stream,msg[i],NIL,&k,FT_PEEK);
  400.                 /* tie off final line if full text output */
  401.         if (j -= blat (t,j,k)) CRLF;
  402.           }
  403.           PBOUT ('.');    /* end of list */
  404.           CRLF;
  405.         }
  406.         else PSOUT ("-ERR Bad line count argument\015\012");
  407.       }
  408.       else PSOUT ("-ERR Bad message number argument\015\012");
  409.     }
  410.     else if (!strcmp (s,"XTND"))
  411.       PSOUT ("-ERR Sorry I can't do that\015\012");
  412.     else PSOUT ("-ERR Unknown TRANSACTION state command\015\012");
  413.     break;
  414.       default:
  415.         PSOUT ("-ERR Server in unknown state\015\012");
  416.     break;
  417.       }
  418.     }
  419.     PFLUSH;            /* make sure output finished */
  420.   }
  421.   if (stream && (state == UPDATE)) {
  422.     mail_expunge (stream);
  423.     syslog (LOG_INFO,"Logout user=%.80s host=%.80s nmsgs=%ld ndele=%ld",
  424.         user,tcp_clienthost (),stream->nmsgs,ndele);
  425.     mail_close (stream);
  426.   }
  427.   else syslog (LOG_INFO,"Logout user=%.80s host=%.80s",user ? user : "???",
  428.            tcp_clienthost ());
  429.   PSOUT (sayonara);        /* "now it's time to say sayonara..." */
  430.   PFLUSH;            /* make sure output finished */
  431.   return 0;            /* all done */
  432. }
  433.  
  434. /* Clock interrupt
  435.  */
  436.  
  437. void clkint ()
  438. {
  439.   PSOUT ("-ERR Autologout; idle for too long\015\012");
  440.   syslog (LOG_INFO,"Autologout user=%.80s host=%.80s",user ? user : "???",
  441.       tcp_clienthost ());
  442.   PFLUSH;            /* make sure output blatted */
  443.   if (critical) state = LOGOUT;    /* badly hosed if in critical code */
  444.   else {            /* try to gracefully close the stream */
  445.     if ((state == TRANSACTION) && !stream->lock) {
  446.       rset ();
  447.       mail_close (stream);
  448.     }
  449.     state = LOGOUT;
  450.     stream = NIL;
  451.     _exit (1);            /* die die die */
  452.   }
  453. }
  454.  
  455.  
  456. /* Kiss Of Death interrupt
  457.  */
  458.  
  459. void kodint ()
  460. {
  461.                 /* only if idle */
  462.   if (idletime && ((time (0) - idletime) > KODTIMEOUT)) {
  463.     alarm (0);            /* disable all interrupts */
  464.     server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  465.     PSOUT ("-ERR Received Kiss of Death\015\012");
  466.     syslog (LOG_INFO,"Killed (lost mailbox lock) user=%.80s host=%.80s",
  467.         user ? user : "???",tcp_clienthost ());
  468.     if (critical) state =LOGOUT;/* must defer if in critical code */
  469.     else {            /* try to gracefully close the stream */
  470.       if ((state == TRANSACTION) && !stream->lock) {
  471.     rset ();
  472.     mail_close (stream);
  473.       }
  474.       state = LOGOUT;
  475.       stream = NIL;
  476.       _exit (1);        /* die die die */
  477.     }
  478.   }
  479. }
  480.  
  481.  
  482. /* Hangup interrupt
  483.  */
  484.  
  485. void hupint ()
  486. {
  487.   alarm (0);            /* disable all interrupts */
  488.   server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  489.   syslog (LOG_INFO,"Hangup user=%.80s host=%.80s",user ? user : "???",
  490.       tcp_clienthost ());
  491.   if (critical) state = LOGOUT;    /* must defer if in critical code */
  492.   else {            /* try to gracefully close the stream */
  493.     if ((state == TRANSACTION) && !stream->lock) {
  494.       rset ();
  495.       mail_close (stream);
  496.     }
  497.     state = LOGOUT;
  498.     stream = NIL;
  499.     _exit (1);            /* die die die */
  500.   }
  501. }
  502.  
  503.  
  504. /* Termination interrupt
  505.  */
  506.  
  507. void trmint ()
  508. {
  509.   alarm (0);            /* disable all interrupts */
  510.   server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  511.   PSOUT ("-ERR Killed\015\012");
  512.   syslog (LOG_INFO,"Killed user=%.80s host=%.80s",user ? user : "???",
  513.       tcp_clienthost ());
  514.   if (critical) state = LOGOUT;    /* must defer if in critical code */
  515.   else {            /* try to gracefully close the stream */
  516.     if ((state == TRANSACTION) && !stream->lock) {
  517.       rset ();
  518.       mail_close (stream);
  519.     }
  520.     state = LOGOUT;
  521.     stream = NIL;
  522.     _exit (1);            /* die die die */
  523.   }
  524. }
  525.  
  526. /* Parse PASS command
  527.  * Accepts: pointer to command argument
  528.  * Returns: new state
  529.  */
  530.  
  531. int login (char *t,int argc,char *argv[])
  532. {
  533.   char tmp[TMPLEN];
  534.                 /* flush old passowrd */
  535.   if (pass) fs_give ((void **) &pass);
  536.   if (!(t && *t)) {        /* if no password given */
  537.     PSOUT ("-ERR Missing password argument\015\012");
  538.     return AUTHORIZATION;
  539.   }
  540.   pass = cpystr (t);        /* copy password argument */
  541. #ifndef DISABLE_POP_PROXY
  542.                 /* remote; build remote INBOX */
  543.   if (host && anonymous_login (argc,argv)) {
  544.     syslog (LOG_INFO,"IMAP login to host=%.80s user=%.80s host=%.80s",host,
  545.         user,tcp_clienthost ());
  546.     sprintf (tmp,"{%.128s/user=%.128s}INBOX",host,user);
  547.                 /* disable rimap just in case */
  548.     mail_parameters (NIL,SET_RSHTIMEOUT,0);
  549.   }
  550.                 /* local; attempt login, select INBOX */
  551.   else if (!host && server_login (user,pass,argc,argv)) strcpy (tmp,"INBOX");
  552. #else
  553.   if (server_login (user,pass,argc,argv)) strcpy (tmp,"INBOX");
  554. #endif
  555.   else {            /* vague error message to confuse crackers */
  556.     PSOUT ("-ERR Bad login\015\012");
  557.     return AUTHORIZATION;
  558.   }
  559.   return mbxopen (tmp);
  560. }
  561.  
  562. /* Authentication responder
  563.  * Accepts: challenge
  564.  *        length of challenge
  565.  *        pointer to response length return location if non-NIL
  566.  * Returns: response
  567.  */
  568.  
  569. #define RESPBUFLEN 8*MAILTMPLEN
  570.  
  571. char *responder (void *challenge,unsigned long clen,unsigned long *rlen)
  572. {
  573.   unsigned long i,j;
  574.   unsigned char *t,resp[RESPBUFLEN];
  575.   if (initial) {        /* initial response given? */
  576.     if (clen) return NIL;    /* not permitted */
  577.                 /* set up response */
  578.     t = (unsigned char *) initial;
  579.     initial = NIL;        /* no more initial response */
  580.     return (char *) rfc822_base64 (t,strlen (t),rlen ? rlen : &i);
  581.   }
  582.   PSOUT ("+ ");
  583.   for (t = rfc822_binary (challenge,clen,&i),j = 0; j < i; j++)
  584.     if (t[j] > ' ') PBOUT (t[j]);
  585.   fs_give ((void **) &t);
  586.   CRLF;
  587.   PFLUSH;            /* dump output buffer */
  588.   resp[RESPBUFLEN-1] = '\0';    /* last buffer character is guaranteed NUL */
  589.   alarm (LOGINTIMEOUT);        /* get a response under timeout */
  590.   errno = 0;            /* clear error */
  591.                 /* read buffer */
  592.   while (!PSIN ((char *) resp,RESPBUFLEN)) {
  593.     if (errno==EINTR) errno = 0;/* ignore if some interrupt */
  594.     else {
  595.       char *e = errno ? strerror (errno) : "command stream end of file";
  596.       alarm (0);        /* disable all interrupts */
  597.       server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  598.       syslog (LOG_INFO,"%s, while reading authentication host=%.80s",
  599.           e,tcp_clienthost ());
  600.       state = UPDATE;
  601.       _exit (1);
  602.     }
  603.   }
  604.   if (!(t = (unsigned char *) strchr ((char *) resp,'\012'))) {
  605.     int c;
  606.     while ((c = PBIN ()) != '\012') if (c == EOF) {
  607.       if (errno==EINTR) errno=0;/* ignore if some interrupt */
  608.       else {
  609.     char *e = errno ? strerror (errno) : "command stream end of file";
  610.     alarm (0);        /* disable all interrupts */
  611.     server_init (NIL,NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
  612.     syslog (LOG_INFO,"%s, while reading auth char user=%.80s host=%.80s",
  613.         e,user ? user : "???",tcp_clienthost ());
  614.     state = UPDATE;
  615.     _exit (1);
  616.       }
  617.     }
  618.     return NIL;
  619.   }
  620.   alarm (0);            /* make sure timeout disabled */
  621.   if (t[-1] == '\015') --t;    /* remove CR */
  622.   *t = '\0';            /* tie off buffer */
  623.   return (resp[0] != '*') ?
  624.     (char *) rfc822_base64 (resp,t-resp,rlen ? rlen : &i) : NIL;
  625. }
  626.  
  627. /* Select mailbox
  628.  * Accepts: mailbox name
  629.  * Returns: new state
  630.  */
  631.  
  632. int mbxopen (char *mailbox)
  633. {
  634.   long i,j;
  635.   char tmp[TMPLEN];
  636.   MESSAGECACHE *elt;
  637.   nmsgs = 0;            /* no messages yet */
  638.   if (msg) fs_give ((void **) &msg);
  639.                 /* open mailbox */
  640.   if (stream = mail_open (stream,mailbox,NIL)) {
  641.     if (!stream->rdonly) {    /* make sure not readonly */
  642.       if (j = stream->nmsgs) {    /* if mailbox non-empty */
  643.     sprintf (tmp,"1:%lu",j);/* fetch fast information for all messages */
  644.     mail_fetch_fast (stream,tmp,NIL);
  645.     msg = (long *) fs_get ((stream->nmsgs + 1) * sizeof (long));
  646.     for (i = 1; i <= j; i++) if (!(elt = mail_elt (stream,i))->deleted) {
  647.       msg[++nmsgs] = i;    /* note the presence of this message */
  648.       if (elt->seen) il = last = nmsgs;
  649.     }
  650.       }
  651.       sprintf (tmp,"+OK Mailbox open, %lu messages\015\012",nmsgs);
  652.       PSOUT (tmp);
  653.       return TRANSACTION;
  654.     }
  655.     else sayonara = "-ERR Can't get lock.  Mailbox in use\015\012";
  656.   }
  657.   else sayonara = "-ERR Unable to open user's INBOX\015\012";
  658.   syslog (LOG_INFO,"Error opening or locking INBOX user=%.80s host=%.80s",
  659.       user,tcp_clienthost ());
  660.   return UPDATE;
  661. }
  662.  
  663. /* Blat a string with dot checking
  664.  * Accepts: string
  665.  *        maximum number of lines if greater than zero
  666.  *        maximum number of bytes to output
  667.  * Returns: number of lines output
  668.  *
  669.  * This routine is uglier and kludgier than it should be, just to be robust
  670.  * in the case of a message which doesn't end in a newline.  Yes, this routine
  671.  * does truncate the last two bytes from the text.  Since it is normally a
  672.  * newline and the main routine adds it back, it usually does not make a
  673.  * difference.  But if it isn't, since the newline is required and the octet
  674.  * counts have to match, there's no choice but to truncate.
  675.  */
  676.  
  677. long blat (char *text,long lines,unsigned long size)
  678. {
  679.   char c,d,e;
  680.   long ret = 0;
  681.                 /* no-op if zero lines or empty string */
  682.   if (!(lines && (size-- > 2))) return 0;
  683.   c = *text++; d = *text++;    /* collect first two bytes */
  684.   if (c == '.') PBOUT ('.');    /* double string-leading dot if necessary */
  685.   while (lines && --size) {    /* copy loop */
  686.     e = *text++;        /* get next byte */
  687.     PBOUT (c);            /* output character */
  688.     if (c == '\012') {        /* end of line? */
  689.       ret++; --lines;        /* count another line */
  690.                 /* double leading dot as necessary */
  691.       if (lines && size && (d == '.')) PBOUT ('.');
  692.     }
  693.     c = d; d = e;        /* move to next character */
  694.   }
  695.   return ret;
  696. }
  697.  
  698.  
  699. /* Reset mailbox
  700.  */
  701.  
  702. void rset ()
  703. {
  704.   unsigned long i;
  705.   char tmp[20];
  706.   if (nmsgs) {            /* undelete and unmark all of our messages */
  707.     for (i = 1; i <= nmsgs; i++) { /*  */
  708.       if (msg[i] < 0) {        /* ugly and inefficient, but trustworthy */
  709.     sprintf (tmp,"%ld",msg[i] = -msg[i]);
  710.     mail_clearflag (stream,tmp,i <= il ? "\\Deleted" : "\\Deleted \\Seen");
  711.       }
  712.       else if (i > il) {
  713.     sprintf (tmp,"%ld",msg[i]);
  714.     mail_clearflag (stream,tmp,"\\Seen");
  715.       }
  716.     }
  717.     last = il;
  718.   }
  719.   ndele = 0;            /* no more deleted messages */
  720. }
  721.  
  722. /* Co-routines from MAIL library */
  723.  
  724.  
  725. /* Message matches a search
  726.  * Accepts: MAIL stream
  727.  *        message number
  728.  */
  729.  
  730. void mm_searched (MAILSTREAM *stream,unsigned long msgno)
  731. {
  732.   /* Never called */
  733. }
  734.  
  735.  
  736. /* Message exists (i.e. there are that many messages in the mailbox)
  737.  * Accepts: MAIL stream
  738.  *        message number
  739.  */
  740.  
  741. void mm_exists (MAILSTREAM *stream,unsigned long number)
  742. {
  743.   /* Can't use this mechanism.  POP has no means of notifying the client of
  744.      new mail during the session. */
  745. }
  746.  
  747.  
  748. /* Message expunged
  749.  * Accepts: MAIL stream
  750.  *        message number
  751.  */
  752.  
  753. void mm_expunged (MAILSTREAM *stream,unsigned long number)
  754. {
  755.   unsigned long i = number + 1;
  756.   msg[number] = 0;        /* I bet that this will annoy someone */
  757.   while (i <= nmsgs) --msg[i++];
  758. }
  759.  
  760.  
  761. /* Message flag status change
  762.  * Accepts: MAIL stream
  763.  *        message number
  764.  */
  765.  
  766. void mm_flags (MAILSTREAM *stream,unsigned long number)
  767. {
  768.   /* This isn't used */
  769. }
  770.  
  771.  
  772. /* Mailbox found
  773.  * Accepts: MAIL stream
  774.  *        hierarchy delimiter
  775.  *        mailbox name
  776.  *        mailbox attributes
  777.  */
  778.  
  779. void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
  780. {
  781.   /* This isn't used */
  782. }
  783.  
  784.  
  785. /* Subscribe mailbox found
  786.  * Accepts: MAIL stream
  787.  *        hierarchy delimiter
  788.  *        mailbox name
  789.  *        mailbox attributes
  790.  */
  791.  
  792. void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
  793. {
  794.   /* This isn't used */
  795. }
  796.  
  797.  
  798. /* Mailbox status
  799.  * Accepts: MAIL stream
  800.  *        mailbox name
  801.  *        mailbox status
  802.  */
  803.  
  804. void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
  805. {
  806.   /* This isn't used */
  807. }
  808.  
  809. /* Notification event
  810.  * Accepts: MAIL stream
  811.  *        string to log
  812.  *        error flag
  813.  */
  814.  
  815. void mm_notify (MAILSTREAM *stream,char *string,long errflg)
  816. {
  817.   mm_log (string,errflg);    /* just do mm_log action */
  818. }
  819.  
  820.  
  821. /* Log an event for the user to see
  822.  * Accepts: string to log
  823.  *        error flag
  824.  */
  825.  
  826. void mm_log (char *string,long errflg)
  827. {
  828.   /* Not doing anything here for now */
  829. }
  830.  
  831.  
  832. /* Log an event to debugging telemetry
  833.  * Accepts: string to log
  834.  */
  835.  
  836. void mm_dlog (char *string)
  837. {
  838.   /* Not doing anything here for now */
  839. }
  840.  
  841.  
  842. /* Get user name and password for this host
  843.  * Accepts: parse of network mailbox name
  844.  *        where to return user name
  845.  *        where to return password
  846.  *        trial count
  847.  */
  848.  
  849. void mm_login (NETMBX *mb,char *username,char *password,long trial)
  850. {
  851.                 /* set user name */
  852.   strncpy (username,*mb->user ? mb->user : user,NETMAXUSER-1);
  853.   strncpy (password,pass,255);    /* and password */
  854.   username[NETMAXUSER] = password[255] = '\0';
  855. }
  856.  
  857. /* About to enter critical code
  858.  * Accepts: stream
  859.  */
  860.  
  861. void mm_critical (MAILSTREAM *stream)
  862. {
  863.   critical = T;
  864. }
  865.  
  866.  
  867. /* About to exit critical code
  868.  * Accepts: stream
  869.  */
  870.  
  871. void mm_nocritical (MAILSTREAM *stream)
  872. {
  873.   critical = NIL;
  874. }
  875.  
  876.  
  877. /* Disk error found
  878.  * Accepts: stream
  879.  *        system error code
  880.  *        flag indicating that mailbox may be clobbered
  881.  * Returns: abort flag
  882.  */
  883.  
  884. long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
  885. {
  886.   if (serious) {        /* try your damnest if clobberage likely */
  887.     syslog (LOG_ALERT,
  888.         "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
  889.         user,tcp_clienthost (),
  890.         (stream && stream->mailbox) ? stream->mailbox : "???",
  891.         strerror (errcode));
  892.     alarm (0);            /* make damn sure timeout disabled */
  893.     sleep (60);            /* give it some time to clear up */
  894.     return NIL;
  895.   }
  896.   syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
  897.       user,tcp_clienthost (),
  898.       (stream && stream->mailbox) ? stream->mailbox : "???",
  899.       strerror (errcode));
  900.   return T;
  901. }
  902.  
  903.  
  904. /* Log a fatal error event
  905.  * Accepts: string to log
  906.  */
  907.  
  908. void mm_fatal (char *string)
  909. {
  910.   mm_log (string,ERROR);    /* shouldn't happen normally */
  911. }
  912.