pop3.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:24k
源码类别:

网络编程

开发平台:

Unix_Linux

  1. /*
  2.  * Program: Post Office Protocol 3 (POP3) client routines
  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: 6 June 1994
  13.  * Last Edited: 28 October 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 notices appear in all copies and that both the
  20.  * above copyright notices 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
  30.  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN
  32.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35. #include "mail.h"
  36. #include "osdep.h"
  37. #include <ctype.h>
  38. #include <stdio.h>
  39. #include <time.h>
  40. #include "pop3.h"
  41. #include "rfc822.h"
  42. #include "misc.h"
  43. #include "netmsg.h"
  44. #include "flstring.h"
  45. /* POP3 mail routines */
  46. /* Driver dispatch used by MAIL */
  47. DRIVER pop3driver = {
  48.   "pop3", /* driver name */
  49. /* driver flags */
  50. #ifdef INADEQUATE_MEMORY
  51.   DR_LOWMEM |
  52. #endif
  53.   DR_MAIL|DR_NOFAST|DR_CRLF|DR_NOSTICKY,
  54.   (DRIVER *) NIL, /* next driver */
  55.   pop3_valid, /* mailbox is valid for us */
  56.   pop3_parameters, /* manipulate parameters */
  57.   pop3_scan, /* scan mailboxes */
  58.   pop3_list, /* find mailboxes */
  59.   pop3_lsub, /* find subscribed mailboxes */
  60.   pop3_subscribe, /* subscribe to mailbox */
  61.   pop3_unsubscribe, /* unsubscribe from mailbox */
  62.   pop3_create, /* create mailbox */
  63.   pop3_delete, /* delete mailbox */
  64.   pop3_rename, /* rename mailbox */
  65.   pop3_status, /* status of mailbox */
  66.   pop3_open, /* open mailbox */
  67.   pop3_close, /* close mailbox */
  68.   pop3_fetchfast, /* fetch message "fast" attributes */
  69.   NIL, /* fetch message flags */
  70.   NIL, /* fetch overview */
  71.   NIL, /* fetch message structure */
  72.   pop3_header, /* fetch message header */
  73.   pop3_text, /* fetch message text */
  74.   NIL, /* fetch message */
  75.   NIL, /* unique identifier */
  76.   NIL, /* message number from UID */
  77.   NIL, /* modify flags */
  78.   NIL, /* per-message modify flags */
  79.   NIL, /* search for message based on criteria */
  80.   NIL, /* sort messages */
  81.   NIL, /* thread messages */
  82.   pop3_ping, /* ping mailbox to see if still alive */
  83.   pop3_check, /* check for new messages */
  84.   pop3_expunge, /* expunge deleted messages */
  85.   pop3_copy, /* copy messages to another mailbox */
  86.   pop3_append, /* append string message to mailbox */
  87.   NIL /* garbage collect stream */
  88. };
  89. /* prototype stream */
  90. MAILSTREAM pop3proto = {&pop3driver};
  91. /* driver parameters */
  92. static unsigned long pop3_maxlogintrials = MAXLOGINTRIALS;
  93. static long pop3_port = 0;
  94. static long pop3_altport = 0;
  95. static char *pop3_altname = NIL;
  96. /* POP3 mail validate mailbox
  97.  * Accepts: mailbox name
  98.  * Returns: our driver if name is valid, NIL otherwise
  99.  */
  100. DRIVER *pop3_valid (char *name)
  101. {
  102.   DRIVER *drv;
  103.   char mbx[MAILTMPLEN];
  104.   return ((drv = mail_valid_net (name,&pop3driver,NIL,mbx)) &&
  105.   !strcmp (ucase (mbx),"INBOX")) ? drv : NIL;
  106. }
  107. /* News manipulate driver parameters
  108.  * Accepts: function code
  109.  *     function-dependent value
  110.  * Returns: function-dependent return value
  111.  */
  112. void *pop3_parameters (long function,void *value)
  113. {
  114.   switch ((int) function) {
  115.   case SET_MAXLOGINTRIALS:
  116.     pop3_maxlogintrials = (unsigned long) value;
  117.     break;
  118.   case GET_MAXLOGINTRIALS:
  119.     value = (void *) pop3_maxlogintrials;
  120.     break;
  121.   case SET_POP3PORT:
  122.     pop3_port = (long) value;
  123.     break;
  124.   case GET_POP3PORT:
  125.     value = (void *) pop3_port;
  126.     break;
  127.   case SET_ALTPOPPORT:
  128.     pop3_altport = (long) value;
  129.     break;
  130.   case GET_ALTPOPPORT:
  131.     value = (void *) pop3_altport;
  132.     break;
  133.   case SET_ALTPOPNAME:
  134.     pop3_altname = (char *) value;
  135.     break;
  136.   case GET_ALTPOPNAME:
  137.     value = (void *) pop3_altname;
  138.     break;
  139.   default:
  140.     value = NIL; /* error case */
  141.     break;
  142.   }
  143.   return value;
  144. }
  145. /* POP3 mail scan mailboxes for string
  146.  * Accepts: mail stream
  147.  *     reference
  148.  *     pattern to search
  149.  *     string to scan
  150.  */
  151. void pop3_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
  152. {
  153.   char tmp[MAILTMPLEN];
  154.   if ((ref && *ref) ? /* have a reference */
  155.       (pop3_valid (ref) && pmatch ("INBOX",pat)) :
  156.       (mail_valid_net (pat,&pop3driver,NIL,tmp) && pmatch ("INBOX",tmp)))
  157.     mm_log ("Scan not valid for POP3 mailboxes",ERROR);
  158. }
  159. /* POP3 mail find list of all mailboxes
  160.  * Accepts: mail stream
  161.  *     reference
  162.  *     pattern to search
  163.  */
  164. void pop3_list (MAILSTREAM *stream,char *ref,char *pat)
  165. {
  166.   char tmp[MAILTMPLEN];
  167.   if (ref && *ref) { /* have a reference */
  168.     if (pop3_valid (ref) && pmatch ("INBOX",pat)) {
  169.       strcpy (strchr (strcpy (tmp,ref),'}')+1,"INBOX");
  170.       mm_list (stream,NIL,tmp,LATT_NOINFERIORS);
  171.     }
  172.   }
  173.   else if (mail_valid_net (pat,&pop3driver,NIL,tmp) && pmatch ("INBOX",tmp)) {
  174.     strcpy (strchr (strcpy (tmp,pat),'}')+1,"INBOX");
  175.     mm_list (stream,NIL,tmp,LATT_NOINFERIORS);
  176.   }
  177. }
  178. /* POP3 mail find list of subscribed mailboxes
  179.  * Accepts: mail stream
  180.  *     reference
  181.  *     pattern to search
  182.  */
  183. void pop3_lsub (MAILSTREAM *stream,char *ref,char *pat)
  184. {
  185.   void *sdb = NIL;
  186.   char *s,mbx[MAILTMPLEN];
  187.   if (*pat == '{') { /* if remote pattern, must be POP3 */
  188.     if (!pop3_valid (pat)) return;
  189.     ref = NIL; /* good POP3 pattern, punt reference */
  190.   }
  191. /* if remote reference, must be valid POP3 */
  192.   if (ref && (*ref == '{') && !pop3_valid (ref)) return;
  193. /* kludgy application of reference */
  194.   if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
  195.   else strcpy (mbx,pat);
  196.   if (s = sm_read (&sdb)) do if (pop3_valid (s) && pmatch (s,mbx))
  197.     mm_lsub (stream,NIL,s,NIL);
  198.   while (s = sm_read (&sdb)); /* until no more subscriptions */
  199. }
  200. /* POP3 mail subscribe to mailbox
  201.  * Accepts: mail stream
  202.  *     mailbox to add to subscription list
  203.  * Returns: T on success, NIL on failure
  204.  */
  205. long pop3_subscribe (MAILSTREAM *stream,char *mailbox)
  206. {
  207.   return sm_subscribe (mailbox);
  208. }
  209. /* POP3 mail unsubscribe to mailbox
  210.  * Accepts: mail stream
  211.  *     mailbox to delete from subscription list
  212.  * Returns: T on success, NIL on failure
  213.  */
  214. long pop3_unsubscribe (MAILSTREAM *stream,char *mailbox)
  215. {
  216.   return sm_unsubscribe (mailbox);
  217. }
  218. /* POP3 mail create mailbox
  219.  * Accepts: mail stream
  220.  *     mailbox name to create
  221.  * Returns: T on success, NIL on failure
  222.  */
  223. long pop3_create (MAILSTREAM *stream,char *mailbox)
  224. {
  225.   return NIL; /* never valid for POP3 */
  226. }
  227. /* POP3 mail delete mailbox
  228.  *     mailbox name to delete
  229.  * Returns: T on success, NIL on failure
  230.  */
  231. long pop3_delete (MAILSTREAM *stream,char *mailbox)
  232. {
  233.   return NIL; /* never valid for POP3 */
  234. }
  235. /* POP3 mail rename mailbox
  236.  * Accepts: mail stream
  237.  *     old mailbox name
  238.  *     new mailbox name
  239.  * Returns: T on success, NIL on failure
  240.  */
  241. long pop3_rename (MAILSTREAM *stream,char *old,char *newname)
  242. {
  243.   return NIL; /* never valid for POP3 */
  244. }
  245. /* POP3 status
  246.  * Accepts: mail stream
  247.  *     mailbox name
  248.  *     status flags
  249.  * Returns: T on success, NIL on failure
  250.  */
  251. long pop3_status (MAILSTREAM *stream,char *mbx,long flags)
  252. {
  253.   MAILSTATUS status;
  254.   unsigned long i;
  255.   long ret = NIL;
  256.   MAILSTREAM *tstream =
  257.     (stream && LOCAL->netstream && mail_usable_network_stream (stream,mbx)) ?
  258.       stream : mail_open (NIL,mbx,OP_SILENT);
  259.   if (tstream) { /* have a usable stream? */
  260.     status.flags = flags; /* return status values */
  261.     status.messages = tstream->nmsgs;
  262.     status.recent = tstream->recent;
  263.     if (flags & SA_UNSEEN) /* must search to get unseen messages */
  264.       for (i = 1,status.unseen = 0; i < tstream->nmsgs; i++)
  265. if (!mail_elt (tstream,i)->seen) status.unseen++;
  266.     status.uidnext = tstream->uid_last + 1;
  267.     status.uidvalidity = tstream->uid_validity;
  268. /* pass status to main program */
  269.     mm_status (tstream,mbx,&status);
  270.     if (stream != tstream) mail_close (tstream);
  271.     ret = LONGT;
  272.   }
  273.   return ret; /* success */
  274. }
  275. /* POP3 mail open
  276.  * Accepts: stream to open
  277.  * Returns: stream on success, NIL on failure
  278.  */
  279. MAILSTREAM *pop3_open (MAILSTREAM *stream)
  280. {
  281.   unsigned long i;
  282.   char tmp[MAILTMPLEN],usr[MAILTMPLEN];
  283.   NETMBX mb;
  284.   MESSAGECACHE *elt;
  285. /* return prototype for OP_PROTOTYPE call */
  286.   if (!stream) return &pop3proto;
  287.   mail_valid_net_parse (stream->mailbox,&mb);
  288.   if (stream->local) fatal ("pop3 recycle stream");
  289. /* /anonymous not supported */
  290.   if (mb.anoflag || stream->anonymous) {
  291.     mm_log ("Anonymous POP3 login not available",ERROR);
  292.     return NIL;
  293.   }
  294. /* copy other switches */
  295.   if (mb.dbgflag) stream->debug = T;
  296.   if (mb.secflag) stream->secure = T;
  297.   mb.tryaltflag = stream->tryalt;
  298.   stream->local = fs_get (sizeof (POP3LOCAL));
  299.   stream->sequence++; /* bump sequence number */
  300.   stream->perm_deleted = T; /* deleted is only valid flag */
  301.   LOCAL->response = LOCAL->reply = NIL;
  302. /* currently no message */
  303.   LOCAL->msgno = LOCAL->hdrsize = 0;
  304.   LOCAL->txt = NIL; /* no file initially */
  305.   if ((LOCAL->netstream = /* try to open connection */
  306.        net_open (&mb,NIL,pop3_port ? pop3_port : POP3TCPPORT,
  307.  (NETDRIVER *) mail_parameters (NIL,GET_ALTDRIVER,NIL),
  308.  (char *) mail_parameters (NIL,GET_ALTPOPNAME,NIL),
  309.  (unsigned long) mail_parameters (NIL,GET_ALTPOPPORT,NIL))) &&
  310.       pop3_reply (stream)) {
  311.     mm_log (LOCAL->reply,NIL); /* give greeting */
  312.     if (!pop3_auth (stream,&mb,tmp,usr)) pop3_close (stream,NIL);
  313.     else if (pop3_send (stream,"STAT",NIL)) {
  314.       int silent = stream->silent;
  315.       stream->silent = T;
  316.       sprintf (tmp,"{%s:%lu/pop3",net_host (LOCAL->netstream),
  317.        net_port (LOCAL->netstream));
  318.       if (mb.altflag) sprintf (tmp + strlen (tmp),"/%s",(char *)
  319.        mail_parameters (NIL,GET_ALTDRIVERNAME,NIL));
  320.       if (mb.secflag) strcat (tmp,"/secure");
  321.       sprintf (tmp + strlen (tmp),"/user="%s"}INBOX",usr);
  322.       stream->inbox = T; /* always INBOX */
  323.       fs_give ((void **) &stream->mailbox);
  324.       stream->mailbox = cpystr (tmp);
  325. /* notify upper level */
  326.       mail_exists (stream,stream->uid_last = strtoul (LOCAL->reply,NIL,10));
  327.       mail_recent (stream,stream->nmsgs);
  328. /* instantiate elt */
  329.       for (i = 0; i < stream->nmsgs;) {
  330. elt = mail_elt (stream,++i);
  331. elt->valid = elt->recent = T;
  332. elt->private.uid = i;
  333.       }
  334.       stream->silent = silent; /* notify main program */
  335.       mail_exists (stream,stream->nmsgs);
  336. /* notify if empty */
  337.       if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",WARN);
  338.     }
  339.     else { /* error in STAT */
  340.       mm_log (LOCAL->reply,ERROR);
  341.       pop3_close (stream,NIL); /* too bad */
  342.     }
  343.   }
  344.   else { /* connection failed */
  345.     if (LOCAL->reply) mm_log (LOCAL->reply,ERROR);
  346.     pop3_close (stream,NIL); /* failed, clean up */
  347.   }
  348.   return LOCAL ? stream : NIL; /* if stream is alive, return to caller */
  349. }
  350. /* POP3 authenticate
  351.  * Accepts: stream to login
  352.  *     parsed network mailbox structure
  353.  *     scratch buffer
  354.  *     place to return user name
  355.  * Returns: T on success, NIL on failure
  356.  */
  357. long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr)
  358. {
  359.   unsigned long i,auths = 0;
  360.   char *t;
  361.   AUTHENTICATOR *at;
  362. /* get list of authenticators */
  363.   if (pop3_send (stream,"AUTH",NIL)) {
  364.     while ((t = net_getline (LOCAL->netstream)) && (t[1] || (*t != '.'))) {
  365.       if (stream->debug) mm_dlog (t);
  366.       if ((i = mail_lookup_auth_name (t,stream->secure)) &&
  367.   (--i < (8*sizeof (unsigned long)))) auths |= (1 << i);
  368.       fs_give ((void **) &t);
  369.     }
  370.     if (t) { /* flush end of text indicator */
  371.       if (stream->debug) mm_dlog (t);
  372.       fs_give ((void **) &t);
  373.     }
  374.   }
  375.   if (auths) { /* got any authenticators? */
  376.     for (t = NIL; LOCAL->netstream && auths &&
  377.  (at = mail_lookup_auth (find_rightmost_bit (&auths)+1)); ) {
  378.       if (t) { /* previous authenticator failed? */
  379. sprintf (tmp,"Retrying using %.80s authentication after %.80s",
  380.  at->name,t);
  381. mm_log (tmp,NIL);
  382. fs_give ((void **) &t);
  383.       }
  384.       for (i = 1,tmp[0] = ''; /* until run out of trials */
  385.    LOCAL->netstream && i && (i <= pop3_maxlogintrials); ) {
  386. if (tmp[0]) mm_log (tmp,WARN);
  387. if (pop3_send (stream,"AUTH",at->name) &&
  388.     (*at->client) (pop3_challenge,pop3_response,mb,stream,&i,usr) &&
  389.     LOCAL->response && (*LOCAL->response == '+')) return LONGT;
  390. t = cpystr (LOCAL->reply);
  391. sprintf (tmp,"Retrying %s authentication after %s",at->name,t);
  392.       }
  393.     }
  394.     if (t) { /* previous authenticator failed? */
  395.       sprintf (tmp,"Can not authenticate to POP3 server: %.80s",t);
  396.       mm_log (tmp,ERROR);
  397.       fs_give ((void **) &t);
  398.     }
  399.   }
  400.   else if (stream->secure)
  401.     mm_log ("Can't do secure authentication with this server",ERROR);
  402.   else { /* traditional login */
  403.     for (i = 0; LOCAL->netstream && (i < pop3_maxlogintrials); ++i) {
  404.       tmp[0] = ''; /* prompt user for password */
  405.       mm_login (mb,usr,tmp,i);
  406.       if (tmp[0]) { /* send login sequence */
  407. if (pop3_send (stream,"USER",usr) && pop3_send (stream,"PASS",tmp))
  408.   return LONGT; /* success */
  409. mm_log (LOCAL->reply,WARN);
  410.       }
  411.       else { /* user refused to give a password */
  412. mm_log ("Login aborted",ERROR);
  413. return NIL;
  414.       }
  415.     }
  416.     mm_log ("Too many login failures",ERROR);
  417.   }
  418.   return NIL; /* ran out of authenticators */
  419. }
  420. /* Get challenge to authenticator in binary
  421.  * Accepts: stream
  422.  *     pointer to returned size
  423.  * Returns: challenge or NIL if not challenge
  424.  */
  425. void *pop3_challenge (void *s,unsigned long *len)
  426. {
  427.   MAILSTREAM *stream = (MAILSTREAM *) s;
  428.   return ((*LOCAL->response == '+') && (LOCAL->response[1] == ' ')) ?
  429.     rfc822_base64 ((unsigned char *) LOCAL->reply,strlen (LOCAL->reply),len) :
  430.       NIL;
  431. }
  432. /* Send authenticator response in BASE64
  433.  * Accepts: MAIL stream
  434.  *     string to send
  435.  *     length of string
  436.  * Returns: T if successful, else NIL
  437.  */
  438. long pop3_response (void *s,char *response,unsigned long size)
  439. {
  440.   MAILSTREAM *stream = (MAILSTREAM *) s;
  441.   unsigned long i,j,ret;
  442.   char *t,*u;
  443.   if (response) { /* make CRLFless BASE64 string */
  444.     if (size) {
  445.       for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
  446.    j < i; j++) if (t[j] > ' ') *u++ = t[j];
  447.       *u = ''; /* tie off string for mm_dlog() */
  448.       if (stream->debug) mm_dlog (t);
  449. /* append CRLF */
  450.       *u++ = '15'; *u++ = '12'; *u = '';
  451.       ret = net_sout (LOCAL->netstream,t,u - t);
  452.       fs_give ((void **) &t);
  453.     }
  454.     else ret = net_sout (LOCAL->netstream,"1512",2);
  455.   }
  456. /* abort requested */
  457.   else ret = net_sout (LOCAL->netstream,"*1512",3);
  458.   pop3_reply (stream); /* set up response */
  459.   return ret;
  460. }
  461. /* POP3 mail close
  462.  * Accepts: MAIL stream
  463.  *     option flags
  464.  */
  465. void pop3_close (MAILSTREAM *stream,long options)
  466. {
  467.   int silent = stream->silent;
  468.   if (LOCAL) { /* only if a file is open */
  469.     if (LOCAL->netstream) { /* close POP3 connection */
  470.       stream->silent = T;
  471.       if (options & CL_EXPUNGE) pop3_expunge (stream);
  472.       stream->silent = silent;
  473.       pop3_send (stream,"QUIT",NIL);
  474.       mm_notify (stream,LOCAL->reply,BYE);
  475.     }
  476. /* close POP3 connection */
  477.     if (LOCAL->netstream) net_close (LOCAL->netstream);
  478.     if (LOCAL->txt) fclose (LOCAL->txt);
  479.     LOCAL->txt = NIL;
  480.     if (LOCAL->response) fs_give ((void **) &LOCAL->response);
  481. /* nuke the local data */
  482.     fs_give ((void **) &stream->local);
  483.     stream->dtb = NIL; /* log out the DTB */
  484.   }
  485. }
  486. /* POP3 mail fetch fast information
  487.  * Accepts: MAIL stream
  488.  *     sequence
  489.  *     option flags
  490.  * This is ugly and slow
  491.  */
  492. void pop3_fetchfast (MAILSTREAM *stream,char *sequence,long flags)
  493. {
  494.   unsigned long i;
  495.   MESSAGECACHE *elt;
  496. /* get sequence */
  497.   if (stream && LOCAL && ((flags & FT_UID) ?
  498.   mail_uid_sequence (stream,sequence) :
  499.   mail_sequence (stream,sequence)))
  500.     for (i = 1; i <= stream->nmsgs; i++)
  501.       if ((elt = mail_elt (stream,i))->sequence &&
  502.   !(elt->day && !elt->rfc822_size)) {
  503. ENVELOPE **env = NIL;
  504. ENVELOPE *e = NIL;
  505. if (!stream->scache) env = &elt->private.msg.env;
  506. else if (stream->msgno == i) env = &stream->env;
  507. else env = &e;
  508. if (!*env || !elt->rfc822_size) {
  509.   STRING bs;
  510.   unsigned long hs;
  511.   char *ht = (*stream->dtb->header) (stream,i,&hs,NIL);
  512. /* need to make an envelope? */
  513.   if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST,
  514.        stream->dtb->flags);
  515. /* need message size too, ugh */
  516.   if (!elt->rfc822_size) {
  517.     (*stream->dtb->text) (stream,i,&bs,FT_PEEK);
  518.     elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs);
  519.   }
  520. }
  521. /* if need date, have date in envelope? */
  522. if (!elt->day && *env && (*env)->date)
  523.   mail_parse_date (elt,(*env)->date);
  524. /* sigh, fill in bogus default */
  525. if (!elt->day) mail_parse_date (elt,"01-JAN-1969 00:00:00 +0000");
  526. mail_free_envelope (&e);
  527.       }
  528. }
  529. /* POP3 fetch header as text
  530.  * Accepts: mail stream
  531.  *     message number
  532.  *     pointer to return size
  533.  *     flags
  534.  * Returns: header text
  535.  */
  536. char *pop3_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
  537.    long flags)
  538. {
  539.   MESSAGECACHE *elt;
  540.   if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return NIL;
  541. /* have header text? */
  542.   if (!(elt = mail_elt (stream,msgno))->private.msg.header.text.data) {
  543.     elt->private.msg.header.text.size = pop3_cache (stream,elt);
  544. /* read the header */
  545.     fread (elt->private.msg.header.text.data = (unsigned char *)
  546.    fs_get ((size_t) elt->private.msg.header.text.size + 1),
  547.    (size_t) 1,(size_t) elt->private.msg.header.text.size,LOCAL->txt);
  548.     elt->private.msg.header.text.data[elt->private.msg.header.text.size] ='';
  549.   }
  550. /* return size of text */
  551.   if (size) *size = elt->private.msg.header.text.size;
  552.   return (char *) elt->private.msg.header.text.data;
  553. }
  554. /* POP3 fetch body
  555.  * Accepts: mail stream
  556.  *     message number
  557.  *     pointer to stringstruct to initialize
  558.  *     flags
  559.  * Returns: T if successful, else NIL
  560.  */
  561. long pop3_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
  562. {
  563.   MESSAGECACHE *elt;
  564.   INIT (bs,mail_string,(void *) "",0);
  565.   if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return NIL;
  566.   elt = mail_elt (stream,msgno);
  567.   pop3_cache (stream,elt); /* make sure cache loaded */
  568.   if (!LOCAL->txt) return NIL; /* error if don't have a file */
  569.   if (!(flags & FT_PEEK)) { /* mark seen if needed */
  570.     elt->seen = T;
  571.     mm_flags (stream,elt->msgno);
  572.   }
  573.   INIT (bs,file_string,(void *) LOCAL->txt,elt->rfc822_size);
  574.   SETPOS (bs,LOCAL->hdrsize); /* skip past header */
  575.   return T;
  576. }
  577. /* POP3 cache message
  578.  * Accepts: mail stream
  579.  *     message number
  580.  * Returns: header size
  581.  */
  582. unsigned long pop3_cache (MAILSTREAM *stream,MESSAGECACHE *elt)
  583. {
  584. /* already cached? */
  585.   if (LOCAL->msgno != elt->msgno) {
  586. /* no, close current file */
  587.     if (LOCAL->txt) fclose (LOCAL->txt);
  588.     LOCAL->txt = NIL;
  589.     LOCAL->msgno = LOCAL->hdrsize = 0;
  590.     if (pop3_send_num (stream,"RETR",elt->msgno)) {
  591.       LOCAL->msgno = elt->msgno;/* set as current message number */
  592. /* load the cache */
  593.       LOCAL->txt = netmsg_slurp (LOCAL->netstream,&elt->rfc822_size,
  594.  &LOCAL->hdrsize);
  595.     }
  596.     else elt->deleted = T;
  597.   }
  598.   return LOCAL->hdrsize;
  599. }
  600. /* POP3 mail ping mailbox
  601.  * Accepts: MAIL stream
  602.  * Returns: T if stream alive, else NIL
  603.  */
  604. long pop3_ping (MAILSTREAM *stream)
  605. {
  606.   return pop3_send (stream,"NOOP",NIL);
  607. }
  608. /* POP3 mail check mailbox
  609.  * Accepts: MAIL stream
  610.  */
  611. void pop3_check (MAILSTREAM *stream)
  612. {
  613.   if (pop3_ping (stream)) mm_log ("Check completed",NIL);
  614. }
  615. /* POP3 mail expunge mailbox
  616.  * Accepts: MAIL stream
  617.  */
  618. void pop3_expunge (MAILSTREAM *stream)
  619. {
  620.   char tmp[MAILTMPLEN];
  621.   unsigned long i = 1,n = 0;
  622.   while (i <= stream->nmsgs) {
  623.     if (mail_elt (stream,i)->deleted && pop3_send_num (stream,"DELE",i)) {
  624.       mail_expunged (stream,i);
  625.       n++;
  626.     }
  627.     else i++; /* try next message */
  628.   }
  629.   if (!stream->silent) { /* only if not silent */
  630.     if (n) { /* did we expunge anything? */
  631.       sprintf (tmp,"Expunged %lu messages",n);
  632.       mm_log (tmp,(long) NIL);
  633.     }
  634.     else mm_log ("No messages deleted, so no update needed",(long) NIL);
  635.   }
  636. }
  637. /* POP3 mail copy message(s)
  638.  * Accepts: MAIL stream
  639.  *     sequence
  640.  *     destination mailbox
  641.  *     option flags
  642.  * Returns: T if copy successful, else NIL
  643.  */
  644. long pop3_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
  645. {
  646.   mailproxycopy_t pc =
  647.     (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
  648.   if (pc) return (*pc) (stream,sequence,mailbox,options);
  649.   mm_log ("Copy not valid for POP3",ERROR);
  650.   return NIL;
  651. }
  652. /* POP3 mail append message from stringstruct
  653.  * Accepts: MAIL stream
  654.  *     destination mailbox
  655.  *     stringstruct of messages to append
  656.  * Returns: T if append successful, else NIL
  657.  */
  658. long pop3_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  659.   STRING *message)
  660. {
  661.   mm_log ("Append not valid for POP3",ERROR);
  662.   return NIL;
  663. }
  664. /* Internal routines */
  665. /* Post Office Protocol 3 send command with number argument
  666.  * Accepts: MAIL stream
  667.  *     command
  668.  *     number
  669.  * Returns: T if successful, NIL if failure
  670.  */
  671. long pop3_send_num (MAILSTREAM *stream,char *command,unsigned long n)
  672. {
  673.   char tmp[MAILTMPLEN];
  674.   sprintf (tmp,"%lu",mail_uid (stream,n));
  675.   return pop3_send (stream,command,tmp);
  676. }
  677. /* Post Office Protocol 3 send command
  678.  * Accepts: MAIL stream
  679.  *     command
  680.  *     command argument
  681.  * Returns: T if successful, NIL if failure
  682.  */
  683. long pop3_send (MAILSTREAM *stream,char *command,char *args)
  684. {
  685.   char tmp[8*MAILTMPLEN];
  686.   long ret;
  687.   mail_lock (stream); /* lock up the stream */
  688.   if (!LOCAL->netstream) ret = pop3_fake (stream,"No-op dead stream");
  689.   else { /* build the complete command */
  690.     if (args) sprintf (tmp,"%s %s",command,args);
  691.     else strcpy (tmp,command);
  692.     if (stream->debug) mm_dlog (tmp);
  693.     strcat (tmp,"1512");
  694. /* send the command */
  695.     ret = net_soutr (LOCAL->netstream,tmp) ? pop3_reply (stream) :
  696.       pop3_fake (stream,"POP3 connection broken in command");
  697.   }
  698.   mail_unlock (stream); /* unlock stream */
  699.   return ret;
  700. }
  701. /* Post Office Protocol 3 get reply
  702.  * Accepts: MAIL stream
  703.  * Returns: T if success reply, NIL if error reply
  704.  */
  705. long pop3_reply (MAILSTREAM *stream)
  706. {
  707.   char *s;
  708. /* flush old reply */
  709.   if (LOCAL->response) fs_give ((void **) &LOCAL->response);
  710.    /* get reply */
  711.   if (!(LOCAL->response = net_getline (LOCAL->netstream)))
  712.     return pop3_fake (stream,"POP3 connection broken in response");
  713.   if (stream->debug) mm_dlog (LOCAL->response);
  714.   LOCAL->reply = (s = strchr (LOCAL->response,' ')) ? s + 1 : LOCAL->response;
  715. /* return success or failure */
  716.   return (*LOCAL->response =='+') ? T : NIL;
  717. }
  718. /* Post Office Protocol 3 set fake error
  719.  * Accepts: MAIL stream
  720.  *     error text
  721.  * Returns: NIL, always
  722.  */
  723. long pop3_fake (MAILSTREAM *stream,char *text)
  724. {
  725.   mm_notify (stream,text,BYE); /* send bye alert */
  726.   if (LOCAL->netstream) net_close (LOCAL->netstream);
  727.   LOCAL->netstream = NIL; /* farewell, dear TCP stream */
  728. /* flush any old reply */
  729.   if (LOCAL->response) fs_give ((void **) &LOCAL->response);
  730.   LOCAL->reply = text; /* set up pseudo-reply string */
  731.   return NIL; /* return error code */
  732. }