imapext-2007

view src/c-client/pop3.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
line source
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 */
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 */
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"
37 /* Parameters */
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 */
44 /* POP3 I/O stream local data */
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;
75 /* Convenient access to local data */
77 #define LOCAL ((POP3LOCAL *) stream->local)
79 /* Function prototypes */
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);
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);
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);
114 /* POP3 mail routines */
117 /* Driver dispatch used by MAIL */
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 };
162 /* prototype stream */
163 MAILSTREAM pop3proto = {&pop3driver};
165 /* driver parameters */
166 static unsigned long pop3_maxlogintrials = MAXLOGINTRIALS;
167 static long pop3_port = 0;
168 static long pop3_sslport = 0;
170 /* POP3 mail validate mailbox
171 * Accepts: mailbox name
172 * Returns: our driver if name is valid, NIL otherwise
173 */
175 DRIVER *pop3_valid (char *name)
176 {
177 NETMBX mb;
178 return (mail_valid_net_parse (name,&mb) &&
179 !strcmp (mb.service,pop3driver.name) && !mb.authuser[0] &&
180 !compare_cstring (mb.mailbox,"INBOX")) ? &pop3driver : NIL;
181 }
184 /* News manipulate driver parameters
185 * Accepts: function code
186 * function-dependent value
187 * Returns: function-dependent return value
188 */
190 void *pop3_parameters (long function,void *value)
191 {
192 switch ((int) function) {
193 case SET_MAXLOGINTRIALS:
194 pop3_maxlogintrials = (unsigned long) value;
195 break;
196 case GET_MAXLOGINTRIALS:
197 value = (void *) pop3_maxlogintrials;
198 break;
199 case SET_POP3PORT:
200 pop3_port = (long) value;
201 break;
202 case GET_POP3PORT:
203 value = (void *) pop3_port;
204 break;
205 case SET_SSLPOPPORT:
206 pop3_sslport = (long) value;
207 break;
208 case GET_SSLPOPPORT:
209 value = (void *) pop3_sslport;
210 break;
211 case GET_IDLETIMEOUT:
212 value = (void *) IDLETIMEOUT;
213 break;
214 default:
215 value = NIL; /* error case */
216 break;
217 }
218 return value;
219 }
221 /* POP3 mail scan mailboxes for string
222 * Accepts: mail stream
223 * reference
224 * pattern to search
225 * string to scan
226 */
228 void pop3_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
229 {
230 char tmp[MAILTMPLEN];
231 if ((ref && *ref) ? /* have a reference */
232 (pop3_valid (ref) && pmatch ("INBOX",pat)) :
233 (mail_valid_net (pat,&pop3driver,NIL,tmp) && pmatch ("INBOX",tmp)))
234 mm_log ("Scan not valid for POP3 mailboxes",ERROR);
235 }
238 /* POP3 mail find list of all mailboxes
239 * Accepts: mail stream
240 * reference
241 * pattern to search
242 */
244 void pop3_list (MAILSTREAM *stream,char *ref,char *pat)
245 {
246 char tmp[MAILTMPLEN];
247 if (ref && *ref) { /* have a reference */
248 if (pop3_valid (ref) && pmatch ("INBOX",pat)) {
249 strcpy (strchr (strcpy (tmp,ref),'}')+1,"INBOX");
250 mm_list (stream,NIL,tmp,LATT_NOINFERIORS);
251 }
252 }
253 else if (mail_valid_net (pat,&pop3driver,NIL,tmp) && pmatch ("INBOX",tmp)) {
254 strcpy (strchr (strcpy (tmp,pat),'}')+1,"INBOX");
255 mm_list (stream,NIL,tmp,LATT_NOINFERIORS);
256 }
257 }
259 /* POP3 mail find list of subscribed mailboxes
260 * Accepts: mail stream
261 * reference
262 * pattern to search
263 */
265 void pop3_lsub (MAILSTREAM *stream,char *ref,char *pat)
266 {
267 void *sdb = NIL;
268 char *s,mbx[MAILTMPLEN];
269 if (*pat == '{') { /* if remote pattern, must be POP3 */
270 if (!pop3_valid (pat)) return;
271 ref = NIL; /* good POP3 pattern, punt reference */
272 }
273 /* if remote reference, must be valid POP3 */
274 if (ref && (*ref == '{') && !pop3_valid (ref)) return;
275 /* kludgy application of reference */
276 if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
277 else strcpy (mbx,pat);
279 if (s = sm_read (&sdb)) do if (pop3_valid (s) && pmatch (s,mbx))
280 mm_lsub (stream,NIL,s,NIL);
281 while (s = sm_read (&sdb)); /* until no more subscriptions */
282 }
285 /* POP3 mail subscribe to mailbox
286 * Accepts: mail stream
287 * mailbox to add to subscription list
288 * Returns: T on success, NIL on failure
289 */
291 long pop3_subscribe (MAILSTREAM *stream,char *mailbox)
292 {
293 return sm_subscribe (mailbox);
294 }
297 /* POP3 mail unsubscribe to mailbox
298 * Accepts: mail stream
299 * mailbox to delete from subscription list
300 * Returns: T on success, NIL on failure
301 */
303 long pop3_unsubscribe (MAILSTREAM *stream,char *mailbox)
304 {
305 return sm_unsubscribe (mailbox);
306 }
308 /* POP3 mail create mailbox
309 * Accepts: mail stream
310 * mailbox name to create
311 * Returns: T on success, NIL on failure
312 */
314 long pop3_create (MAILSTREAM *stream,char *mailbox)
315 {
316 return NIL; /* never valid for POP3 */
317 }
320 /* POP3 mail delete mailbox
321 * mailbox name to delete
322 * Returns: T on success, NIL on failure
323 */
325 long pop3_delete (MAILSTREAM *stream,char *mailbox)
326 {
327 return NIL; /* never valid for POP3 */
328 }
331 /* POP3 mail rename mailbox
332 * Accepts: mail stream
333 * old mailbox name
334 * new mailbox name
335 * Returns: T on success, NIL on failure
336 */
338 long pop3_rename (MAILSTREAM *stream,char *old,char *newname)
339 {
340 return NIL; /* never valid for POP3 */
341 }
343 /* POP3 status
344 * Accepts: mail stream
345 * mailbox name
346 * status flags
347 * Returns: T on success, NIL on failure
348 */
350 long pop3_status (MAILSTREAM *stream,char *mbx,long flags)
351 {
352 MAILSTATUS status;
353 unsigned long i;
354 long ret = NIL;
355 MAILSTREAM *tstream =
356 (stream && LOCAL->netstream && mail_usable_network_stream (stream,mbx)) ?
357 stream : mail_open (NIL,mbx,OP_SILENT);
358 if (tstream) { /* have a usable stream? */
359 status.flags = flags; /* return status values */
360 status.messages = tstream->nmsgs;
361 status.recent = tstream->recent;
362 if (flags & SA_UNSEEN) /* must search to get unseen messages */
363 for (i = 1,status.unseen = 0; i <= tstream->nmsgs; i++)
364 if (!mail_elt (tstream,i)->seen) status.unseen++;
365 status.uidnext = tstream->uid_last + 1;
366 status.uidvalidity = tstream->uid_validity;
367 /* pass status to main program */
368 mm_status (tstream,mbx,&status);
369 if (stream != tstream) mail_close (tstream);
370 ret = LONGT;
371 }
372 return ret; /* success */
373 }
375 /* POP3 mail open
376 * Accepts: stream to open
377 * Returns: stream on success, NIL on failure
378 */
380 MAILSTREAM *pop3_open (MAILSTREAM *stream)
381 {
382 unsigned long i,j;
383 char *s,*t,tmp[MAILTMPLEN],usr[MAILTMPLEN];
384 NETMBX mb;
385 MESSAGECACHE *elt;
386 /* return prototype for OP_PROTOTYPE call */
387 if (!stream) return &pop3proto;
388 mail_valid_net_parse (stream->mailbox,&mb);
389 usr[0] = '\0'; /* initially no user name */
390 if (stream->local) fatal ("pop3 recycle stream");
391 /* /anonymous not supported */
392 if (mb.anoflag || stream->anonymous) {
393 mm_log ("Anonymous POP3 login not available",ERROR);
394 return NIL;
395 }
396 /* /readonly not supported either */
397 if (mb.readonlyflag || stream->rdonly) {
398 mm_log ("Read-only POP3 access not available",ERROR);
399 return NIL;
400 }
401 /* copy other switches */
402 if (mb.dbgflag) stream->debug = T;
403 if (mb.secflag) stream->secure = T;
404 mb.trysslflag = stream->tryssl = (mb.trysslflag || stream->tryssl) ? T : NIL;
405 stream->local = /* instantiate localdata */
406 (void *) memset (fs_get (sizeof (POP3LOCAL)),0,sizeof (POP3LOCAL));
407 stream->sequence++; /* bump sequence number */
408 stream->perm_deleted = T; /* deleted is only valid flag */
410 if ((LOCAL->netstream = /* try to open connection */
411 net_open (&mb,NIL,pop3_port ? pop3_port : POP3TCPPORT,
412 (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL),
413 "*pop3s",pop3_sslport ? pop3_sslport : POP3SSLPORT)) &&
414 pop3_reply (stream)) {
415 mm_log (LOCAL->reply,NIL); /* give greeting */
416 if (!pop3_auth (stream,&mb,tmp,usr)) pop3_close (stream,NIL);
417 else if (pop3_send (stream,"STAT",NIL)) {
418 int silent = stream->silent;
419 stream->silent = T;
420 sprintf (tmp,"{%.200s:%lu/pop3",
421 (long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
422 net_host (LOCAL->netstream) : mb.host,
423 net_port (LOCAL->netstream));
424 if (mb.tlsflag) strcat (tmp,"/tls");
425 if (mb.tlssslv23) strcat (tmp,"/tls-sslv23");
426 if (mb.notlsflag) strcat (tmp,"/notls");
427 if (mb.sslflag) strcat (tmp,"/ssl");
428 if (mb.novalidate) strcat (tmp,"/novalidate-cert");
429 if (LOCAL->loser = mb.loser) strcat (tmp,"/loser");
430 if (stream->secure) strcat (tmp,"/secure");
431 sprintf (tmp + strlen (tmp),"/user=\"%s\"}%s",usr,mb.mailbox);
432 stream->inbox = T; /* always INBOX */
433 fs_give ((void **) &stream->mailbox);
434 stream->mailbox = cpystr (tmp);
435 /* notify upper level */
436 mail_exists (stream,stream->uid_last = strtoul (LOCAL->reply,NIL,10));
437 mail_recent (stream,stream->nmsgs);
438 /* instantiate elt */
439 for (i = 0; i < stream->nmsgs;) {
440 elt = mail_elt (stream,++i);
441 elt->valid = elt->recent = T;
442 elt->private.uid = i;
443 }
445 /* trust LIST output if new server */
446 if (!LOCAL->loser && LOCAL->cap.capa && pop3_send (stream,"LIST",NIL)) {
447 while ((s = net_getline (LOCAL->netstream)) && (*s != '.')) {
448 if ((i = strtoul (s,&t,10)) && (i <= stream->nmsgs) &&
449 (j = strtoul (t,NIL,10))) mail_elt (stream,i)->rfc822_size = j;
450 fs_give ((void **) &s);
451 }
452 /* flush final dot */
453 if (s) fs_give ((void **) &s);
454 else { /* lost connection */
455 mm_log ("POP3 connection broken while itemizing messages",ERROR);
456 pop3_close (stream,NIL);
457 return NIL;
458 }
459 }
460 stream->silent = silent; /* notify main program */
461 mail_exists (stream,stream->nmsgs);
462 /* notify if empty */
463 if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",WARN);
464 }
465 else { /* error in STAT */
466 mm_log (LOCAL->reply,ERROR);
467 pop3_close (stream,NIL); /* too bad */
468 }
469 }
470 else { /* connection failed */
471 if (LOCAL->reply) mm_log (LOCAL->reply,ERROR);
472 pop3_close (stream,NIL); /* failed, clean up */
473 }
474 return LOCAL ? stream : NIL; /* if stream is alive, return to caller */
475 }
477 /* POP3 capabilities
478 * Accepts: stream
479 * authenticator flags
480 * Returns: T on success, NIL on failure
481 */
483 long pop3_capa (MAILSTREAM *stream,long flags)
484 {
485 unsigned long i;
486 char *s,*t,*r,*args;
487 if (LOCAL->cap.implementation)/* zap all old capabilities */
488 fs_give ((void **) &LOCAL->cap.implementation);
489 memset (&LOCAL->cap,0,sizeof (LOCAL->cap));
490 /* get server capabilities */
491 if (pop3_send (stream,"CAPA",NIL)) LOCAL->cap.capa = T;
492 else {
493 LOCAL->cap.user = T; /* guess worst-case old server */
494 return NIL; /* no CAPA on this server */
495 }
496 while ((t = net_getline (LOCAL->netstream)) && (t[1] || (*t != '.'))) {
497 if (stream->debug) mm_dlog (t);
498 /* get optional capability arguments */
499 if (args = strchr (t,' ')) *args++ = '\0';
500 if (!compare_cstring (t,"STLS")) LOCAL->cap.stls = T;
501 else if (!compare_cstring (t,"PIPELINING")) LOCAL->cap.pipelining = T;
502 else if (!compare_cstring (t,"RESP-CODES")) LOCAL->cap.respcodes = T;
503 else if (!compare_cstring (t,"TOP")) LOCAL->cap.top = T;
504 else if (!compare_cstring (t,"UIDL")) LOCAL->cap.uidl = T;
505 else if (!compare_cstring (t,"USER")) LOCAL->cap.user = T;
506 else if (!compare_cstring (t,"IMPLEMENTATION") && args)
507 LOCAL->cap.implementation = cpystr (args);
508 else if (!compare_cstring (t,"EXPIRE") && args) {
509 LOCAL->cap.expire = T; /* note that it is present */
510 if (s = strchr(args,' ')){/* separate time from possible USER */
511 *s++ = '\0';
512 /* in case they add something after USER */
513 if ((strlen (s) > 4) && (s[4] == ' ')) s[4] = '\0';
514 }
515 LOCAL->cap.expire = /* get expiration time */
516 (!compare_cstring (args,"NEVER")) ? 65535 :
517 ((s && !compare_cstring (s,"USER")) ? -atoi (args) : atoi (args));
518 }
519 else if (!compare_cstring (t,"LOGIN-DELAY") && args) {
520 LOCAL->cap.logindelay = T;/* note that it is present */
521 if (s = strchr(args,' ')){/* separate time from possible USER */
522 *s++ = '\0';
523 /* in case they add something after USER */
524 if ((strlen (s) > 4) && (s[4] == ' ')) s[4] = '\0';
525 }
526 /* get delay time */
527 LOCAL->cap.delaysecs = (s && !compare_cstring (s,"USER")) ?
528 -atoi (args) : atoi (args);
529 }
530 else if (!compare_cstring (t,"SASL") && args)
531 for (args = strtok_r (args," ",&r); args; args = strtok_r (NIL," ",&r))
532 if ((i = mail_lookup_auth_name (args,flags)) &&
533 (--i < MAXAUTHENTICATORS))
534 LOCAL->cap.sasl |= (1 << i);
535 fs_give ((void **) &t);
536 }
537 if (t) { /* flush end of text indicator */
538 if (stream->debug) mm_dlog (t);
539 fs_give ((void **) &t);
540 }
541 return LONGT;
542 }
544 /* POP3 authenticate
545 * Accepts: stream to login
546 * parsed network mailbox structure
547 * scratch buffer of length MAILTMPLEN
548 * place to return user name
549 * Returns: T on success, NIL on failure
550 */
552 long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr)
553 {
554 unsigned long i,trial,auths = 0;
555 char *t;
556 AUTHENTICATOR *at;
557 long ret = NIL;
558 long flags = (stream->secure ? AU_SECURE : NIL) |
559 (mb->authuser[0] ? AU_AUTHUSER : NIL);
560 long capaok = pop3_capa (stream,flags);
561 NETDRIVER *ssld = (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL);
562 sslstart_t stls = (sslstart_t) mail_parameters (NIL,GET_SSLSTART,NIL);
563 /* server has TLS? */
564 if (stls && LOCAL->cap.stls && !mb->sslflag && !mb->notlsflag &&
565 pop3_send (stream,"STLS",NIL)) {
566 mb->tlsflag = T; /* TLS OK, get into TLS at this end */
567 LOCAL->netstream->dtb = ssld;
568 if (!(LOCAL->netstream->stream =
569 (*stls) (LOCAL->netstream->stream,mb->host,
570 (mb->tlssslv23 ? NIL : NET_TLSCLIENT) |
571 (mb->novalidate ? NET_NOVALIDATECERT : NIL)))) {
572 /* drat, drop this connection */
573 if (LOCAL->netstream) net_close (LOCAL->netstream);
574 LOCAL->netstream= NIL;
575 return NIL; /* TLS negotiation failed */
576 }
577 pop3_capa (stream,flags); /* get capabilities now that TLS in effect */
578 }
579 else if (mb->tlsflag) { /* user specified /tls but can't do it */
580 mm_log ("Unable to negotiate TLS with this server",ERROR);
581 return NIL;
582 }
583 /* get authenticators from capabilities */
584 if (capaok) auths = LOCAL->cap.sasl;
585 /* get list of authenticators */
586 else if (pop3_send (stream,"AUTH",NIL)) {
587 while ((t = net_getline (LOCAL->netstream)) && (t[1] || (*t != '.'))) {
588 if (stream->debug) mm_dlog (t);
589 if ((i = mail_lookup_auth_name (t,flags)) && (--i < MAXAUTHENTICATORS))
590 auths |= (1 << i);
591 fs_give ((void **) &t);
592 }
593 if (t) { /* flush end of text indicator */
594 if (stream->debug) mm_dlog (t);
595 fs_give ((void **) &t);
596 }
597 }
598 /* disable LOGIN if PLAIN also advertised */
599 if ((i = mail_lookup_auth_name ("PLAIN",NIL)) && (--i < MAXAUTHENTICATORS) &&
600 (auths & (1 << i)) &&
601 (i = mail_lookup_auth_name ("LOGIN",NIL)) && (--i < MAXAUTHENTICATORS))
602 auths &= ~(1 << i);
604 if (auths) { /* got any authenticators? */
605 if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
606 /* remote name for authentication */
607 strncpy (mb->host,(long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
608 net_remotehost (LOCAL->netstream) : net_host (LOCAL->netstream),
609 NETMAXHOST-1);
610 mb->host[NETMAXHOST-1] = '\0';
611 }
612 for (t = NIL, LOCAL->saslcancel = NIL; !ret && LOCAL->netstream && auths &&
613 (at = mail_lookup_auth (find_rightmost_bit (&auths)+1)); ) {
614 if (t) { /* previous authenticator failed? */
615 sprintf (pwd,"Retrying using %.80s authentication after %.80s",
616 at->name,t);
617 mm_log (pwd,NIL);
618 fs_give ((void **) &t);
619 }
620 trial = 0; /* initial trial count */
621 do {
622 if (t) {
623 sprintf (pwd,"Retrying %s authentication after %.80s",at->name,t);
624 mm_log (pwd,WARN);
625 fs_give ((void **) &t);
626 }
627 LOCAL->saslcancel = NIL;
628 if (pop3_send (stream,"AUTH",at->name)) {
629 /* hide client authentication responses */
630 if (!(at->flags & AU_SECURE)) LOCAL->sensitive = T;
631 if ((*at->client) (pop3_challenge,pop3_response,"pop",mb,stream,
632 &trial,usr) && LOCAL->response) {
633 if (*LOCAL->response == '+') ret = LONGT;
634 /* if main program requested cancellation */
635 else if (!trial) mm_log ("POP3 Authentication cancelled",ERROR);
636 }
637 LOCAL->sensitive=NIL; /* unhide */
638 }
639 /* remember response if error and no cancel */
640 if (!ret && trial) t = cpystr (LOCAL->reply);
641 } while (!ret && trial && (trial < pop3_maxlogintrials) &&
642 LOCAL->netstream);
643 }
644 if (t) { /* previous authenticator failed? */
645 if (!LOCAL->saslcancel) { /* don't do this if a cancel */
646 sprintf (pwd,"Can not authenticate to POP3 server: %.80s",t);
647 mm_log (pwd,ERROR);
648 }
649 fs_give ((void **) &t);
650 }
651 }
653 else if (stream->secure)
654 mm_log ("Can't do secure authentication with this server",ERROR);
655 else if (mb->authuser[0])
656 mm_log ("Can't do /authuser with this server",ERROR);
657 else if (!LOCAL->cap.user) mm_log ("Can't login to this server",ERROR);
658 else { /* traditional login */
659 trial = 0; /* initial trial count */
660 do {
661 pwd[0] = 0; /* prompt user for password */
662 mm_login (mb,usr,pwd,trial++);
663 if (pwd[0]) { /* send login sequence if have password */
664 if (pop3_send (stream,"USER",usr)) {
665 LOCAL->sensitive = T; /* hide this command */
666 if (pop3_send (stream,"PASS",pwd)) ret = LONGT;
667 LOCAL->sensitive=NIL; /* unhide */
668 }
669 if (!ret) { /* failure */
670 mm_log (LOCAL->reply,WARN);
671 if (trial == pop3_maxlogintrials)
672 mm_log ("Too many login failures",ERROR);
673 }
674 }
675 /* user refused to give password */
676 else mm_log ("Login aborted",ERROR);
677 } while (!ret && pwd[0] && (trial < pop3_maxlogintrials) &&
678 LOCAL->netstream);
679 }
680 memset (pwd,0,MAILTMPLEN); /* erase password */
681 /* get capabilities if logged in */
682 if (ret && capaok) pop3_capa (stream,flags);
683 return ret;
684 }
686 /* Get challenge to authenticator in binary
687 * Accepts: stream
688 * pointer to returned size
689 * Returns: challenge or NIL if not challenge
690 */
692 void *pop3_challenge (void *s,unsigned long *len)
693 {
694 char tmp[MAILTMPLEN];
695 void *ret = NIL;
696 MAILSTREAM *stream = (MAILSTREAM *) s;
697 if (stream && LOCAL->response &&
698 (*LOCAL->response == '+') && (LOCAL->response[1] == ' ') &&
699 !(ret = rfc822_base64 ((unsigned char *) LOCAL->reply,
700 strlen (LOCAL->reply),len))) {
701 sprintf (tmp,"POP3 SERVER BUG (invalid challenge): %.80s",LOCAL->reply);
702 mm_log (tmp,ERROR);
703 }
704 return ret;
705 }
708 /* Send authenticator response in BASE64
709 * Accepts: MAIL stream
710 * string to send
711 * length of string
712 * Returns: T if successful, else NIL
713 */
715 long pop3_response (void *s,char *response,unsigned long size)
716 {
717 MAILSTREAM *stream = (MAILSTREAM *) s;
718 unsigned long i,j,ret;
719 char *t,*u;
720 if (response) { /* make CRLFless BASE64 string */
721 if (size) {
722 for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
723 j < i; j++) if (t[j] > ' ') *u++ = t[j];
724 *u = '\0'; /* tie off string for mm_dlog() */
725 if (stream->debug) mail_dlog (t,LOCAL->sensitive);
726 /* append CRLF */
727 *u++ = '\015'; *u++ = '\012'; *u = '\0';
728 ret = net_sout (LOCAL->netstream,t,u - t);
729 fs_give ((void **) &t);
730 }
731 else ret = net_sout (LOCAL->netstream,"\015\012",2);
732 }
733 else { /* abort requested */
734 ret = net_sout (LOCAL->netstream,"*\015\012",3);
735 LOCAL->saslcancel = T; /* mark protocol-requested SASL cancel */
736 }
737 pop3_reply (stream); /* get response */
738 return ret;
739 }
741 /* POP3 mail close
742 * Accepts: MAIL stream
743 * option flags
744 */
746 void pop3_close (MAILSTREAM *stream,long options)
747 {
748 int silent = stream->silent;
749 if (LOCAL) { /* only if a file is open */
750 if (LOCAL->netstream) { /* close POP3 connection */
751 stream->silent = T;
752 if (options & CL_EXPUNGE) pop3_expunge (stream,NIL,NIL);
753 stream->silent = silent;
754 pop3_send (stream,"QUIT",NIL);
755 mm_notify (stream,LOCAL->reply,BYE);
756 }
757 /* close POP3 connection */
758 if (LOCAL->netstream) net_close (LOCAL->netstream);
759 /* clean up */
760 if (LOCAL->cap.implementation)
761 fs_give ((void **) &LOCAL->cap.implementation);
762 if (LOCAL->txt) fclose (LOCAL->txt);
763 LOCAL->txt = NIL;
764 if (LOCAL->response) fs_give ((void **) &LOCAL->response);
765 /* nuke the local data */
766 fs_give ((void **) &stream->local);
767 stream->dtb = NIL; /* log out the DTB */
768 }
769 }
771 /* POP3 mail fetch fast information
772 * Accepts: MAIL stream
773 * sequence
774 * option flags
775 * This is ugly and slow
776 */
778 void pop3_fetchfast (MAILSTREAM *stream,char *sequence,long flags)
779 {
780 unsigned long i;
781 MESSAGECACHE *elt;
782 /* get sequence */
783 if (stream && LOCAL && ((flags & FT_UID) ?
784 mail_uid_sequence (stream,sequence) :
785 mail_sequence (stream,sequence)))
786 for (i = 1; i <= stream->nmsgs; i++)
787 if ((elt = mail_elt (stream,i))->sequence &&
788 !(elt->day && elt->rfc822_size)) {
789 ENVELOPE **env = NIL;
790 ENVELOPE *e = NIL;
791 if (!stream->scache) env = &elt->private.msg.env;
792 else if (stream->msgno == i) env = &stream->env;
793 else env = &e;
794 if (!*env || !elt->rfc822_size) {
795 STRING bs;
796 unsigned long hs;
797 char *ht = (*stream->dtb->header) (stream,i,&hs,NIL);
798 /* need to make an envelope? */
799 if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST,
800 stream->dtb->flags);
801 /* need message size too, ugh */
802 if (!elt->rfc822_size) {
803 (*stream->dtb->text) (stream,i,&bs,FT_PEEK);
804 elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs);
805 }
806 }
807 /* if need date, have date in envelope? */
808 if (!elt->day && *env && (*env)->date)
809 mail_parse_date (elt,(*env)->date);
810 /* sigh, fill in bogus default */
811 if (!elt->day) elt->day = elt->month = 1;
812 mail_free_envelope (&e);
813 }
814 }
816 /* POP3 fetch header as text
817 * Accepts: mail stream
818 * message number
819 * pointer to return size
820 * flags
821 * Returns: header text
822 */
824 char *pop3_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
825 long flags)
826 {
827 unsigned long i;
828 char tmp[MAILTMPLEN];
829 MESSAGECACHE *elt;
830 FILE *f = NIL;
831 *size = 0; /* initially no header size */
832 if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return "";
833 /* have header text already? */
834 if (!(elt = mail_elt (stream,msgno))->private.msg.header.text.data) {
835 /* if have CAPA and TOP, assume good TOP */
836 if (!LOCAL->loser && LOCAL->cap.top) {
837 sprintf (tmp,"TOP %lu 0",mail_uid (stream,msgno));
838 if (pop3_send (stream,tmp,NIL))
839 f = netmsg_slurp (LOCAL->netstream,&i,
840 &elt->private.msg.header.text.size);
841 }
842 /* otherwise load the cache with the message */
843 else if (elt->private.msg.header.text.size = pop3_cache (stream,elt))
844 f = LOCAL->txt;
845 if (f) { /* got it, make sure at start of file */
846 fseek (f,(unsigned long) 0,SEEK_SET);
847 /* read header from the cache */
848 fread (elt->private.msg.header.text.data = (unsigned char *)
849 fs_get ((size_t) elt->private.msg.header.text.size + 1),
850 (size_t) 1,(size_t) elt->private.msg.header.text.size,f);
851 /* tie off header text */
852 elt->private.msg.header.text.data[elt->private.msg.header.text.size] =
853 '\0';
854 /* close if not the cache */
855 if (f != LOCAL->txt) fclose (f);
856 }
857 }
858 /* return size of text */
859 if (size) *size = elt->private.msg.header.text.size;
860 return elt->private.msg.header.text.data ?
861 (char *) elt->private.msg.header.text.data : "";
862 }
864 /* POP3 fetch body
865 * Accepts: mail stream
866 * message number
867 * pointer to stringstruct to initialize
868 * flags
869 * Returns: T if successful, else NIL
870 */
872 long pop3_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
873 {
874 MESSAGECACHE *elt;
875 INIT (bs,mail_string,(void *) "",0);
876 if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return NIL;
877 elt = mail_elt (stream,msgno);
878 pop3_cache (stream,elt); /* make sure cache loaded */
879 if (!LOCAL->txt) return NIL; /* error if don't have a file */
880 if (!(flags & FT_PEEK)) { /* mark seen if needed */
881 elt->seen = T;
882 mm_flags (stream,elt->msgno);
883 }
884 INIT (bs,file_string,(void *) LOCAL->txt,elt->rfc822_size);
885 SETPOS (bs,LOCAL->hdrsize); /* skip past header */
886 return T;
887 }
889 /* POP3 cache message
890 * Accepts: mail stream
891 * message number
892 * Returns: header size
893 */
895 unsigned long pop3_cache (MAILSTREAM *stream,MESSAGECACHE *elt)
896 {
897 /* already cached? */
898 if (LOCAL->cached != mail_uid (stream,elt->msgno)) {
899 /* no, close current file */
900 if (LOCAL->txt) fclose (LOCAL->txt);
901 LOCAL->txt = NIL;
902 LOCAL->cached = LOCAL->hdrsize = 0;
903 if (pop3_send_num (stream,"RETR",elt->msgno) &&
904 (LOCAL->txt = netmsg_slurp (LOCAL->netstream,&elt->rfc822_size,
905 &LOCAL->hdrsize)))
906 /* set as current message number */
907 LOCAL->cached = mail_uid (stream,elt->msgno);
908 else elt->deleted = T;
909 }
910 return LOCAL->hdrsize;
911 }
913 /* POP3 mail ping mailbox
914 * Accepts: MAIL stream
915 * Returns: T if stream alive, else NIL
916 */
918 long pop3_ping (MAILSTREAM *stream)
919 {
920 return pop3_send (stream,"NOOP",NIL);
921 }
924 /* POP3 mail check mailbox
925 * Accepts: MAIL stream
926 */
928 void pop3_check (MAILSTREAM *stream)
929 {
930 if (pop3_ping (stream)) mm_log ("Check completed",NIL);
931 }
934 /* POP3 mail expunge mailbox
935 * Accepts: MAIL stream
936 * sequence to expunge if non-NIL
937 * expunge options
938 * Returns: T if success, NIL if failure
939 */
941 long pop3_expunge (MAILSTREAM *stream,char *sequence,long options)
942 {
943 char tmp[MAILTMPLEN];
944 MESSAGECACHE *elt;
945 unsigned long i = 1,n = 0;
946 long ret;
947 if (ret = sequence ? ((options & EX_UID) ?
948 mail_uid_sequence (stream,sequence) :
949 mail_sequence (stream,sequence)) :
950 LONGT) { /* build selected sequence if needed */
951 while (i <= stream->nmsgs) {
952 elt = mail_elt (stream,i);
953 if (elt->deleted && (sequence ? elt->sequence : T) &&
954 pop3_send_num (stream,"DELE",i)) {
955 /* expunging currently cached message? */
956 if (LOCAL->cached == mail_uid (stream,i)) {
957 /* yes, close current file */
958 if (LOCAL->txt) fclose (LOCAL->txt);
959 LOCAL->txt = NIL;
960 LOCAL->cached = LOCAL->hdrsize = 0;
961 }
962 mail_expunged (stream,i);
963 n++;
964 }
965 else i++; /* try next message */
966 }
967 if (!stream->silent) { /* only if not silent */
968 if (n) { /* did we expunge anything? */
969 sprintf (tmp,"Expunged %lu messages",n);
970 mm_log (tmp,(long) NIL);
971 }
972 else mm_log ("No messages deleted, so no update needed",(long) NIL);
973 }
974 }
975 return ret;
976 }
978 /* POP3 mail copy message(s)
979 * Accepts: MAIL stream
980 * sequence
981 * destination mailbox
982 * option flags
983 * Returns: T if copy successful, else NIL
984 */
986 long pop3_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
987 {
988 mailproxycopy_t pc =
989 (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
990 if (pc) return (*pc) (stream,sequence,mailbox,options);
991 mm_log ("Copy not valid for POP3",ERROR);
992 return NIL;
993 }
996 /* POP3 mail append message from stringstruct
997 * Accepts: MAIL stream
998 * destination mailbox
999 * append callback
1000 * data for callback
1001 * Returns: T if append successful, else NIL
1002 */
1004 long pop3_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
1006 mm_log ("Append not valid for POP3",ERROR);
1007 return NIL;
1010 /* Internal routines */
1013 /* Post Office Protocol 3 send command with number argument
1014 * Accepts: MAIL stream
1015 * command
1016 * number
1017 * Returns: T if successful, NIL if failure
1018 */
1020 long pop3_send_num (MAILSTREAM *stream,char *command,unsigned long n)
1022 char tmp[MAILTMPLEN];
1023 sprintf (tmp,"%lu",mail_uid (stream,n));
1024 return pop3_send (stream,command,tmp);
1028 /* Post Office Protocol 3 send command
1029 * Accepts: MAIL stream
1030 * command
1031 * command argument
1032 * Returns: T if successful, NIL if failure
1033 */
1035 long pop3_send (MAILSTREAM *stream,char *command,char *args)
1037 long ret;
1038 char *s = (char *) fs_get (strlen (command) + (args ? strlen (args) + 1: 0)
1039 + 3);
1040 mail_lock (stream); /* lock up the stream */
1041 if (!LOCAL->netstream) ret = pop3_fake (stream,"POP3 connection lost");
1042 else { /* build the complete command */
1043 if (args) sprintf (s,"%s %s",command,args);
1044 else strcpy (s,command);
1045 if (stream->debug) mail_dlog (s,LOCAL->sensitive);
1046 strcat (s,"\015\012");
1047 /* send the command */
1048 ret = net_soutr (LOCAL->netstream,s) ? pop3_reply (stream) :
1049 pop3_fake (stream,"POP3 connection broken in command");
1051 fs_give ((void **) &s);
1052 mail_unlock (stream); /* unlock stream */
1053 return ret;
1056 /* Post Office Protocol 3 get reply
1057 * Accepts: MAIL stream
1058 * Returns: T if success reply, NIL if error reply
1059 */
1061 long pop3_reply (MAILSTREAM *stream)
1063 char *s;
1064 /* flush old reply */
1065 if (LOCAL->response) fs_give ((void **) &LOCAL->response);
1066 /* get reply */
1067 if (!(LOCAL->response = net_getline (LOCAL->netstream)))
1068 return pop3_fake (stream,"POP3 connection broken in response");
1069 if (stream->debug) mm_dlog (LOCAL->response);
1070 LOCAL->reply = (s = strchr (LOCAL->response,' ')) ? s + 1 : LOCAL->response;
1071 /* return success or failure */
1072 return (*LOCAL->response =='+') ? T : NIL;
1076 /* Post Office Protocol 3 set fake error
1077 * Accepts: MAIL stream
1078 * error text
1079 * Returns: NIL, always
1080 */
1082 long pop3_fake (MAILSTREAM *stream,char *text)
1084 mm_notify (stream,text,BYE); /* send bye alert */
1085 if (LOCAL->netstream) net_close (LOCAL->netstream);
1086 LOCAL->netstream = NIL; /* farewell, dear TCP stream */
1087 /* flush any old reply */
1088 if (LOCAL->response) fs_give ((void **) &LOCAL->response);
1089 LOCAL->reply = text; /* set up pseudo-reply string */
1090 return NIL; /* return error code */

UW-IMAP'd extensions by yuuji