4e29d6edc1914be7077734d3c30f4995293f9bde
[external/uw-imap-toolkit.git] / imap-2007e / c-client / pop3.c
1 /* ========================================================================
2  * Copyright 1988-2007 University of Washington
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * 
11  * ========================================================================
12  */
13
14 /*
15  * Program:     Post Office Protocol 3 (POP3) client routines
16  *
17  * Author:      Mark Crispin
18  *              Networks and Distributed Computing
19  *              Computing & Communications
20  *              University of Washington
21  *              Administration Building, AG-44
22  *              Seattle, WA  98195
23  *              Internet: MRC@CAC.Washington.EDU
24  *
25  * Date:        6 June 1994
26  * Last Edited: 4 April 2007
27  */
28
29
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <time.h>
33 #include "c-client.h"
34 #include "flstring.h"
35 #include "netmsg.h"
36 \f
37 /* Parameters */
38
39 #define POP3TCPPORT (long) 110  /* assigned TCP contact port */
40 #define POP3SSLPORT (long) 995  /* assigned SSL TCP contact port */
41 #define IDLETIMEOUT (long) 10   /* defined in RFC 1939 */
42
43
44 /* POP3 I/O stream local data */
45         
46 typedef struct pop3_local {
47   NETSTREAM *netstream;         /* TCP I/O stream */
48   char *response;               /* last server reply */
49   char *reply;                  /* text of last server reply */
50   unsigned long cached;         /* current cached message uid */
51   unsigned long hdrsize;        /* current cached header size */
52   FILE *txt;                    /* current cached file descriptor */
53   struct {
54     unsigned int capa : 1;      /* server has CAPA, definitely new */
55     unsigned int expire : 1;    /* server has EXPIRE */
56     unsigned int logindelay : 1;/* server has LOGIN-DELAY */
57     unsigned int stls : 1;      /* server has STLS */
58     unsigned int pipelining : 1;/* server has PIPELINING */
59     unsigned int respcodes : 1; /* server has RESP-CODES */
60     unsigned int top : 1;       /* server has TOP */
61     unsigned int uidl : 1;      /* server has UIDL */
62     unsigned int user : 1;      /* server has USER */
63     char *implementation;       /* server implementation string */
64     long delaysecs;             /* minimum time between login (neg variable) */
65     long expiredays;            /* server-guaranteed minimum retention days */
66                                 /* supported authenticators */
67     unsigned int sasl : MAXAUTHENTICATORS;
68   } cap;
69   unsigned int sensitive : 1;   /* sensitive data in progress */
70   unsigned int loser : 1;       /* server is a loser */
71   unsigned int saslcancel : 1;  /* SASL cancelled by protocol */
72 } POP3LOCAL;
73
74
75 /* Convenient access to local data */
76
77 #define LOCAL ((POP3LOCAL *) stream->local)
78 \f
79 /* Function prototypes */
80
81 DRIVER *pop3_valid (char *name);
82 void *pop3_parameters (long function,void *value);
83 void pop3_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
84 void pop3_list (MAILSTREAM *stream,char *ref,char *pat);
85 void pop3_lsub (MAILSTREAM *stream,char *ref,char *pat);
86 long pop3_subscribe (MAILSTREAM *stream,char *mailbox);
87 long pop3_unsubscribe (MAILSTREAM *stream,char *mailbox);
88 long pop3_create (MAILSTREAM *stream,char *mailbox);
89 long pop3_delete (MAILSTREAM *stream,char *mailbox);
90 long pop3_rename (MAILSTREAM *stream,char *old,char *newname);
91 long pop3_status (MAILSTREAM *stream,char *mbx,long flags);
92 MAILSTREAM *pop3_open (MAILSTREAM *stream);
93 long pop3_capa (MAILSTREAM *stream,long flags);
94 long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr, int apop);
95 void *pop3_challenge (void *stream,unsigned long *len);
96 long pop3_response (void *stream,char *s,unsigned long size);
97 void pop3_close (MAILSTREAM *stream,long options);
98 void pop3_fetchfast (MAILSTREAM *stream,char *sequence,long flags);
99 char *pop3_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
100                    long flags);
101 long pop3_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
102 unsigned long pop3_cache (MAILSTREAM *stream,MESSAGECACHE *elt);
103 long pop3_ping (MAILSTREAM *stream);
104 void pop3_check (MAILSTREAM *stream);
105 long pop3_expunge (MAILSTREAM *stream,char *sequence,long options);
106 long pop3_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
107 long pop3_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
108
109 long pop3_send_num (MAILSTREAM *stream,char *command,unsigned long n);
110 long pop3_send (MAILSTREAM *stream,char *command,char *args);
111 long pop3_reply (MAILSTREAM *stream);
112 long pop3_fake (MAILSTREAM *stream,char *text);
113 \f
114 /* POP3 mail routines */
115
116
117 /* Driver dispatch used by MAIL */
118
119 DRIVER pop3driver = {
120   "pop3",                       /* driver name */
121                                 /* driver flags */
122 #ifdef INADEQUATE_MEMORY
123   DR_LOWMEM |
124 #endif
125   DR_MAIL|DR_NOFAST|DR_CRLF|DR_NOSTICKY|DR_NOINTDATE|DR_NONEWMAIL,
126   (DRIVER *) NIL,               /* next driver */
127   pop3_valid,                   /* mailbox is valid for us */
128   pop3_parameters,              /* manipulate parameters */
129   pop3_scan,                    /* scan mailboxes */
130   pop3_list,                    /* find mailboxes */
131   pop3_lsub,                    /* find subscribed mailboxes */
132   pop3_subscribe,               /* subscribe to mailbox */
133   pop3_unsubscribe,             /* unsubscribe from mailbox */
134   pop3_create,                  /* create mailbox */
135   pop3_delete,                  /* delete mailbox */
136   pop3_rename,                  /* rename mailbox */
137   pop3_status,                  /* status of mailbox */
138   pop3_open,                    /* open mailbox */
139   pop3_close,                   /* close mailbox */
140   pop3_fetchfast,               /* fetch message "fast" attributes */
141   NIL,                          /* fetch message flags */
142   NIL,                          /* fetch overview */
143   NIL,                          /* fetch message structure */
144   pop3_header,                  /* fetch message header */
145   pop3_text,                    /* fetch message text */
146   NIL,                          /* fetch message */
147   NIL,                          /* unique identifier */
148   NIL,                          /* message number from UID */
149   NIL,                          /* modify flags */
150   NIL,                          /* per-message modify flags */
151   NIL,                          /* search for message based on criteria */
152   NIL,                          /* sort messages */
153   NIL,                          /* thread messages */
154   pop3_ping,                    /* ping mailbox to see if still alive */
155   pop3_check,                   /* check for new messages */
156   pop3_expunge,                 /* expunge deleted messages */
157   pop3_copy,                    /* copy messages to another mailbox */
158   pop3_append,                  /* append string message to mailbox */
159   NIL                           /* garbage collect stream */
160 };
161
162                                 /* prototype stream */
163 MAILSTREAM pop3proto = {&pop3driver};
164
165                                 /* driver parameters */
166 static unsigned long pop3_maxlogintrials = MAXLOGINTRIALS;
167 static long pop3_port = 0;
168 static long pop3_sslport = 0;
169 \f
170 #if 1 //Added for generating digest for APOP authentication
171 #define MD5BLKLEN 64            /* MD5 block length */
172 #define MD5DIGLEN 16            /* MD5 digest length */
173 typedef struct {
174   unsigned long chigh;          /* high 32bits of byte count */
175   unsigned long clow;           /* low 32bits of byte count */
176   unsigned long state[4];       /* state (ABCD) */
177   unsigned char buf[MD5BLKLEN]; /* input buffer */
178   unsigned char *ptr;           /* buffer position */
179 } MD5CONTEXT;
180 void md5_init (MD5CONTEXT *ctx);
181 void md5_update (MD5CONTEXT *ctx,unsigned char *data,unsigned long len);
182 void md5_final (unsigned char *digest,MD5CONTEXT *ctx);
183 static void md5_transform (unsigned long *state,unsigned char *block);
184 static void md5_encode (unsigned char *dst,unsigned long *src,int len);
185 static void md5_decode (unsigned long *dst,unsigned char *src,int len);
186 #endif
187 /* POP3 mail validate mailbox
188  * Accepts: mailbox name
189  * Returns: our driver if name is valid, NIL otherwise
190  */
191
192 DRIVER *pop3_valid (char *name)
193 {
194   NETMBX mb;
195   return (mail_valid_net_parse (name,&mb) &&
196           !strcmp (mb.service,pop3driver.name) && !mb.authuser[0] &&
197           !compare_cstring (mb.mailbox,"INBOX")) ? &pop3driver : NIL;
198 }
199
200
201 /* News manipulate driver parameters
202  * Accepts: function code
203  *          function-dependent value
204  * Returns: function-dependent return value
205  */
206
207 void *pop3_parameters (long function,void *value)
208 {
209   switch ((int) function) {
210   case SET_MAXLOGINTRIALS:
211     pop3_maxlogintrials = (unsigned long) value;
212     break;
213   case GET_MAXLOGINTRIALS:
214     value = (void *) pop3_maxlogintrials;
215     break;
216   case SET_POP3PORT:
217     pop3_port = (long) value;
218     break;
219   case GET_POP3PORT:
220     value = (void *) pop3_port;
221     break;
222   case SET_SSLPOPPORT:
223     pop3_sslport = (long) value;
224     break;
225   case GET_SSLPOPPORT:
226     value = (void *) pop3_sslport;
227     break;
228   case GET_IDLETIMEOUT:
229     value = (void *) IDLETIMEOUT;
230     break;
231   default:
232     value = NIL;                /* error case */
233     break;
234   }
235   return value;
236 }
237 \f
238 /* POP3 mail scan mailboxes for string
239  * Accepts: mail stream
240  *          reference
241  *          pattern to search
242  *          string to scan
243  */
244
245 void pop3_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
246 {
247   char tmp[MAILTMPLEN];
248   if ((ref && *ref) ?           /* have a reference */
249       (pop3_valid (ref) && pmatch ("INBOX",pat)) :
250       (mail_valid_net (pat,&pop3driver,NIL,tmp) && pmatch ("INBOX",tmp)))
251     mm_log ("Scan not valid for POP3 mailboxes",ERROR);
252 }
253
254
255 /* POP3 mail find list of all mailboxes
256  * Accepts: mail stream
257  *          reference
258  *          pattern to search
259  */
260
261 void pop3_list (MAILSTREAM *stream,char *ref,char *pat)
262 {
263   char tmp[MAILTMPLEN];
264   if (ref && *ref) {            /* have a reference */
265     if (pop3_valid (ref) && pmatch ("INBOX",pat)) {
266       strcpy (strchr (strcpy (tmp,ref),'}')+1,"INBOX");
267       mm_list (stream,NIL,tmp,LATT_NOINFERIORS);
268     }
269   }
270   else if (mail_valid_net (pat,&pop3driver,NIL,tmp) && pmatch ("INBOX",tmp)) {
271     strcpy (strchr (strcpy (tmp,pat),'}')+1,"INBOX");
272     mm_list (stream,NIL,tmp,LATT_NOINFERIORS);
273   }
274 }
275 \f
276 /* POP3 mail find list of subscribed mailboxes
277  * Accepts: mail stream
278  *          reference
279  *          pattern to search
280  */
281
282 void pop3_lsub (MAILSTREAM *stream,char *ref,char *pat)
283 {
284   void *sdb = NIL;
285   char *s,mbx[MAILTMPLEN];
286   if (*pat == '{') {            /* if remote pattern, must be POP3 */
287     if (!pop3_valid (pat)) return;
288     ref = NIL;                  /* good POP3 pattern, punt reference */
289   }
290                                 /* if remote reference, must be valid POP3 */
291   if (ref && (*ref == '{') && !pop3_valid (ref)) return;
292                                 /* kludgy application of reference */
293   if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
294   else strcpy (mbx,pat);
295
296   if (s = sm_read (&sdb)) do if (pop3_valid (s) && pmatch (s,mbx))
297     mm_lsub (stream,NIL,s,NIL);
298   while (s = sm_read (&sdb));   /* until no more subscriptions */
299 }
300
301
302 /* POP3 mail subscribe to mailbox
303  * Accepts: mail stream
304  *          mailbox to add to subscription list
305  * Returns: T on success, NIL on failure
306  */
307
308 long pop3_subscribe (MAILSTREAM *stream,char *mailbox)
309 {
310   return sm_subscribe (mailbox);
311 }
312
313
314 /* POP3 mail unsubscribe to mailbox
315  * Accepts: mail stream
316  *          mailbox to delete from subscription list
317  * Returns: T on success, NIL on failure
318  */
319
320 long pop3_unsubscribe (MAILSTREAM *stream,char *mailbox)
321 {
322   return sm_unsubscribe (mailbox);
323 }
324 \f
325 /* POP3 mail create mailbox
326  * Accepts: mail stream
327  *          mailbox name to create
328  * Returns: T on success, NIL on failure
329  */
330
331 long pop3_create (MAILSTREAM *stream,char *mailbox)
332 {
333   return NIL;                   /* never valid for POP3 */
334 }
335
336
337 /* POP3 mail delete mailbox
338  *          mailbox name to delete
339  * Returns: T on success, NIL on failure
340  */
341
342 long pop3_delete (MAILSTREAM *stream,char *mailbox)
343 {
344   return NIL;                   /* never valid for POP3 */
345 }
346
347
348 /* POP3 mail rename mailbox
349  * Accepts: mail stream
350  *          old mailbox name
351  *          new mailbox name
352  * Returns: T on success, NIL on failure
353  */
354
355 long pop3_rename (MAILSTREAM *stream,char *old,char *newname)
356 {
357   return NIL;                   /* never valid for POP3 */
358 }
359 \f
360 /* POP3 status
361  * Accepts: mail stream
362  *          mailbox name
363  *          status flags
364  * Returns: T on success, NIL on failure
365  */
366
367 long pop3_status (MAILSTREAM *stream,char *mbx,long flags)
368 {
369   MAILSTATUS status;
370   unsigned long i;
371   long ret = NIL;
372   MAILSTREAM *tstream =
373     (stream && LOCAL->netstream && mail_usable_network_stream (stream,mbx)) ?
374       stream : mail_open (NIL,mbx,OP_SILENT);
375   if (tstream) {                /* have a usable stream? */
376     status.flags = flags;       /* return status values */
377     status.messages = tstream->nmsgs;
378     status.recent = tstream->recent;
379     if (flags & SA_UNSEEN)      /* must search to get unseen messages */
380       for (i = 1,status.unseen = 0; i <= tstream->nmsgs; i++)
381         if (!mail_elt (tstream,i)->seen) status.unseen++;
382     status.uidnext = tstream->uid_last + 1;
383     status.uidvalidity = tstream->uid_validity;
384                                 /* pass status to main program */
385     mm_status (tstream,mbx,&status);
386     if (stream != tstream) mail_close (tstream);
387     ret = LONGT;
388   }
389   return ret;                   /* success */
390 }
391 \f
392 /* POP3 mail open
393  * Accepts: stream to open
394  * Returns: stream on success, NIL on failure
395  */
396
397 MAILSTREAM *pop3_open (MAILSTREAM *stream)
398 {
399   unsigned long i,j;
400   char *s,*t,tmp[MAILTMPLEN],usr[MAILTMPLEN], tmp_str[MAILTMPLEN], temp[256];    // APOP - shasikala.p@siso.com
401   NETMBX mb;
402   MESSAGECACHE *elt;
403   char* timestamp = NULL; // APOP - shasikala.p@siso.com
404   int apop = 0;
405   MD5CONTEXT ctx;
406   unsigned char digest[MD5DIGLEN];
407   char *hex = "0123456789abcdef";
408                                 /* return prototype for OP_PROTOTYPE call */
409   if (!stream) return &pop3proto;
410   mail_valid_net_parse (stream->mailbox,&mb);
411   usr[0] = '\0';                /* initially no user name */
412   if (stream->local) fatal ("pop3 recycle stream");
413                                 /* /anonymous not supported */
414   if (mb.anoflag || stream->anonymous) {
415     mm_log ("Anonymous POP3 login not available",ERROR);
416     return NIL;
417   }
418                                 /* /readonly not supported either */
419   if (mb.readonlyflag || stream->rdonly) {
420     mm_log ("Read-only POP3 access not available",ERROR);
421     return NIL;
422   }
423                                 /* copy other switches */
424   if (mb.dbgflag) stream->debug = T;
425   if (mb.secflag) stream->secure = T;
426   mb.trysslflag = stream->tryssl = (mb.trysslflag || stream->tryssl) ? T : NIL;
427   stream->local =               /* instantiate localdata */
428     (void *) memset (fs_get (sizeof (POP3LOCAL)),0,sizeof (POP3LOCAL));
429   stream->sequence++;           /* bump sequence number */
430   stream->perm_deleted = T;     /* deleted is only valid flag */
431 \f
432   if ((LOCAL->netstream =       /* try to open connection */
433        net_open (&mb,NIL,pop3_port ? pop3_port : POP3TCPPORT,
434                  (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL),
435                  "*pop3s",pop3_sslport ? pop3_sslport : POP3SSLPORT)) &&
436       pop3_reply (stream)) {
437     mm_log (LOCAL->reply,NIL);  /* give greeting */
438         #if 1 //APOP - shasikala.p@siso.com
439         strcpy(tmp_str, LOCAL->reply);
440         mm_log(tmp_str, NIL);
441         timestamp = strchr(tmp_str, '<');
442
443         sprintf(temp, "Pop3_open - mb->apop - %d", mb.apop);
444         mm_log(temp, NIL);
445         //EM_DEBUG_LOG("Pop3_open - mb->apop - %d", mb.apop);
446
447 //      printf(" >>>>>>>>>>>>>>>>>>>>>>>> Stream APOP [ %d ]>>>> \n", stream->apop);
448         
449         if(timestamp && mb.apop != 0)
450                 apop = 1;
451         else
452         {
453                 mm_log("server doesnt support apop", NIL);
454                 if(mb.apop == 1)
455                         mm_log("Uncheck apop", NIL);
456                 apop = 0;
457         }
458         
459         //EM_DEBUG_LOG(" >>>>>>>>>>>>>>>>>>>>>>>> Stream APOP [ %d ]>>>> \n", apop);
460                 
461         if(apop)
462         {
463                 mm_log(timestamp, NIL);
464                 mm_login(&mb, usr, tmp, 0);
465                 mm_log(usr, NIL);
466                 mm_log(tmp, NIL);
467                 timestamp = strcat(timestamp, tmp);
468                 mm_log(timestamp, NIL);
469                 memset(tmp, 0, MAILTMPLEN);
470                 md5_init (&ctx);
471                 md5_update (&ctx,(unsigned char *) timestamp,strlen (timestamp));
472                 md5_final (digest,&ctx);
473                 mm_log(digest, NIL);
474                     for (i = 0, s = tmp ; i < MD5DIGLEN; i++) {
475                         *s++ = hex[(j = digest[i]) >> 4];
476                         *s++ = hex[j & 0xf];
477                         }
478                 *s = '\0';
479                 mm_log(tmp, NIL);
480         }
481         
482         #endif
483         if (!pop3_auth (stream,&mb,tmp,usr, apop)) pop3_close (stream,NIL);
484     else if (pop3_send (stream,"STAT",NIL)) {
485       int silent = stream->silent;
486       stream->silent = T;
487       sprintf (tmp,"{%.200s:%lu/pop3",
488                (long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
489                net_host (LOCAL->netstream) : mb.host,
490                net_port (LOCAL->netstream));
491       if (mb.tlsflag) strcat (tmp,"/tls");
492       if (mb.tlssslv23) strcat (tmp,"/tls-sslv23");
493       if (mb.notlsflag) strcat (tmp,"/notls");
494       if (mb.sslflag) strcat (tmp,"/ssl");
495       if (mb.novalidate) strcat (tmp,"/novalidate-cert");
496       if (LOCAL->loser = mb.loser) strcat (tmp,"/loser");
497       if (stream->secure) strcat (tmp,"/secure");
498       sprintf (tmp + strlen (tmp),"/user=\"%s\"}%s",usr,mb.mailbox);
499       stream->inbox = T;        /* always INBOX */
500       fs_give ((void **) &stream->mailbox);
501       stream->mailbox = cpystr (tmp);
502                                 /* notify upper level */
503       mail_exists (stream,stream->uid_last = strtoul (LOCAL->reply,NIL,10));
504       mail_recent (stream,stream->nmsgs);
505                                 /* instantiate elt */
506       for (i = 0; i < stream->nmsgs;) {
507         elt = mail_elt (stream,++i);
508         elt->valid = elt->recent = T;
509         elt->private.uid = i;
510       }
511 \f
512                                 /* trust LIST output if new server */
513       if (!LOCAL->loser && LOCAL->cap.capa && pop3_send (stream,"LIST",NIL)) {
514         while ((s = net_getline (LOCAL->netstream)) && (*s != '.')) {
515           if ((i = strtoul (s,&t,10)) && (i <= stream->nmsgs) &&
516               (j = strtoul (t,NIL,10))) mail_elt (stream,i)->rfc822_size = j;
517           fs_give ((void **) &s);
518         }
519                                 /* flush final dot */
520         if (s) fs_give ((void **) &s);
521         else {                  /* lost connection */
522           mm_log ("POP3 connection broken while itemizing messages",ERROR);
523           pop3_close (stream,NIL);
524           return NIL;
525         }
526       }
527       stream->silent = silent;  /* notify main program */
528       mail_exists (stream,stream->nmsgs);
529                                 /* notify if empty */
530       if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",WARN);
531     }
532     else {                      /* error in STAT */
533       mm_log (LOCAL->reply,ERROR);
534       pop3_close (stream,NIL);  /* too bad */
535     }
536   }
537   else {                        /* connection failed */
538     if (LOCAL->reply) mm_log (LOCAL->reply,ERROR);
539     pop3_close (stream,NIL);    /* failed, clean up */
540   }
541   return LOCAL ? stream : NIL;  /* if stream is alive, return to caller */
542 }
543 \f
544 /* POP3 capabilities
545  * Accepts: stream
546  *          authenticator flags
547  * Returns: T on success, NIL on failure
548  */
549
550 long pop3_capa (MAILSTREAM *stream,long flags)
551 {
552   unsigned long i;
553   char *s,*t,*r,*args;
554   if (LOCAL->cap.implementation)/* zap all old capabilities */
555     fs_give ((void **) &LOCAL->cap.implementation);
556   memset (&LOCAL->cap,0,sizeof (LOCAL->cap));
557                                 /* get server capabilities */
558   if (pop3_send (stream,"CAPA",NIL)) LOCAL->cap.capa = T;
559   else {
560     LOCAL->cap.user = T;        /* guess worst-case old server */
561     LOCAL->cap.top = T; /* 05-Apr-2010 - guess TOP is supported. for hotmail server */
562         LOCAL->cap.capa = T;    
563     return NIL;                 /* no CAPA on this server */
564   }
565   while ((t = net_getline (LOCAL->netstream)) && (t[1] || (*t != '.'))) {
566     if (stream->debug) mm_dlog (t);
567                                 /* get optional capability arguments */
568     if (args = strchr (t,' ')) *args++ = '\0';
569     if (!compare_cstring (t,"STLS")) LOCAL->cap.stls = T;
570     else if (!compare_cstring (t,"PIPELINING")) LOCAL->cap.pipelining = T;
571     else if (!compare_cstring (t,"RESP-CODES")) LOCAL->cap.respcodes = T;
572     else if (!compare_cstring (t,"TOP")) LOCAL->cap.top = T;
573     else if (!compare_cstring (t,"UIDL")) LOCAL->cap.uidl = T;
574     else if (!compare_cstring (t,"USER")) LOCAL->cap.user = T;
575     else if (!compare_cstring (t,"IMPLEMENTATION") && args)
576       LOCAL->cap.implementation = cpystr (args);
577     else if (!compare_cstring (t,"EXPIRE") && args) {
578       LOCAL->cap.expire = T;    /* note that it is present */
579       if (s = strchr(args,' ')){/* separate time from possible USER */
580         *s++ = '\0';
581                                 /* in case they add something after USER */
582         if ((strlen (s) > 4) && (s[4] == ' ')) s[4] = '\0';
583       }
584       LOCAL->cap.expire =       /* get expiration time */
585         (!compare_cstring (args,"NEVER")) ? 65535 :
586           ((s && !compare_cstring (s,"USER")) ? -atoi (args) : atoi (args));
587     }
588     else if (!compare_cstring (t,"LOGIN-DELAY") && args) {
589       LOCAL->cap.logindelay = T;/* note that it is present */
590       if (s = strchr(args,' ')){/* separate time from possible USER */
591         *s++ = '\0';
592                                 /* in case they add something after USER */
593         if ((strlen (s) > 4) && (s[4] == ' ')) s[4] = '\0';
594       }
595                                 /* get delay time */
596       LOCAL->cap.delaysecs = (s && !compare_cstring (s,"USER")) ?
597         -atoi (args) : atoi (args);
598     }
599     else if (!compare_cstring (t,"SASL") && args)
600       for (args = strtok_r (args," ",&r); args; args = strtok_r (NIL," ",&r))
601         if ((i = mail_lookup_auth_name (args,flags)) &&
602             (--i < MAXAUTHENTICATORS))
603           LOCAL->cap.sasl |= (1 << i);
604     fs_give ((void **) &t);
605   }
606   if (t) {                      /* flush end of text indicator */
607     if (stream->debug) mm_dlog (t);
608     fs_give ((void **) &t);
609   }
610   return LONGT;
611 }
612 \f
613 /* POP3 authenticate
614  * Accepts: stream to login
615  *          parsed network mailbox structure
616  *          scratch buffer of length MAILTMPLEN
617  *          place to return user name
618  * Returns: T on success, NIL on failure
619  */
620
621 long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr, int apop)
622 {
623   unsigned long i,trial,auths = 0;
624   char *t;
625   AUTHENTICATOR *at;
626   long ret = NIL;
627   long flags = (stream->secure ? AU_SECURE : NIL) |
628     (mb->authuser[0] ? AU_AUTHUSER : NIL);
629   mm_log("in pop3_auth1", NIL);
630   if(apop)
631   {
632         apop = 0;
633         mm_log("apop set", NIL);
634         char* tmp = (char *)malloc(strlen(usr)+strlen(pwd) +1);
635         sprintf (tmp,"%s %s",usr,pwd);
636         mm_log(tmp, NIL);
637         if(pop3_send(stream, "APOP", tmp))
638         {
639                 mm_log("successfully logged in using APOP", NIL);
640                 pop3_capa (stream,flags);
641                 return LONGT;
642         }
643   }
644   long capaok = pop3_capa (stream,flags);
645   NETDRIVER *ssld = (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL);
646   sslstart_t stls = (sslstart_t) mail_parameters (NIL,GET_SSLSTART,NIL);
647                                 /* server has TLS? */
648   if (stls && LOCAL->cap.stls && !mb->sslflag && !mb->notlsflag &&
649       pop3_send (stream,"STLS",NIL)) {
650     mb->tlsflag = T;            /* TLS OK, get into TLS at this end */
651     LOCAL->netstream->dtb = ssld;
652     if (!(LOCAL->netstream->stream =
653           (*stls) (LOCAL->netstream->stream,mb->host,
654                    (mb->tlssslv23 ? NIL : NET_TLSCLIENT) |
655                    (mb->novalidate ? NET_NOVALIDATECERT : NIL)))) {
656                                 /* drat, drop this connection */
657       if (LOCAL->netstream) net_close (LOCAL->netstream);
658       LOCAL->netstream= NIL;
659       return NIL;               /* TLS negotiation failed */
660     }
661     pop3_capa (stream,flags);   /* get capabilities now that TLS in effect */
662   }
663   else if (mb->tlsflag) {       /* user specified /tls but can't do it */
664     mm_log ("Unable to negotiate TLS with this server",ERROR);
665     return NIL;
666   }
667                                 /* get authenticators from capabilities */
668   if (capaok) auths = LOCAL->cap.sasl;
669                                 /* get list of authenticators */
670   else if (pop3_send (stream,"AUTH",NIL)) {
671     while ((t = net_getline (LOCAL->netstream)) && (t[1] || (*t != '.'))) {
672       if (stream->debug) mm_dlog (t);
673       if ((i = mail_lookup_auth_name (t,flags)) && (--i < MAXAUTHENTICATORS))
674         auths |= (1 << i);
675       fs_give ((void **) &t);
676     }
677     if (t) {                    /* flush end of text indicator */
678       if (stream->debug) mm_dlog (t);
679       fs_give ((void **) &t);
680     }
681   }
682                                 /* disable LOGIN if PLAIN also advertised */
683   if ((i = mail_lookup_auth_name ("PLAIN",NIL)) && (--i < MAXAUTHENTICATORS) &&
684       (auths & (1 << i)) &&
685       (i = mail_lookup_auth_name ("LOGIN",NIL)) && (--i < MAXAUTHENTICATORS))
686     auths &= ~(1 << i);
687 \f
688   if (auths) {                  /* got any authenticators? */
689     if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
690                                 /* remote name for authentication */
691       strncpy (mb->host,(long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
692                net_remotehost (LOCAL->netstream) : net_host (LOCAL->netstream),
693                NETMAXHOST-1);
694       mb->host[NETMAXHOST-1] = '\0';
695     }
696     for (t = NIL, LOCAL->saslcancel = NIL; !ret && LOCAL->netstream && auths &&
697          (at = mail_lookup_auth (find_rightmost_bit (&auths)+1)); ) {
698       if (t) {                  /* previous authenticator failed? */
699         sprintf (pwd,"Retrying using %.80s authentication after %.80s",
700                  at->name,t);
701         mm_log (pwd,NIL);
702         fs_give ((void **) &t);
703       }
704       trial = 0;                /* initial trial count */
705       do {
706         if (t) {
707           sprintf (pwd,"Retrying %s authentication after %.80s",at->name,t);
708           mm_log (pwd,WARN);
709           fs_give ((void **) &t);
710         }
711         LOCAL->saslcancel = NIL;
712         if (pop3_send (stream,"AUTH",at->name)) {
713                                 /* hide client authentication responses */
714           if (!(at->flags & AU_SECURE)) LOCAL->sensitive = T;
715           if ((*at->client) (pop3_challenge,pop3_response,"pop",mb,stream,
716                              &trial,usr) && LOCAL->response) {
717             if (*LOCAL->response == '+') ret = LONGT;
718                                 /* if main program requested cancellation */
719             else if (!trial) mm_log ("POP3 Authentication cancelled",ERROR);
720           }
721           LOCAL->sensitive=NIL; /* unhide */
722         }
723                                 /* remember response if error and no cancel */
724         if (!ret && trial) t = cpystr (LOCAL->reply);
725       } while (!ret && trial && (trial < pop3_maxlogintrials) &&
726                LOCAL->netstream);
727     }
728     if (t) {                    /* previous authenticator failed? */
729       if (!LOCAL->saslcancel) { /* don't do this if a cancel */
730         sprintf (pwd,"Can not authenticate to POP3 server: %.80s",t);
731         mm_log (pwd,ERROR);
732       }
733       fs_give ((void **) &t);
734     }
735   }
736 \f
737   else if (stream->secure)
738     mm_log ("Can't do secure authentication with this server",ERROR);
739   else if (mb->authuser[0])
740     mm_log ("Can't do /authuser with this server",ERROR);
741   else if (!LOCAL->cap.user) mm_log ("Can't login to this server",ERROR);
742   else {                        /* traditional login */
743     trial = 0;                  /* initial trial count */
744     do {
745       pwd[0] = 0;               /* prompt user for password */
746       mm_login (mb,usr,pwd,trial++);
747       if (pwd[0]) {             /* send login sequence if have password */
748         if (pop3_send (stream,"USER",usr)) {
749           LOCAL->sensitive = T; /* hide this command */
750           if (pop3_send (stream,"PASS",pwd)) ret = LONGT;
751           LOCAL->sensitive=NIL; /* unhide */
752         }
753         if (!ret) {             /* failure */
754           mm_log (LOCAL->reply,WARN);
755           if (trial == pop3_maxlogintrials)
756             mm_log ("Too many login failures",ERROR);
757         }
758       }
759                                 /* user refused to give password */
760       else mm_log ("Login aborted",ERROR);
761     } while (!ret && pwd[0] && (trial < pop3_maxlogintrials) &&
762              LOCAL->netstream);
763   }
764   memset (pwd,0,MAILTMPLEN);    /* erase password */
765                                 /* get capabilities if logged in */
766   if (ret && capaok) pop3_capa (stream,flags);
767   return ret;
768 }
769 \f
770 /* Get challenge to authenticator in binary
771  * Accepts: stream
772  *          pointer to returned size
773  * Returns: challenge or NIL if not challenge
774  */
775
776 void *pop3_challenge (void *s,unsigned long *len)
777 {
778   char tmp[MAILTMPLEN];
779   void *ret = NIL;
780   MAILSTREAM *stream = (MAILSTREAM *) s;
781   if (stream && LOCAL->response &&
782       (*LOCAL->response == '+') && (LOCAL->response[1] == ' ') &&
783       !(ret = rfc822_base64 ((unsigned char *) LOCAL->reply,
784                              strlen (LOCAL->reply),len))) {
785     sprintf (tmp,"POP3 SERVER BUG (invalid challenge): %.80s",LOCAL->reply);
786     mm_log (tmp,ERROR);
787   }
788   return ret;
789 }
790
791
792 /* Send authenticator response in BASE64
793  * Accepts: MAIL stream
794  *          string to send
795  *          length of string
796  * Returns: T if successful, else NIL
797  */
798
799 long pop3_response (void *s,char *response,unsigned long size)
800 {
801   MAILSTREAM *stream = (MAILSTREAM *) s;
802   unsigned long i,j,ret;
803   char *t,*u;
804   if (response) {               /* make CRLFless BASE64 string */
805     if (size) {
806       for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
807            j < i; j++) if (t[j] > ' ') *u++ = t[j];
808       *u = '\0';                /* tie off string for mm_dlog() */
809       if (stream->debug) mail_dlog (t,LOCAL->sensitive);
810                                 /* append CRLF */
811       *u++ = '\015'; *u++ = '\012'; *u = '\0';
812       ret = net_sout (LOCAL->netstream,t,u - t);
813       fs_give ((void **) &t);
814     }
815     else ret = net_sout (LOCAL->netstream,"\015\012",2);
816   }
817   else {                        /* abort requested */
818     ret = net_sout (LOCAL->netstream,"*\015\012",3);
819     LOCAL->saslcancel = T;      /* mark protocol-requested SASL cancel */
820   }
821   pop3_reply (stream);          /* get response */
822   return ret;
823 }
824 \f
825 /* POP3 mail close
826  * Accepts: MAIL stream
827  *          option flags
828  */
829
830 void pop3_close (MAILSTREAM *stream,long options)
831 {
832   int silent = stream->silent;
833   if (LOCAL) {                  /* only if a file is open */
834     if (LOCAL->netstream) {     /* close POP3 connection */
835       stream->silent = T;
836       if (options & CL_EXPUNGE) pop3_expunge (stream,NIL,NIL);
837       stream->silent = silent;
838       pop3_send (stream,"QUIT",NIL);
839       mm_notify (stream,LOCAL->reply,BYE);
840     }
841                                 /* close POP3 connection */
842     if (LOCAL->netstream) net_close (LOCAL->netstream);
843                                 /* clean up */
844     if (LOCAL->cap.implementation)
845       fs_give ((void **) &LOCAL->cap.implementation);
846     if (LOCAL->txt) fclose (LOCAL->txt);
847     LOCAL->txt = NIL;
848     if (LOCAL->response) fs_give ((void **) &LOCAL->response);
849                                 /* nuke the local data */
850     fs_give ((void **) &stream->local);
851     stream->dtb = NIL;          /* log out the DTB */
852   }
853 }
854 \f
855 /* POP3 mail fetch fast information
856  * Accepts: MAIL stream
857  *          sequence
858  *          option flags
859  * This is ugly and slow
860  */
861
862 void pop3_fetchfast (MAILSTREAM *stream,char *sequence,long flags)
863 {
864   unsigned long i;
865   MESSAGECACHE *elt;
866                                 /* get sequence */
867   if (stream && LOCAL && ((flags & FT_UID) ?
868                           mail_uid_sequence (stream,sequence) :
869                           mail_sequence (stream,sequence)))
870     for (i = 1; i <= stream->nmsgs; i++)
871       if ((elt = mail_elt (stream,i))->sequence &&
872           !(elt->day && elt->rfc822_size)) {
873         ENVELOPE **env = NIL;
874         ENVELOPE *e = NIL;
875         if (!stream->scache) env = &elt->private.msg.env;
876         else if (stream->msgno == i) env = &stream->env;
877         else env = &e;
878         if (!*env || !elt->rfc822_size) {
879           STRING bs;
880           unsigned long hs;
881           char *ht = (*stream->dtb->header) (stream,i,&hs,NIL);
882                                 /* need to make an envelope? */
883           if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST,
884                                        stream->dtb->flags);
885                                 /* need message size too, ugh */
886           if (!elt->rfc822_size) {
887             (*stream->dtb->text) (stream,i,&bs,FT_PEEK);
888             elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs);
889           }
890         }
891                                 /* if need date, have date in envelope? */
892         if (!elt->day && *env && (*env)->date)
893           mail_parse_date (elt,(*env)->date);
894                                 /* sigh, fill in bogus default */
895         if (!elt->day) elt->day = elt->month = 1;
896         mail_free_envelope (&e);
897       }
898 }
899 \f
900 /* POP3 fetch header as text
901  * Accepts: mail stream
902  *          message number
903  *          pointer to return size
904  *          flags
905  * Returns: header text
906  */
907
908 char *pop3_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
909                    long flags)
910 {
911   unsigned long i;
912   char tmp[MAILTMPLEN];
913   MESSAGECACHE *elt;
914   FILE *f = NIL;
915   *size = 0;                    /* initially no header size */
916   if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return "";
917                                 /* have header text already? */
918   if (!(elt = mail_elt (stream,msgno))->private.msg.header.text.data) {
919                                 /* if have CAPA and TOP, assume good TOP */
920     if (!LOCAL->loser && LOCAL->cap.top) {
921       sprintf (tmp,"TOP %lu 0",mail_uid (stream,msgno));
922       if (pop3_send (stream,tmp,NIL))
923         f = netmsg_slurp (LOCAL->netstream,&i,
924                           &elt->private.msg.header.text.size);
925     }
926                                 /* otherwise load the cache with the message */
927     else if (elt->private.msg.header.text.size = pop3_cache (stream,elt))
928       f = LOCAL->txt;
929     if (f) {                    /* got it, make sure at start of file */
930       fseek (f,(unsigned long) 0,SEEK_SET);
931                                 /* read header from the cache */
932       fread (elt->private.msg.header.text.data = (unsigned char *)
933              fs_get ((size_t) elt->private.msg.header.text.size + 1),
934              (size_t) 1,(size_t) elt->private.msg.header.text.size,f);
935                                 /* tie off header text */
936       elt->private.msg.header.text.data[elt->private.msg.header.text.size] =
937         '\0';
938                                 /* close if not the cache */
939       if (f != LOCAL->txt) fclose (f);
940     }
941   }
942                                 /* return size of text */
943   if (size) *size = elt->private.msg.header.text.size;
944   return elt->private.msg.header.text.data ?
945     (char *) elt->private.msg.header.text.data : "";
946 }
947 \f
948 /* POP3 fetch body
949  * Accepts: mail stream
950  *          message number
951  *          pointer to stringstruct to initialize
952  *          flags
953  * Returns: T if successful, else NIL
954  */
955
956 long pop3_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
957 {
958   MESSAGECACHE *elt;
959   INIT (bs,mail_string,(void *) "",0);
960   if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return NIL;
961   elt = mail_elt (stream,msgno);
962   pop3_cache (stream,elt);      /* make sure cache loaded */
963   if (!LOCAL->txt) return NIL;  /* error if don't have a file */
964   if (!(flags & FT_PEEK)) {     /* mark seen if needed */
965     elt->seen = T;
966     mm_flags (stream,elt->msgno);
967   }
968   INIT (bs,file_string,(void *) LOCAL->txt,elt->rfc822_size);
969   SETPOS (bs,LOCAL->hdrsize);   /* skip past header */
970   return T;
971 }
972 \f
973 /* POP3 cache message
974  * Accepts: mail stream
975  *          message number
976  * Returns: header size
977  */
978
979 unsigned long pop3_cache (MAILSTREAM *stream,MESSAGECACHE *elt)
980 {
981                                 /* already cached? */
982   if (LOCAL->cached != mail_uid (stream,elt->msgno)) {
983                                 /* no, close current file */
984     if (LOCAL->txt) fclose (LOCAL->txt);
985     LOCAL->txt = NIL;
986     LOCAL->cached = LOCAL->hdrsize = 0;
987     if (pop3_send_num (stream,"RETR",elt->msgno) &&
988         (LOCAL->txt = netmsg_slurp (LOCAL->netstream,&elt->rfc822_size,
989                                     &LOCAL->hdrsize)))
990                                 /* set as current message number */
991       LOCAL->cached = mail_uid (stream,elt->msgno);
992     else elt->deleted = T;
993   }
994   return LOCAL->hdrsize;
995 }
996 \f
997 /* POP3 mail ping mailbox
998  * Accepts: MAIL stream
999  * Returns: T if stream alive, else NIL
1000  */
1001
1002 long pop3_ping (MAILSTREAM *stream)
1003 {
1004   return pop3_send (stream,"NOOP",NIL);
1005 }
1006
1007
1008 /* POP3 mail check mailbox
1009  * Accepts: MAIL stream
1010  */
1011
1012 void pop3_check (MAILSTREAM *stream)
1013 {
1014   if (pop3_ping (stream)) mm_log ("Check completed",NIL);
1015 }
1016
1017
1018 /* POP3 mail expunge mailbox
1019  * Accepts: MAIL stream
1020  *          sequence to expunge if non-NIL
1021  *          expunge options
1022  * Returns: T if success, NIL if failure
1023  */
1024
1025 long pop3_expunge (MAILSTREAM *stream,char *sequence,long options)
1026 {
1027   char tmp[MAILTMPLEN];
1028   MESSAGECACHE *elt;
1029   unsigned long i = 1,n = 0;
1030   long ret;
1031   if (ret = sequence ? ((options & EX_UID) ?
1032                         mail_uid_sequence (stream,sequence) :
1033                         mail_sequence (stream,sequence)) :
1034       LONGT) {                  /* build selected sequence if needed */
1035     while (i <= stream->nmsgs) {
1036       elt = mail_elt (stream,i);
1037       if (elt->deleted && (sequence ? elt->sequence : T) &&
1038           pop3_send_num (stream,"DELE",i)) {
1039                                 /* expunging currently cached message? */
1040         if (LOCAL->cached == mail_uid (stream,i)) {
1041                                 /* yes, close current file */
1042           if (LOCAL->txt) fclose (LOCAL->txt);
1043           LOCAL->txt = NIL;
1044           LOCAL->cached = LOCAL->hdrsize = 0;
1045         }
1046         mail_expunged (stream,i);
1047         n++;
1048       }
1049       else i++;                 /* try next message */
1050     }
1051     if (!stream->silent) {      /* only if not silent */
1052       if (n) {                  /* did we expunge anything? */
1053         sprintf (tmp,"Expunged %lu messages",n);
1054         mm_log (tmp,(long) NIL);
1055       }
1056       else mm_log ("No messages deleted, so no update needed",(long) NIL);
1057     }
1058   }
1059   return ret;
1060 }
1061 \f
1062 /* POP3 mail copy message(s)
1063  * Accepts: MAIL stream
1064  *          sequence
1065  *          destination mailbox
1066  *          option flags
1067  * Returns: T if copy successful, else NIL
1068  */
1069
1070 long pop3_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
1071 {
1072   mailproxycopy_t pc =
1073     (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
1074   if (pc) return (*pc) (stream,sequence,mailbox,options);
1075   mm_log ("Copy not valid for POP3",ERROR);
1076   return NIL;
1077 }
1078
1079
1080 /* POP3 mail append message from stringstruct
1081  * Accepts: MAIL stream
1082  *          destination mailbox
1083  *          append callback
1084  *          data for callback
1085  * Returns: T if append successful, else NIL
1086  */
1087
1088 long pop3_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
1089 {
1090   mm_log ("Append not valid for POP3",ERROR);
1091   return NIL;
1092 }
1093 \f
1094 /* Internal routines */
1095
1096
1097 /* Post Office Protocol 3 send command with number argument
1098  * Accepts: MAIL stream
1099  *          command
1100  *          number
1101  * Returns: T if successful, NIL if failure
1102  */
1103
1104 long pop3_send_num (MAILSTREAM *stream,char *command,unsigned long n)
1105 {
1106   char tmp[MAILTMPLEN];
1107   sprintf (tmp,"%lu",mail_uid (stream,n));
1108   return pop3_send (stream,command,tmp);
1109 }
1110
1111
1112 /* Post Office Protocol 3 send command
1113  * Accepts: MAIL stream
1114  *          command
1115  *          command argument
1116  * Returns: T if successful, NIL if failure
1117  */
1118
1119 long pop3_send (MAILSTREAM *stream,char *command,char *args)
1120 {
1121   long ret;
1122   char *s = (char *) fs_get (strlen (command) + (args ? strlen (args) + 1: 0)
1123                              + 3);
1124   mail_lock (stream);           /* lock up the stream */
1125   if (!LOCAL->netstream) ret = pop3_fake (stream,"POP3 connection lost");
1126   else {                        /* build the complete command */
1127     if (args) sprintf (s,"%s %s",command,args);
1128     else strcpy (s,command);
1129     if (stream->debug) mail_dlog (s,LOCAL->sensitive);
1130     strcat (s,"\015\012");
1131                                 /* send the command */
1132     ret = net_soutr (LOCAL->netstream,s) ? pop3_reply (stream) :
1133       pop3_fake (stream,"POP3 connection broken in command");
1134   }
1135   fs_give ((void **) &s);
1136   mail_unlock (stream);         /* unlock stream */
1137   return ret;
1138 }
1139 \f
1140 /* Post Office Protocol 3 get reply
1141  * Accepts: MAIL stream
1142  * Returns: T if success reply, NIL if error reply
1143  */
1144
1145 long pop3_reply (MAILSTREAM *stream)
1146 {
1147   char *s;
1148                                 /* flush old reply */
1149   if (LOCAL->response) fs_give ((void **) &LOCAL->response);
1150                                 /* get reply */
1151   if (!(LOCAL->response = net_getline (LOCAL->netstream)))
1152     return pop3_fake (stream,"POP3 connection broken in response");
1153   if (stream->debug) mm_dlog (LOCAL->response);
1154   LOCAL->reply = (s = strchr (LOCAL->response,' ')) ? s + 1 : LOCAL->response;
1155                                 /* return success or failure */
1156   return (*LOCAL->response =='+') ? T : NIL;
1157 }
1158
1159
1160 /* Post Office Protocol 3 set fake error
1161  * Accepts: MAIL stream
1162  *          error text
1163  * Returns: NIL, always
1164  */
1165
1166 long pop3_fake (MAILSTREAM *stream,char *text)
1167 {
1168   mm_notify (stream,text,BYE);  /* send bye alert */
1169   if (LOCAL->netstream) net_close (LOCAL->netstream);
1170   LOCAL->netstream = NIL;       /* farewell, dear TCP stream */
1171                                 /* flush any old reply */
1172   if (LOCAL->response) fs_give ((void **) &LOCAL->response);
1173   LOCAL->reply = text;          /* set up pseudo-reply string */
1174   return NIL;                   /* return error code */
1175 }