1 /* ========================================================================
2 * Copyright 2008-2011 Mark Crispin
3 * ========================================================================
7 * Program: Post Office Protocol 3 (POP3) client routines
12 * Last Edited: 8 April 2011
14 * Previous versions of this file were:
16 * Copyright 1988-2007 University of Washington
18 * Licensed under the Apache License, Version 2.0 (the "License");
19 * you may not use this file except in compliance with the License.
20 * You may obtain a copy of the License at
22 * http://www.apache.org/licenses/LICENSE-2.0
36 #define POP3TCPPORT (long) 110 /* assigned TCP contact port */
37 #define POP3SSLPORT (long) 995 /* assigned SSL TCP contact port */
38 #define IDLETIMEOUT (long) 10 /* defined in RFC 1939 */
41 /* POP3 I/O stream local data */
43 typedef struct pop3_local
{
44 NETSTREAM
*netstream
; /* TCP I/O stream */
45 char *response
; /* last server reply */
46 char *reply
; /* text of last server reply */
47 unsigned long cached
; /* current cached message uid */
48 unsigned long hdrsize
; /* current cached header size */
49 FILE *txt
; /* current cached file descriptor */
51 unsigned int capa
: 1; /* server has CAPA, definitely new */
52 unsigned int expire
: 1; /* server has EXPIRE */
53 unsigned int logindelay
: 1;/* server has LOGIN-DELAY */
54 unsigned int stls
: 1; /* server has STLS */
55 unsigned int pipelining
: 1;/* server has PIPELINING */
56 unsigned int respcodes
: 1; /* server has RESP-CODES */
57 unsigned int top
: 1; /* server has TOP */
58 unsigned int uidl
: 1; /* server has UIDL */
59 unsigned int user
: 1; /* server has USER */
60 char *implementation
; /* server implementation string */
61 long delaysecs
; /* minimum time between login (neg variable) */
62 long expiredays
; /* server-guaranteed minimum retention days */
63 /* supported authenticators */
64 unsigned int sasl
: MAXAUTHENTICATORS
;
66 unsigned int sensitive
: 1; /* sensitive data in progress */
67 unsigned int loser
: 1; /* server is a loser */
68 unsigned int saslcancel
: 1; /* SASL cancelled by protocol */
72 /* Convenient access to local data */
74 #define LOCAL ((POP3LOCAL *) stream->local)
76 /* Function prototypes */
78 DRIVER
*pop3_valid (char *name
);
79 void *pop3_parameters (long function
,void *value
);
80 void pop3_scan (MAILSTREAM
*stream
,char *ref
,char *pat
,char *contents
);
81 void pop3_list (MAILSTREAM
*stream
,char *ref
,char *pat
);
82 void pop3_lsub (MAILSTREAM
*stream
,char *ref
,char *pat
);
83 long pop3_subscribe (MAILSTREAM
*stream
,char *mailbox
);
84 long pop3_unsubscribe (MAILSTREAM
*stream
,char *mailbox
);
85 long pop3_create (MAILSTREAM
*stream
,char *mailbox
);
86 long pop3_delete (MAILSTREAM
*stream
,char *mailbox
);
87 long pop3_rename (MAILSTREAM
*stream
,char *old
,char *newname
);
88 long pop3_status (MAILSTREAM
*stream
,char *mbx
,long flags
);
89 MAILSTREAM
*pop3_open (MAILSTREAM
*stream
);
90 long pop3_capa (MAILSTREAM
*stream
,long flags
);
91 long pop3_auth (MAILSTREAM
*stream
,NETMBX
*mb
,char *pwd
,char *usr
);
92 void *pop3_challenge (void *stream
,unsigned long *len
);
93 long pop3_response (void *stream
,char *s
,unsigned long size
);
94 void pop3_close (MAILSTREAM
*stream
,long options
);
95 void pop3_fetchfast (MAILSTREAM
*stream
,char *sequence
,long flags
);
96 char *pop3_header (MAILSTREAM
*stream
,unsigned long msgno
,unsigned long *size
,
98 long pop3_text (MAILSTREAM
*stream
,unsigned long msgno
,STRING
*bs
,long flags
);
99 unsigned long pop3_cache (MAILSTREAM
*stream
,MESSAGECACHE
*elt
);
100 long pop3_ping (MAILSTREAM
*stream
);
101 void pop3_check (MAILSTREAM
*stream
);
102 long pop3_expunge (MAILSTREAM
*stream
,char *sequence
,long options
);
103 long pop3_copy (MAILSTREAM
*stream
,char *sequence
,char *mailbox
,long options
);
104 long pop3_append (MAILSTREAM
*stream
,char *mailbox
,append_t af
,void *data
);
106 long pop3_send_num (MAILSTREAM
*stream
,char *command
,unsigned long n
);
107 long pop3_send (MAILSTREAM
*stream
,char *command
,char *args
);
108 long pop3_reply (MAILSTREAM
*stream
);
109 long pop3_fake (MAILSTREAM
*stream
,char *text
);
111 /* POP3 mail routines */
114 /* Driver dispatch used by MAIL */
116 DRIVER pop3driver
= {
117 "pop3", /* driver name */
119 #ifdef INADEQUATE_MEMORY
122 DR_MAIL
|DR_NOFAST
|DR_CRLF
|DR_NOSTICKY
|DR_NOINTDATE
|DR_NONEWMAIL
,
123 (DRIVER
*) NIL
, /* next driver */
124 pop3_valid
, /* mailbox is valid for us */
125 pop3_parameters
, /* manipulate parameters */
126 pop3_scan
, /* scan mailboxes */
127 pop3_list
, /* find mailboxes */
128 pop3_lsub
, /* find subscribed mailboxes */
129 pop3_subscribe
, /* subscribe to mailbox */
130 pop3_unsubscribe
, /* unsubscribe from mailbox */
131 pop3_create
, /* create mailbox */
132 pop3_delete
, /* delete mailbox */
133 pop3_rename
, /* rename mailbox */
134 pop3_status
, /* status of mailbox */
135 pop3_open
, /* open mailbox */
136 pop3_close
, /* close mailbox */
137 pop3_fetchfast
, /* fetch message "fast" attributes */
138 NIL
, /* fetch message flags */
139 NIL
, /* fetch overview */
140 NIL
, /* fetch message structure */
141 pop3_header
, /* fetch message header */
142 pop3_text
, /* fetch message text */
143 NIL
, /* fetch message */
144 NIL
, /* unique identifier */
145 NIL
, /* message number from UID */
146 NIL
, /* modify flags */
147 NIL
, /* per-message modify flags */
148 NIL
, /* search for message based on criteria */
149 NIL
, /* sort messages */
150 NIL
, /* thread messages */
151 pop3_ping
, /* ping mailbox to see if still alive */
152 pop3_check
, /* check for new messages */
153 pop3_expunge
, /* expunge deleted messages */
154 pop3_copy
, /* copy messages to another mailbox */
155 pop3_append
, /* append string message to mailbox */
156 NIL
/* garbage collect stream */
159 /* prototype stream */
160 MAILSTREAM pop3proto
= {&pop3driver
};
162 /* driver parameters */
163 static unsigned long pop3_maxlogintrials
= MAXLOGINTRIALS
;
164 static long pop3_port
= 0;
165 static long pop3_sslport
= 0;
167 /* POP3 mail validate mailbox
168 * Accepts: mailbox name
169 * Returns: our driver if name is valid, NIL otherwise
172 DRIVER
*pop3_valid (char *name
)
175 return (mail_valid_net_parse (name
,&mb
) &&
176 !strcmp (mb
.service
,pop3driver
.name
) && !mb
.authuser
[0] &&
177 !compare_cstring (mb
.mailbox
,"INBOX")) ? &pop3driver
: NIL
;
181 /* News manipulate driver parameters
182 * Accepts: function code
183 * function-dependent value
184 * Returns: function-dependent return value
187 void *pop3_parameters (long function
,void *value
)
189 switch ((int) function
) {
190 case SET_MAXLOGINTRIALS
:
191 pop3_maxlogintrials
= (unsigned long) value
;
193 case GET_MAXLOGINTRIALS
:
194 value
= (void *) pop3_maxlogintrials
;
197 pop3_port
= (long) value
;
200 value
= (void *) pop3_port
;
203 pop3_sslport
= (long) value
;
206 value
= (void *) pop3_sslport
;
208 case GET_IDLETIMEOUT
:
209 value
= (void *) IDLETIMEOUT
;
212 value
= NIL
; /* error case */
218 /* POP3 mail scan mailboxes for string
219 * Accepts: mail stream
225 void pop3_scan (MAILSTREAM
*stream
,char *ref
,char *pat
,char *contents
)
227 char tmp
[MAILTMPLEN
];
228 if ((ref
&& *ref
) ? /* have a reference */
229 (pop3_valid (ref
) && pmatch ("INBOX",pat
)) :
230 (mail_valid_net (pat
,&pop3driver
,NIL
,tmp
) && pmatch ("INBOX",tmp
)))
231 mm_log ("Scan not valid for POP3 mailboxes",ERROR
);
235 /* POP3 mail find list of all mailboxes
236 * Accepts: mail stream
241 void pop3_list (MAILSTREAM
*stream
,char *ref
,char *pat
)
243 char tmp
[MAILTMPLEN
];
244 if (ref
&& *ref
) { /* have a reference */
245 if (pop3_valid (ref
) && pmatch ("INBOX",pat
)) {
246 strcpy (strchr (strcpy (tmp
,ref
),'}')+1,"INBOX");
247 mm_list (stream
,NIL
,tmp
,LATT_NOINFERIORS
);
250 else if (mail_valid_net (pat
,&pop3driver
,NIL
,tmp
) && pmatch ("INBOX",tmp
)) {
251 strcpy (strchr (strcpy (tmp
,pat
),'}')+1,"INBOX");
252 mm_list (stream
,NIL
,tmp
,LATT_NOINFERIORS
);
256 /* POP3 mail find list of subscribed mailboxes
257 * Accepts: mail stream
262 void pop3_lsub (MAILSTREAM
*stream
,char *ref
,char *pat
)
265 char *s
,mbx
[MAILTMPLEN
],tmp
[MAILTMPLEN
];
266 if (*pat
== '{') { /* if remote pattern, must be POP3 */
267 if (!pop3_valid (pat
)) return;
268 ref
= NIL
; /* good POP3 pattern, punt reference */
270 /* if remote reference, must be valid POP3 */
271 if (ref
&& (*ref
== '{') && !pop3_valid (ref
)) return;
272 /* kludgy application of reference */
273 if (ref
&& *ref
) sprintf (mbx
,"%s%s",ref
,pat
);
274 else strcpy (mbx
,pat
);
276 if ((s
= sm_read (tmp
,&sdb
)) != NULL
) do if (pop3_valid (s
) && pmatch (s
,mbx
))
277 mm_lsub (stream
,NIL
,s
,NIL
);
278 /* until no more subscriptions */
279 while ((s
= sm_read (tmp
,&sdb
)) != NULL
);
283 /* POP3 mail subscribe to mailbox
284 * Accepts: mail stream
285 * mailbox to add to subscription list
286 * Returns: T on success, NIL on failure
289 long pop3_subscribe (MAILSTREAM
*stream
,char *mailbox
)
291 return sm_subscribe (mailbox
);
295 /* POP3 mail unsubscribe to mailbox
296 * Accepts: mail stream
297 * mailbox to delete from subscription list
298 * Returns: T on success, NIL on failure
301 long pop3_unsubscribe (MAILSTREAM
*stream
,char *mailbox
)
303 return sm_unsubscribe (mailbox
);
306 /* POP3 mail create mailbox
307 * Accepts: mail stream
308 * mailbox name to create
309 * Returns: T on success, NIL on failure
312 long pop3_create (MAILSTREAM
*stream
,char *mailbox
)
314 return NIL
; /* never valid for POP3 */
318 /* POP3 mail delete mailbox
319 * mailbox name to delete
320 * Returns: T on success, NIL on failure
323 long pop3_delete (MAILSTREAM
*stream
,char *mailbox
)
325 return NIL
; /* never valid for POP3 */
329 /* POP3 mail rename mailbox
330 * Accepts: mail stream
333 * Returns: T on success, NIL on failure
336 long pop3_rename (MAILSTREAM
*stream
,char *old
,char *newname
)
338 return NIL
; /* never valid for POP3 */
342 * Accepts: mail stream
345 * Returns: T on success, NIL on failure
348 long pop3_status (MAILSTREAM
*stream
,char *mbx
,long flags
)
353 MAILSTREAM
*tstream
=
354 (stream
&& LOCAL
->netstream
&& mail_usable_network_stream (stream
,mbx
)) ?
355 stream
: mail_open (NIL
,mbx
,OP_SILENT
);
356 if (tstream
) { /* have a usable stream? */
357 status
.flags
= flags
; /* return status values */
358 status
.messages
= tstream
->nmsgs
;
359 status
.recent
= tstream
->recent
;
360 if (flags
& SA_UNSEEN
) /* must search to get unseen messages */
361 for (i
= 1,status
.unseen
= 0; i
<= tstream
->nmsgs
; i
++)
362 if (!mail_elt (tstream
,i
)->seen
) status
.unseen
++;
363 status
.uidnext
= tstream
->uid_last
+ 1;
364 status
.uidvalidity
= tstream
->uid_validity
;
365 /* pass status to main program */
366 mm_status (tstream
,mbx
,&status
);
367 if (stream
!= tstream
) mail_close (tstream
);
370 return ret
; /* success */
374 * Accepts: stream to open
375 * Returns: stream on success, NIL on failure
378 MAILSTREAM
*pop3_open (MAILSTREAM
*stream
)
381 char *s
,*t
,tmp
[MAILTMPLEN
],usr
[MAILTMPLEN
];
384 /* return prototype for OP_PROTOTYPE call */
385 if (!stream
) return &pop3proto
;
386 mail_valid_net_parse (stream
->mailbox
,&mb
);
387 usr
[0] = '\0'; /* initially no user name */
388 if (stream
->local
) fatal ("pop3 recycle stream");
389 /* /anonymous not supported */
390 if (mb
.anoflag
|| stream
->anonymous
) {
391 mm_log ("Anonymous POP3 login not available",ERROR
);
394 /* /readonly not supported either */
395 if (mb
.readonlyflag
|| stream
->rdonly
) {
396 mm_log ("Read-only POP3 access not available",ERROR
);
399 /* copy other switches */
400 if (mb
.dbgflag
) stream
->debug
= T
;
401 if (mb
.secflag
) stream
->secure
= T
;
402 mb
.trysslflag
= stream
->tryssl
= (mb
.trysslflag
|| stream
->tryssl
) ? T
: NIL
;
403 stream
->local
= /* instantiate localdata */
404 (void *) memset (fs_get (sizeof (POP3LOCAL
)),0,sizeof (POP3LOCAL
));
405 stream
->sequence
++; /* bump sequence number */
406 stream
->perm_deleted
= T
; /* deleted is only valid flag */
408 if ((LOCAL
->netstream
= /* try to open connection */
409 net_open (&mb
,NIL
,pop3_port
? pop3_port
: POP3TCPPORT
,
410 (NETDRIVER
*) mail_parameters (NIL
,GET_SSLDRIVER
,NIL
),
411 "*pop3s",pop3_sslport
? pop3_sslport
: POP3SSLPORT
)) &&
412 pop3_reply (stream
)) {
413 mm_log (LOCAL
->reply
,NIL
); /* give greeting */
414 if (!pop3_auth (stream
,&mb
,tmp
,usr
)) pop3_close (stream
,NIL
);
415 else if (pop3_send (stream
,"STAT",NIL
)) {
416 int silent
= stream
->silent
;
418 sprintf (tmp
,"{%.200s:%lu/pop3",
419 (long) mail_parameters (NIL
,GET_TRUSTDNS
,NIL
) ?
420 net_host (LOCAL
->netstream
) : mb
.host
,
421 net_port (LOCAL
->netstream
));
422 if (mb
.tlsflag
) strcat (tmp
,"/tls");
423 if (mb
.tlssslv23
) strcat (tmp
,"/tls-sslv23");
424 if (mb
.tls1
) strcat (tmp
,"/tls1");
425 if (mb
.tls1_1
) strcat (tmp
,"/tls1_1");
426 if (mb
.tls1_2
) strcat (tmp
,"/tls1_2");
427 if (mb
.tls1_3
) strcat (tmp
,"/tls1_3");
428 if (mb
.notlsflag
) strcat (tmp
,"/notls");
429 if (mb
.sslflag
) strcat (tmp
,"/ssl");
430 if (mb
.novalidate
) strcat (tmp
,"/novalidate-cert");
431 if ((LOCAL
->loser
= mb
.loser
) != 0) strcat (tmp
,"/loser");
432 if (stream
->secure
) strcat (tmp
,"/secure");
433 sprintf (tmp
+ strlen (tmp
),"/user=\"%s\"}%s",usr
,mb
.mailbox
);
434 stream
->inbox
= T
; /* always INBOX */
435 fs_give ((void **) &stream
->mailbox
);
436 stream
->mailbox
= cpystr (tmp
);
437 /* notify upper level */
438 mail_exists (stream
,stream
->uid_last
= strtoul (LOCAL
->reply
,NIL
,10));
439 mail_recent (stream
,stream
->nmsgs
);
440 /* instantiate elt */
441 for (i
= 0; i
< stream
->nmsgs
;) {
442 elt
= mail_elt (stream
,++i
);
443 elt
->valid
= elt
->recent
= T
;
444 elt
->private.uid
= i
;
447 /* trust LIST output if new server */
448 if (!LOCAL
->loser
&& LOCAL
->cap
.capa
&& pop3_send (stream
,"LIST",NIL
)) {
449 while ((s
= net_getline (LOCAL
->netstream
)) && (*s
!= '.')) {
450 if ((i
= strtoul (s
,&t
,10)) && (i
<= stream
->nmsgs
) &&
451 (j
= strtoul (t
,NIL
,10))) mail_elt (stream
,i
)->rfc822_size
= j
;
452 fs_give ((void **) &s
);
454 /* flush final dot */
455 if (s
) fs_give ((void **) &s
);
456 else { /* lost connection */
457 mm_log ("POP3 connection broken while itemizing messages",ERROR
);
458 pop3_close (stream
,NIL
);
462 stream
->silent
= silent
; /* notify main program */
463 mail_exists (stream
,stream
->nmsgs
);
464 /* notify if empty */
465 if (!(stream
->nmsgs
|| stream
->silent
)) mm_log ("Mailbox is empty",WARN
);
467 else { /* error in STAT */
468 mm_log (LOCAL
->reply
,ERROR
);
469 pop3_close (stream
,NIL
); /* too bad */
472 else { /* connection failed */
473 if (LOCAL
->reply
) mm_log (LOCAL
->reply
,ERROR
);
474 pop3_close (stream
,NIL
); /* failed, clean up */
476 return LOCAL
? stream
: NIL
; /* if stream is alive, return to caller */
481 * authenticator flags
482 * Returns: T on success, NIL on failure
485 long pop3_capa (MAILSTREAM
*stream
,long flags
)
489 if (LOCAL
->cap
.implementation
)/* zap all old capabilities */
490 fs_give ((void **) &LOCAL
->cap
.implementation
);
491 memset (&LOCAL
->cap
,0,sizeof (LOCAL
->cap
));
492 /* get server capabilities */
493 if (pop3_send (stream
,"CAPA",NIL
)) LOCAL
->cap
.capa
= T
;
495 LOCAL
->cap
.user
= T
; /* guess worst-case old server */
496 return NIL
; /* no CAPA on this server */
498 while ((t
= net_getline (LOCAL
->netstream
)) && (t
[1] || (*t
!= '.'))) {
499 if (stream
->debug
) mm_dlog (t
);
500 /* get optional capability arguments */
501 if ((args
= strchr (t
,' ')) != NULL
) *args
++ = '\0';
502 if (!compare_cstring (t
,"STLS")) LOCAL
->cap
.stls
= T
;
503 else if (!compare_cstring (t
,"PIPELINING")) LOCAL
->cap
.pipelining
= T
;
504 else if (!compare_cstring (t
,"RESP-CODES")) LOCAL
->cap
.respcodes
= T
;
505 else if (!compare_cstring (t
,"TOP")) LOCAL
->cap
.top
= T
;
506 else if (!compare_cstring (t
,"UIDL")) LOCAL
->cap
.uidl
= T
;
507 else if (!compare_cstring (t
,"USER")) LOCAL
->cap
.user
= T
;
508 else if (!compare_cstring (t
,"IMPLEMENTATION") && args
)
509 LOCAL
->cap
.implementation
= cpystr (args
);
510 else if (!compare_cstring (t
,"EXPIRE") && args
) {
511 LOCAL
->cap
.expire
= T
; /* note that it is present */
512 if ((s
= strchr(args
,' ')) != NULL
){/* separate time from possible USER */
514 /* in case they add something after USER */
515 if ((strlen (s
) > 4) && (s
[4] == ' ')) s
[4] = '\0';
517 LOCAL
->cap
.expire
= /* get expiration time */
518 (!compare_cstring (args
,"NEVER")) ? 65535 :
519 ((s
&& !compare_cstring (s
,"USER")) ? -atoi (args
) : atoi (args
));
521 else if (!compare_cstring (t
,"LOGIN-DELAY") && args
) {
522 LOCAL
->cap
.logindelay
= T
;/* note that it is present */
523 if ((s
= strchr(args
,' ')) != NULL
){/* separate time from possible USER */
525 /* in case they add something after USER */
526 if ((strlen (s
) > 4) && (s
[4] == ' ')) s
[4] = '\0';
529 LOCAL
->cap
.delaysecs
= (s
&& !compare_cstring (s
,"USER")) ?
530 -atoi (args
) : atoi (args
);
532 else if (!compare_cstring (t
,"SASL") && args
)
533 for (args
= strtok_r (args
," ",&r
); args
; args
= strtok_r (NIL
," ",&r
))
534 if ((i
= mail_lookup_auth_name (args
,flags
)) &&
535 (--i
< MAXAUTHENTICATORS
))
536 LOCAL
->cap
.sasl
|= (1 << i
);
537 fs_give ((void **) &t
);
539 if (t
) { /* flush end of text indicator */
540 if (stream
->debug
) mm_dlog (t
);
541 fs_give ((void **) &t
);
547 * Accepts: stream to login
548 * parsed network mailbox structure
549 * scratch buffer of length MAILTMPLEN
550 * place to return user name
551 * Returns: T on success, NIL on failure
554 long pop3_auth (MAILSTREAM
*stream
,NETMBX
*mb
,char *pwd
,char *usr
)
556 unsigned long i
,trial
,auths
= 0, authsaved
;
558 AUTHENTICATOR
*at
, *atsaved
;
560 long flags
= (stream
->secure
? AU_SECURE
: NIL
) |
561 (mb
->authuser
[0] ? AU_AUTHUSER
: NIL
);
562 long capaok
= pop3_capa (stream
,flags
);
563 NETDRIVER
*ssld
= (NETDRIVER
*) mail_parameters (NIL
,GET_SSLDRIVER
,NIL
);
564 sslstart_t stls
= (sslstart_t
) mail_parameters (NIL
,GET_SSLSTART
,NIL
);
565 /* server has TLS? */
566 if (stls
&& LOCAL
->cap
.stls
&& !mb
->sslflag
&& !mb
->notlsflag
&&
567 pop3_send (stream
,"STLS",NIL
)) {
568 mb
->tlsflag
= T
; /* TLS OK, get into TLS at this end */
569 LOCAL
->netstream
->dtb
= ssld
;
570 if (!(LOCAL
->netstream
->stream
=
571 (*stls
) (LOCAL
->netstream
->stream
,mb
->host
,
572 SSL_MTHD(*mb
) | (mb
->novalidate
? NET_NOVALIDATECERT
: NIL
)))) {
573 /* drat, drop this connection */
574 if (LOCAL
->netstream
) net_close (LOCAL
->netstream
);
575 LOCAL
->netstream
= NIL
;
576 return NIL
; /* TLS negotiation failed */
578 pop3_capa (stream
,flags
); /* get capabilities now that TLS in effect */
580 else if (mb
->tlsflag
) { /* user specified /tls but can't do it */
581 mm_log ("Unable to negotiate TLS with this server",ERROR
);
584 /* get authenticators from capabilities */
585 if (capaok
) auths
= LOCAL
->cap
.sasl
;
586 /* get list of authenticators */
587 else if (pop3_send (stream
,"AUTH",NIL
)) {
588 while ((t
= net_getline (LOCAL
->netstream
)) && (t
[1] || (*t
!= '.'))) {
589 if (stream
->debug
) mm_dlog (t
);
590 if ((i
= mail_lookup_auth_name (t
,flags
)) && (--i
< MAXAUTHENTICATORS
))
592 fs_give ((void **) &t
);
594 if (t
) { /* flush end of text indicator */
595 if (stream
->debug
) mm_dlog (t
);
596 fs_give ((void **) &t
);
599 /* disable LOGIN if PLAIN also advertised */
600 if ((i
= mail_lookup_auth_name ("PLAIN",NIL
)) && (--i
< MAXAUTHENTICATORS
) &&
601 (auths
& (1 << i
)) &&
602 (i
= mail_lookup_auth_name ("LOGIN",NIL
)) && (--i
< MAXAUTHENTICATORS
))
605 if (auths
) { /* got any authenticators? */
606 if ((long) mail_parameters (NIL
,GET_TRUSTDNS
,NIL
)) {
607 /* remote name for authentication */
608 strncpy (mb
->host
,(long) mail_parameters (NIL
,GET_SASLUSESPTRNAME
,NIL
) ?
609 net_remotehost (LOCAL
->netstream
) : net_host (LOCAL
->netstream
),
611 mb
->host
[NETMAXHOST
-1] = '\0';
613 for (t
= NIL
, LOCAL
->saslcancel
= NIL
; !ret
&& LOCAL
->netstream
&& auths
&&
614 (at
= mail_lookup_auth (find_rightmost_bit (&auths
)+1)); ) {
616 if(!compare_cstring(at
->name
, mb
->auth
))
624 if (t
) { /* previous authenticator failed? */
625 sprintf (pwd
,"Retrying using %.80s authentication after %.80s",
628 fs_give ((void **) &t
);
630 trial
= 0; /* initial trial count */
633 sprintf (pwd
,"Retrying %s authentication after %.80s",at
->name
,t
);
635 fs_give ((void **) &t
);
637 LOCAL
->saslcancel
= NIL
;
638 if (pop3_send (stream
,"AUTH",at
->name
)) {
639 /* hide client authentication responses */
640 if (!(at
->flags
& AU_SECURE
)) LOCAL
->sensitive
= T
;
641 if ((*at
->client
) (pop3_challenge
,pop3_response
,"pop",mb
,stream
,
642 &trial
,usr
) && LOCAL
->response
) {
643 if (*LOCAL
->response
== '+') ret
= LONGT
;
644 /* if main program requested cancellation */
645 else if (!trial
) mm_log ("POP3 Authentication cancelled",ERROR
);
647 LOCAL
->sensitive
=NIL
; /* unhide */
649 /* remember response if error and no cancel */
650 if (!ret
&& trial
) t
= cpystr (LOCAL
->reply
);
651 } while (!ret
&& trial
&& (trial
< pop3_maxlogintrials
) &&
654 if (t
) { /* previous authenticator failed? */
655 if (!LOCAL
->saslcancel
) { /* don't do this if a cancel */
656 sprintf (pwd
,"Can not authenticate to POP3 server: %.80s",t
);
659 fs_give ((void **) &t
);
662 if(!authsaved
) sprintf (pwd
,"Client does not support AUTH=%.80s authenticator",mb
->auth
);
663 else if (!atsaved
) sprintf (pwd
,"POP server does not support AUTH=%.80s authenticator",mb
->auth
);
664 if (!authsaved
|| !atsaved
) mm_log (pwd
,ERROR
);
669 else if (stream
->secure
)
670 mm_log ("Can't do secure authentication with this server",ERROR
);
671 else if (mb
->authuser
[0])
672 mm_log ("Can't do /authuser with this server",ERROR
);
673 else if (!LOCAL
->cap
.user
) mm_log ("Can't login to this server",ERROR
);
674 else { /* traditional login */
675 trial
= 0; /* initial trial count */
677 pwd
[0] = 0; /* prompt user for password */
678 mm_login (mb
,usr
,pwd
,trial
++);
679 if (pwd
[0]) { /* send login sequence if have password */
680 if (pop3_send (stream
,"USER",usr
)) {
681 LOCAL
->sensitive
= T
; /* hide this command */
682 if (pop3_send (stream
,"PASS",pwd
)) ret
= LONGT
;
683 LOCAL
->sensitive
=NIL
; /* unhide */
685 if (!ret
) { /* failure */
686 mm_log (LOCAL
->reply
,WARN
);
687 if (trial
== pop3_maxlogintrials
)
688 mm_log ("Too many login failures",ERROR
);
691 /* user refused to give password */
692 else mm_log ("Login aborted",ERROR
);
693 } while (!ret
&& pwd
[0] && (trial
< pop3_maxlogintrials
) &&
696 memset (pwd
,0,MAILTMPLEN
); /* erase password */
697 /* get capabilities if logged in */
698 if (ret
&& capaok
) pop3_capa (stream
,flags
);
702 /* Get challenge to authenticator in binary
704 * pointer to returned size
705 * Returns: challenge or NIL if not challenge
708 void *pop3_challenge (void *s
,unsigned long *len
)
710 char tmp
[MAILTMPLEN
];
712 MAILSTREAM
*stream
= (MAILSTREAM
*) s
;
713 if (stream
&& LOCAL
->response
&&
714 (*LOCAL
->response
== '+') && (LOCAL
->response
[1] == ' ') &&
715 !(ret
= rfc822_base64 ((unsigned char *) LOCAL
->reply
,
716 strlen (LOCAL
->reply
),len
))) {
717 sprintf (tmp
,"POP3 SERVER BUG (invalid challenge): %.80s",LOCAL
->reply
);
724 /* Send authenticator response in BASE64
725 * Accepts: MAIL stream
728 * Returns: T if successful, else NIL
731 long pop3_response (void *s
,char *response
,unsigned long size
)
733 MAILSTREAM
*stream
= (MAILSTREAM
*) s
;
734 unsigned long i
,j
,ret
;
736 if (response
) { /* make CRLFless BASE64 string */
738 for (t
= (char *) rfc822_binary ((void *) response
,size
,&i
),u
= t
,j
= 0;
739 j
< i
; j
++) if (t
[j
] > ' ') *u
++ = t
[j
];
740 *u
= '\0'; /* tie off string for mm_dlog() */
741 if (stream
->debug
) mail_dlog (t
,LOCAL
->sensitive
);
743 *u
++ = '\015'; *u
++ = '\012'; *u
= '\0';
744 ret
= net_sout (LOCAL
->netstream
,t
,u
- t
);
745 fs_give ((void **) &t
);
747 else ret
= net_sout (LOCAL
->netstream
,"\015\012",2);
749 else { /* abort requested */
750 ret
= net_sout (LOCAL
->netstream
,"*\015\012",3);
751 LOCAL
->saslcancel
= T
; /* mark protocol-requested SASL cancel */
753 pop3_reply (stream
); /* get response */
758 * Accepts: MAIL stream
762 void pop3_close (MAILSTREAM
*stream
,long options
)
764 int silent
= stream
->silent
;
765 if (LOCAL
) { /* only if a file is open */
766 if (LOCAL
->netstream
) { /* close POP3 connection */
768 if (options
& CL_EXPUNGE
) pop3_expunge (stream
,NIL
,NIL
);
769 stream
->silent
= silent
;
770 pop3_send (stream
,"QUIT",NIL
);
771 mm_notify (stream
,LOCAL
->reply
,BYE
);
773 /* close POP3 connection */
774 if (LOCAL
->netstream
) net_close (LOCAL
->netstream
);
776 if (LOCAL
->cap
.implementation
)
777 fs_give ((void **) &LOCAL
->cap
.implementation
);
778 if (LOCAL
->txt
) fclose (LOCAL
->txt
);
780 if (LOCAL
->response
) fs_give ((void **) &LOCAL
->response
);
781 /* nuke the local data */
782 fs_give ((void **) &stream
->local
);
783 stream
->dtb
= NIL
; /* log out the DTB */
787 /* POP3 mail fetch fast information
788 * Accepts: MAIL stream
791 * This is ugly and slow
794 void pop3_fetchfast (MAILSTREAM
*stream
,char *sequence
,long flags
)
799 if (stream
&& LOCAL
&& ((flags
& FT_UID
) ?
800 mail_uid_sequence (stream
,sequence
) :
801 mail_sequence (stream
,sequence
)))
802 for (i
= 1; i
<= stream
->nmsgs
; i
++)
803 if ((elt
= mail_elt (stream
,i
))->sequence
&&
804 !(elt
->day
&& elt
->rfc822_size
)) {
805 ENVELOPE
**env
= NIL
;
807 if (!stream
->scache
) env
= &elt
->private.msg
.env
;
808 else if (stream
->msgno
== i
) env
= &stream
->env
;
810 if (!*env
|| !elt
->rfc822_size
) {
813 char *ht
= (*stream
->dtb
->header
) (stream
,i
,&hs
,NIL
);
814 /* need to make an envelope? */
815 if (!*env
) rfc822_parse_msg (env
,NIL
,ht
,hs
,NIL
,BADHOST
,
817 /* need message size too, ugh */
818 if (!elt
->rfc822_size
) {
819 (*stream
->dtb
->text
) (stream
,i
,&bs
,FT_PEEK
);
820 elt
->rfc822_size
= hs
+ SIZE (&bs
) - GETPOS (&bs
);
823 /* if need date, have date in envelope? */
824 if (!elt
->day
&& *env
&& (*env
)->date
)
825 mail_parse_date (elt
,(*env
)->date
);
826 /* sigh, fill in bogus default */
827 if (!elt
->day
) elt
->day
= elt
->month
= 1;
828 mail_free_envelope (&e
);
832 /* POP3 fetch header as text
833 * Accepts: mail stream
835 * pointer to return size
837 * Returns: header text
840 char *pop3_header (MAILSTREAM
*stream
,unsigned long msgno
,unsigned long *size
,
844 char tmp
[MAILTMPLEN
];
847 *size
= 0; /* initially no header size */
848 if ((flags
& FT_UID
) && !(msgno
= mail_msgno (stream
,msgno
))) return "";
849 /* have header text already? */
850 if (!(elt
= mail_elt (stream
,msgno
))->private.msg
.header
.text
.data
) {
851 /* if have CAPA and TOP, assume good TOP */
852 if (!LOCAL
->loser
&& LOCAL
->cap
.top
) {
853 sprintf (tmp
,"TOP %lu 0",mail_uid (stream
,msgno
));
854 if (pop3_send (stream
,tmp
,NIL
))
855 f
= netmsg_slurp (LOCAL
->netstream
,&i
,
856 &elt
->private.msg
.header
.text
.size
);
858 /* otherwise load the cache with the message */
859 else if ((elt
->private.msg
.header
.text
.size
= pop3_cache (stream
,elt
)) != 0L)
861 if (f
) { /* got it, make sure at start of file */
862 fseek (f
,(unsigned long) 0,SEEK_SET
);
863 /* read header from the cache */
864 fread (elt
->private.msg
.header
.text
.data
= (unsigned char *)
865 fs_get ((size_t) elt
->private.msg
.header
.text
.size
+ 1),
866 (size_t) 1,(size_t) elt
->private.msg
.header
.text
.size
,f
);
867 /* tie off header text */
868 elt
->private.msg
.header
.text
.data
[elt
->private.msg
.header
.text
.size
] =
870 /* close if not the cache */
871 if (f
!= LOCAL
->txt
) fclose (f
);
874 /* return size of text */
875 if (size
) *size
= elt
->private.msg
.header
.text
.size
;
876 return elt
->private.msg
.header
.text
.data
?
877 (char *) elt
->private.msg
.header
.text
.data
: "";
881 * Accepts: mail stream
883 * pointer to stringstruct to initialize
885 * Returns: T if successful, else NIL
888 long pop3_text (MAILSTREAM
*stream
,unsigned long msgno
,STRING
*bs
,long flags
)
891 INIT (bs
,mail_string
,(void *) "",0);
892 if ((flags
& FT_UID
) && !(msgno
= mail_msgno (stream
,msgno
))) return NIL
;
893 elt
= mail_elt (stream
,msgno
);
894 pop3_cache (stream
,elt
); /* make sure cache loaded */
895 if (!LOCAL
->txt
) return NIL
; /* error if don't have a file */
896 if (!(flags
& FT_PEEK
)) { /* mark seen if needed */
898 mm_flags (stream
,elt
->msgno
);
900 INIT (bs
,file_string
,(void *) LOCAL
->txt
,elt
->rfc822_size
);
901 SETPOS (bs
,LOCAL
->hdrsize
); /* skip past header */
905 /* POP3 cache message
906 * Accepts: mail stream
908 * Returns: header size
911 unsigned long pop3_cache (MAILSTREAM
*stream
,MESSAGECACHE
*elt
)
913 /* already cached? */
914 if (LOCAL
->cached
!= mail_uid (stream
,elt
->msgno
)) {
915 /* no, close current file */
916 if (LOCAL
->txt
) fclose (LOCAL
->txt
);
918 LOCAL
->cached
= LOCAL
->hdrsize
= 0;
919 if (pop3_send_num (stream
,"RETR",elt
->msgno
) &&
920 (LOCAL
->txt
= netmsg_slurp (LOCAL
->netstream
,&elt
->rfc822_size
,
922 /* set as current message number */
923 LOCAL
->cached
= mail_uid (stream
,elt
->msgno
);
924 else elt
->deleted
= T
;
926 return LOCAL
->hdrsize
;
929 /* POP3 mail ping mailbox
930 * Accepts: MAIL stream
931 * Returns: T if stream alive, else NIL
934 long pop3_ping (MAILSTREAM
*stream
)
936 return pop3_send (stream
,"NOOP",NIL
);
940 /* POP3 mail check mailbox
941 * Accepts: MAIL stream
944 void pop3_check (MAILSTREAM
*stream
)
946 if (pop3_ping (stream
)) mm_log ("Check completed",NIL
);
950 /* POP3 mail expunge mailbox
951 * Accepts: MAIL stream
952 * sequence to expunge if non-NIL
954 * Returns: T if success, NIL if failure
957 long pop3_expunge (MAILSTREAM
*stream
,char *sequence
,long options
)
959 char tmp
[MAILTMPLEN
];
961 unsigned long i
= 1,n
= 0;
963 if ((ret
= sequence
? ((options
& EX_UID
) ?
964 mail_uid_sequence (stream
,sequence
) :
965 mail_sequence (stream
,sequence
)) :
966 LONGT
) != 0L) { /* build selected sequence if needed */
967 while (i
<= stream
->nmsgs
) {
968 elt
= mail_elt (stream
,i
);
969 if (elt
->deleted
&& (sequence
? elt
->sequence
: T
) &&
970 pop3_send_num (stream
,"DELE",i
)) {
971 /* expunging currently cached message? */
972 if (LOCAL
->cached
== mail_uid (stream
,i
)) {
973 /* yes, close current file */
974 if (LOCAL
->txt
) fclose (LOCAL
->txt
);
976 LOCAL
->cached
= LOCAL
->hdrsize
= 0;
978 mail_expunged (stream
,i
);
981 else i
++; /* try next message */
983 if (!stream
->silent
) { /* only if not silent */
984 if (n
) { /* did we expunge anything? */
985 sprintf (tmp
,"Expunged %lu messages",n
);
986 mm_log (tmp
,(long) NIL
);
988 else mm_log ("No messages deleted, so no update needed",(long) NIL
);
994 /* POP3 mail copy message(s)
995 * Accepts: MAIL stream
997 * destination mailbox
999 * Returns: T if copy successful, else NIL
1002 long pop3_copy (MAILSTREAM
*stream
,char *sequence
,char *mailbox
,long options
)
1004 mailproxycopy_t pc
=
1005 (mailproxycopy_t
) mail_parameters (stream
,GET_MAILPROXYCOPY
,NIL
);
1006 if (pc
) return (*pc
) (stream
,sequence
,mailbox
,options
);
1007 mm_log ("Copy not valid for POP3",ERROR
);
1012 /* POP3 mail append message from stringstruct
1013 * Accepts: MAIL stream
1014 * destination mailbox
1017 * Returns: T if append successful, else NIL
1020 long pop3_append (MAILSTREAM
*stream
,char *mailbox
,append_t af
,void *data
)
1022 mm_log ("Append not valid for POP3",ERROR
);
1026 /* Internal routines */
1029 /* Post Office Protocol 3 send command with number argument
1030 * Accepts: MAIL stream
1033 * Returns: T if successful, NIL if failure
1036 long pop3_send_num (MAILSTREAM
*stream
,char *command
,unsigned long n
)
1038 char tmp
[MAILTMPLEN
];
1039 sprintf (tmp
,"%lu",mail_uid (stream
,n
));
1040 return pop3_send (stream
,command
,tmp
);
1044 /* Post Office Protocol 3 send command
1045 * Accepts: MAIL stream
1048 * Returns: T if successful, NIL if failure
1051 long pop3_send (MAILSTREAM
*stream
,char *command
,char *args
)
1054 char *s
= (char *) fs_get (strlen (command
) + (args
? strlen (args
) + 1: 0)
1056 mail_lock (stream
); /* lock up the stream */
1057 if (!LOCAL
->netstream
) ret
= pop3_fake (stream
,"POP3 connection lost");
1058 else { /* build the complete command */
1059 if (args
) sprintf (s
,"%s %s",command
,args
);
1060 else strcpy (s
,command
);
1061 if (stream
->debug
) mail_dlog (s
,LOCAL
->sensitive
);
1062 strcat (s
,"\015\012");
1063 /* send the command */
1064 ret
= net_soutr (LOCAL
->netstream
,s
) ? pop3_reply (stream
) :
1065 pop3_fake (stream
,"POP3 connection broken in command");
1067 fs_give ((void **) &s
);
1068 mail_unlock (stream
); /* unlock stream */
1072 /* Post Office Protocol 3 get reply
1073 * Accepts: MAIL stream
1074 * Returns: T if success reply, NIL if error reply
1077 long pop3_reply (MAILSTREAM
*stream
)
1080 /* flush old reply */
1081 if (LOCAL
->response
) fs_give ((void **) &LOCAL
->response
);
1083 if (!(LOCAL
->response
= net_getline (LOCAL
->netstream
)))
1084 return pop3_fake (stream
,"POP3 connection broken in response");
1085 if (stream
->debug
) mm_dlog (LOCAL
->response
);
1086 LOCAL
->reply
= (s
= strchr (LOCAL
->response
,' ')) ? s
+ 1 : LOCAL
->response
;
1087 /* return success or failure */
1088 return (*LOCAL
->response
=='+') ? T
: NIL
;
1092 /* Post Office Protocol 3 set fake error
1093 * Accepts: MAIL stream
1095 * Returns: NIL, always
1098 long pop3_fake (MAILSTREAM
*stream
,char *text
)
1100 mm_notify (stream
,text
,BYE
); /* send bye alert */
1101 if (LOCAL
->netstream
) net_close (LOCAL
->netstream
);
1102 LOCAL
->netstream
= NIL
; /* farewell, dear TCP stream */
1103 /* flush any old reply */
1104 if (LOCAL
->response
) fs_give ((void **) &LOCAL
->response
);
1105 LOCAL
->reply
= text
; /* set up pseudo-reply string */
1106 return NIL
; /* return error code */