1
/* ========================================================================
2
* Copyright 1988-2006 University of Washington
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
8
* http://www.apache.org/licenses/LICENSE-2.0
11
* ========================================================================
15
* Program: IPOP3D - IMAP to POP3 conversion server
17
* Author: Mark Crispin
18
* Networks and Distributed Computing
19
* Computing & Communications
20
* University of Washington
21
* Administration Building, AG-44
23
* Internet: MRC@CAC.Washington.EDU
25
* Date: 1 November 1990
26
* Last Edited: 18 December 2006
34
extern int errno; /* just in case */
40
#define CRLF PSOUT ("\015\012") /* primary output terpri */
43
/* Autologout timer */
44
#define KODTIMEOUT 60*5
45
#define LOGINTIMEOUT 60*3
49
/* Size of temporary buffers */
55
#define AUTHORIZATION 0
62
#define STATUS "Status: %s%s\015\012"
63
#define SLEN (sizeof (STATUS)-3)
68
char *version = "2006e.96"; /* server version */
69
short state = AUTHORIZATION; /* server state */
70
short critical = NIL; /* non-zero if in critical code */
71
MAILSTREAM *stream = NIL; /* mailbox stream */
72
time_t idletime = 0; /* time we went idle */
73
unsigned long nmsgs = 0; /* current number of messages */
74
unsigned long ndele = 0; /* number of deletes */
75
unsigned long last = 0; /* highest message accessed */
76
unsigned long il = 0; /* initial last message */
77
char challenge[128]; /* challenge */
78
char *host = NIL; /* remote host name */
79
char *user = NIL; /* user name */
80
char *pass = NIL; /* password */
81
char *initial = NIL; /* initial response */
82
long *msg = NIL; /* message translation vector */
83
char *logout = "Logout";
84
char *goodbye = "+OK Sayonara\015\012";
87
/* Function prototypes */
89
int main (int argc,char *argv[]);
90
void sayonara (int status);
95
int pass_login (char *t,int argc,char *argv[]);
96
char *apop_login (char *chal,char *user,char *md5,int argc,char *argv[]);
97
char *responder (void *challenge,unsigned long clen,unsigned long *rlen);
98
int mbxopen (char *mailbox);
99
long blat (char *text,long lines,unsigned long size,STRING *st);
104
int main (int argc,char *argv[])
109
time_t autologouttime;
110
char *pgmname = (argc && argv[0]) ?
111
(((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ?
112
s+1 : argv[0]) : "ipop3d";
113
/* set service name before linkage */
114
mail_parameters (NIL,SET_SERVICENAME,(void *) "pop");
116
/* initialize server */
117
server_init (pgmname,"pop3","pop3s",clkint,kodint,hupint,trmint);
118
{ /* set up MD5 challenge */
119
AUTHENTICATOR *auth = mail_lookup_auth (1);
120
while (auth && compare_cstring (auth->name,"CRAM-MD5")) auth = auth->next;
121
/* build challenge -- less than 128 chars */
122
if (auth && auth->server && !(auth->flags & AU_DISABLE))
123
sprintf (challenge,"<%lx.%lx@%.64s>",(unsigned long) getpid (),
124
(unsigned long) time (0),tcp_serverhost ());
125
else challenge[0] = '\0'; /* no MD5 authentication */
127
/* There are reports of POP3 clients which get upset if anything appears
128
* between the "+OK" and the "POP3" in the greeting.
131
if (!challenge[0]) { /* if no MD5 enable, output host name */
132
PSOUT (tcp_serverhost ());
136
PSOUT (" server ready");
137
if (challenge[0]) { /* if MD5 enable, output challenge here */
142
PFLUSH (); /* dump output buffer */
143
autologouttime = time (0) + LOGINTIMEOUT;
144
/* command processing loop */
145
while ((state != UPDATE) && (state != LOGOUT)) {
146
idletime = time (0); /* get a command under timeout */
147
alarm ((state == TRANSACTION) ? TIMEOUT : LOGINTIMEOUT);
148
clearerr (stdin); /* clear stdin errors */
149
while (!PSIN (tmp,TMPLEN)){ /* read command line */
150
/* ignore if some interrupt */
151
if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
153
char *e = ferror (stdin) ?
154
strerror (errno) : "Command stream end of file";
155
alarm (0); /* disable all interrupts */
156
server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
157
sprintf (logout = tmp,"%.80s, while reading line",strerror (errno));
159
rset (); /* try to gracefully close the stream */
160
if (state == TRANSACTION) mail_close (stream);
166
alarm (0); /* make sure timeout disabled */
167
idletime = 0; /* no longer idle */
169
if (!strchr (tmp,'\012')) /* find end of line */
170
PSOUT ("-ERR Command line too long\015\012");
171
else if (!(s = strtok (tmp," \015\012")))
172
PSOUT ("-ERR Null command\015\012");
173
else { /* dispatch based on command */
174
ucase (s); /* canonicalize case */
176
t = strtok (NIL,"\015\012");
177
/* QUIT command always valid */
178
if (!strcmp (s,"QUIT")) state = UPDATE;
179
else if (!strcmp (s,"CAPA")) {
181
PSOUT ("+OK Capability list follows:\015\012");
182
PSOUT ("TOP\015\012LOGIN-DELAY 180\015\012UIDL\015\012");
183
if (s = ssl_start_tls (NIL)) fs_give ((void **) &s);
184
else PSOUT ("STLS\015\012");
185
if (i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL))
186
PSOUT ("USER\015\012");
187
/* display secure server authenticators */
188
for (auth = mail_lookup_auth (1), s = "SASL"; auth; auth = auth->next)
189
if (auth->server && !(auth->flags & AU_DISABLE) &&
190
!(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
198
PSOUT (s ? ".\015\012" : "\015\012.\015\012");
201
else switch (state) { /* else dispatch based on state */
202
case AUTHORIZATION: /* waiting to get logged in */
203
if (!strcmp (s,"AUTH")) {
204
if (t && *t) { /* mechanism given? */
205
if (host) fs_give ((void **) &host);
206
if (user) fs_give ((void **) &user);
207
if (pass) fs_give ((void **) &pass);
208
s = strtok (t," "); /* get mechanism name */
209
/* get initial response */
210
initial = strtok (NIL,"\015\012");
211
if (!(user = cpystr (mail_auth (s,responder,argc,argv)))) {
212
PSOUT ("-ERR Bad authentication\015\012");
213
syslog (LOG_INFO,"AUTHENTICATE %s failure host=%.80s",s,
216
else if ((state = mbxopen ("INBOX")) == TRANSACTION)
217
syslog (LOG_INFO,"Auth user=%.80s host=%.80s nmsgs=%ld/%ld",
218
user,tcp_clienthost (),nmsgs,stream->nmsgs);
219
else syslog (LOG_INFO,"Auth user=%.80s host=%.80s no mailbox",
220
user,tcp_clienthost ());
224
PSOUT ("+OK Supported authentication mechanisms:\015\012");
225
i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL);
226
for (auth = mail_lookup_auth (1); auth; auth = auth->next)
227
if (auth->server && !(auth->flags & AU_DISABLE) &&
228
!(auth->flags & AU_HIDE) &&
229
(i || (auth->flags & AU_SECURE))) {
238
else if (!strcmp (s,"APOP")) {
239
if (challenge[0]) { /* can do it if have an MD5 challenge */
240
if (host) fs_give ((void **) &host);
241
if (user) fs_give ((void **) &user);
242
if (pass) fs_give ((void **) &pass);
244
if (!(t && *t && (s = strtok (t," ")) && (t = strtok(NIL,"\012"))))
245
PSOUT ("-ERR Missing APOP argument\015\012");
246
else if (!(user = apop_login (challenge,s,t,argc,argv)))
247
PSOUT ("-ERR Bad APOP\015\012");
248
else if ((state = mbxopen ("INBOX")) == TRANSACTION)
249
syslog (LOG_INFO,"APOP user=%.80s host=%.80s nmsgs=%ld/%ld",
250
user,tcp_clienthost (),nmsgs,stream->nmsgs);
251
else syslog (LOG_INFO,"APOP user=%.80s host=%.80s no mailbox",
252
user,tcp_clienthost ());
254
else PSOUT ("-ERR Not supported\015\012");
257
else if (!strcmp (s,"RPOP"))
258
PSOUT ("-ERR Nice try, bunkie\015\012");
259
else if (!strcmp (s,"STLS")) {
260
if (t = ssl_start_tls (pgmname)) {
261
PSOUT ("-ERR STLS failed: ");
265
else PSOUT ("+OK STLS completed\015\012");
267
else if (!mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) &&
268
!strcmp (s,"USER")) {
269
if (host) fs_give ((void **) &host);
270
if (user) fs_give ((void **) &user);
271
if (pass) fs_give ((void **) &pass);
272
if (t && *t) { /* if user name given */
273
/* skip leading whitespace (bogus clients!) */
274
while (*t == ' ') ++t;
275
/* remote user name? */
276
if (s = strchr (t,':')) {
277
*s++ = '\0'; /* tie off host name */
278
host = cpystr (t);/* copy host name */
279
user = cpystr (s);/* copy user name */
281
/* local user name */
282
else user = cpystr (t);
283
PSOUT ("+OK User name accepted, password please\015\012");
285
else PSOUT ("-ERR Missing username argument\015\012");
287
else if (!mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) &&
288
user && *user && !strcmp (s,"PASS"))
289
state = pass_login (t,argc,argv);
290
else PSOUT ("-ERR Unknown AUTHORIZATION state command\015\012");
293
case TRANSACTION: /* logged in */
294
if (!strcmp (s,"STAT")) {
295
for (i = 1,j = 0,k = 0; i <= nmsgs; i++)
296
if (msg[i] > 0) { /* message still exists? */
297
j++; /* count one more undeleted message */
298
k += mail_elt (stream,msg[i])->rfc822_size + SLEN;
300
sprintf (tmp,"+OK %lu %lu\015\012",j,k);
303
else if (!strcmp (s,"LIST")) {
304
if (t && *t) { /* argument do single message */
305
if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) {
306
sprintf (tmp,"+OK %lu %lu\015\012",i,
307
mail_elt(stream,msg[i])->rfc822_size + SLEN);
310
else PSOUT ("-ERR No such message\015\012");
312
else { /* entire mailbox */
313
PSOUT ("+OK Mailbox scan listing follows\015\012");
314
for (i = 1,j = 0,k = 0; i <= nmsgs; i++) if (msg[i] > 0) {
315
sprintf (tmp,"%lu %lu\015\012",i,
316
mail_elt (stream,msg[i])->rfc822_size + SLEN);
319
PBOUT ('.'); /* end of list */
323
else if (!strcmp (s,"UIDL")) {
324
if (t && *t) { /* argument do single message */
325
if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) {
326
sprintf (tmp,"+OK %lu %08lx%08lx\015\012",i,stream->uid_validity,
327
mail_uid (stream,msg[i]));
330
else PSOUT ("-ERR No such message\015\012");
332
else { /* entire mailbox */
333
PSOUT ("+OK Unique-ID listing follows\015\012");
334
for (i = 1,j = 0,k = 0; i <= nmsgs; i++) if (msg[i] > 0) {
335
sprintf (tmp,"%lu %08lx%08lx\015\012",i,stream->uid_validity,
336
mail_uid (stream,msg[i]));
339
PBOUT ('.'); /* end of list */
344
else if (!strcmp (s,"RETR")) {
345
if (t && *t) { /* must have an argument */
346
if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) {
348
/* update highest message accessed */
349
if (i > last) last = i;
350
sprintf (tmp,"+OK %lu octets\015\012",
351
(elt = mail_elt (stream,msg[i]))->rfc822_size + SLEN);
354
t = mail_fetch_header (stream,msg[i],NIL,NIL,&k,FT_PEEK);
355
blat (t,-1,k,NIL);/* write up to trailing CRLF */
357
sprintf (tmp,STATUS,elt->seen ? "R" : " ",
358
elt->recent ? " " : "O");
359
if (k < 4) CRLF; /* don't write Status: if no header */
360
/* normal header ending with CRLF CRLF? */
361
else if (t[k-3] == '\012') {
362
PSOUT (tmp); /* write status */
363
CRLF; /* then write second CRLF */
365
else { /* abnormal - no blank line at end of header */
366
CRLF; /* write CRLF first then */
370
t = mail_fetch_text (stream,msg[i],NIL,&k,
371
FT_RETURNSTRINGSTRUCT);
372
if (k) { /* only if there is a text body */
373
blat (t,-1,k,&stream->private.string);
374
CRLF; /* end of list */
379
else PSOUT ("-ERR No such message\015\012");
381
else PSOUT ("-ERR Missing message number argument\015\012");
383
else if (!strcmp (s,"DELE")) {
384
if (t && *t) { /* must have an argument */
385
if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) {
386
/* update highest message accessed */
387
if (i > last) last = i;
389
sprintf (tmp,"%ld",msg[i]);
390
mail_setflag (stream,tmp,"\\Deleted");
391
msg[i] = -msg[i]; /* note that we deleted this message */
392
PSOUT ("+OK Message deleted\015\012");
393
ndele++; /* one more message deleted */
395
else PSOUT ("-ERR No such message\015\012");
397
else PSOUT ("-ERR Missing message number argument\015\012");
400
else if (!strcmp (s,"NOOP"))
401
PSOUT ("+OK No-op to you too!\015\012");
402
else if (!strcmp (s,"LAST")) {
403
sprintf (tmp,"+OK %lu\015\012",last);
406
else if (!strcmp (s,"RSET")) {
407
rset (); /* reset the mailbox */
408
PSOUT ("+OK Reset state\015\012");
410
else if (!strcmp (s,"TOP")) {
411
if (t && *t && (i =strtoul (t,&s,10)) && (i <= nmsgs) &&
413
/* skip whitespace */
414
while (*s == ' ') s++;
415
/* make sure line count argument good */
416
if ((*s >= '0') && (*s <= '9')) {
417
MESSAGECACHE *elt = mail_elt (stream,msg[i]);
418
j = strtoul (s,NIL,10);
419
/* update highest message accessed */
420
if (i > last) last = i;
421
PSOUT ("+OK Top of message follows\015\012");
423
t = mail_fetch_header (stream,msg[i],NIL,NIL,&k,FT_PEEK);
424
blat (t,-1,k,NIL);/* write up to trailing CRLF */
426
sprintf (tmp,STATUS,elt->seen ? "R" : " ",
427
elt->recent ? " " : "O");
428
if (k < 4) CRLF; /* don't write Status: if no header */
429
/* normal header ending with CRLF CRLF? */
430
else if (t[k-3] == '\012') {
431
PSOUT (tmp); /* write status */
432
CRLF; /* then write second CRLF */
434
else { /* abnormal - no blank line at end of header */
435
CRLF; /* write CRLF first then */
438
if (j) { /* want any text lines? */
440
t = mail_fetch_text (stream,msg[i],NIL,&k,
441
FT_PEEK | FT_RETURNSTRINGSTRUCT);
442
/* tie off final line if full text output */
443
if (k && (j -= blat (t,j,k,&stream->private.string))) CRLF;
445
PBOUT ('.'); /* end of list */
448
else PSOUT ("-ERR Bad line count argument\015\012");
450
else PSOUT ("-ERR Bad message number argument\015\012");
452
else if (!strcmp (s,"XTND"))
453
PSOUT ("-ERR Sorry I can't do that\015\012");
454
else PSOUT ("-ERR Unknown TRANSACTION state command\015\012");
457
PSOUT ("-ERR Server in unknown state\015\012");
461
PFLUSH (); /* make sure output finished */
462
if (autologouttime) { /* have an autologout in effect? */
463
/* cancel if no longer waiting for login */
464
if (state != AUTHORIZATION) autologouttime = 0;
465
/* took too long to login */
466
else if (autologouttime < time (0)) {
467
goodbye = "-ERR Autologout\015\012";
468
logout = "Autologout";
469
state = LOGOUT; /* sayonara */
473
if (stream && (state == UPDATE)) {
474
mail_expunge (stream);
475
syslog (LOG_INFO,"Update user=%.80s host=%.80s nmsgs=%ld ndele=%ld",
476
user,tcp_clienthost (),stream->nmsgs,ndele);
480
return 0; /* stupid compilers */
485
* Accepts: exit status
490
void sayonara (int status)
492
logouthook_t lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL);
493
if (goodbye) { /* have a goodbye message? */
495
PFLUSH (); /* make sure blatted */
497
syslog (LOG_INFO,"%s user=%.80s host=%.80s",logout,
498
user ? (char *) user : "???",tcp_clienthost ());
499
/* do logout hook if needed */
500
if (lgoh) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL));
501
_exit (status); /* all done */
509
alarm (0); /* disable all interrupts */
510
server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
511
goodbye = "-ERR Autologout; idle for too long\015\012";
512
logout = "Autologout";
513
if (critical) state = LOGOUT; /* badly hosed if in critical code */
514
else { /* try to gracefully close the stream */
515
if ((state == TRANSACTION) && !stream->lock) {
526
/* Kiss Of Death interrupt
532
if (idletime && ((time (0) - idletime) > KODTIMEOUT)) {
533
alarm (0); /* disable all interrupts */
534
server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
535
goodbye = "-ERR Received Kiss of Death\015\012";
536
logout = "Killed (lost mailbox lock)";
537
if (critical) state =LOGOUT;/* must defer if in critical code */
538
else { /* try to gracefully close the stream */
539
if ((state == TRANSACTION) && !stream->lock) {
545
sayonara (1); /* die die die */
556
alarm (0); /* disable all interrupts */
557
server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
558
goodbye = NIL; /* nobody left to talk to */
560
if (critical) state = LOGOUT; /* must defer if in critical code */
561
else { /* try to gracefully close the stream */
562
if ((state == TRANSACTION) && !stream->lock) {
568
sayonara (1); /* die die die */
573
/* Termination interrupt
578
alarm (0); /* disable all interrupts */
579
server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
580
goodbye = "-ERR Killed\015\012";
582
if (critical) state = LOGOUT; /* must defer if in critical code */
583
/* Make no attempt at graceful closure since a shutdown may be in
584
* progress, and we won't have any time to do mail_close() actions.
586
else sayonara (1); /* die die die */
589
/* Parse PASS command
590
* Accepts: pointer to command argument
594
int pass_login (char *t,int argc,char *argv[])
597
/* flush old passowrd */
598
if (pass) fs_give ((void **) &pass);
599
if (!(t && *t)) { /* if no password given */
600
PSOUT ("-ERR Missing password argument\015\012");
601
return AUTHORIZATION;
603
pass = cpystr (t); /* copy password argument */
604
if (!host) { /* want remote mailbox? */
605
/* no, delimit user from possible admin */
606
if (t = strchr (user,'*')) *t++ ='\0';
607
/* attempt the login */
608
if (server_login (user,pass,t,argc,argv)) {
609
int ret = mbxopen ("INBOX");
610
if (ret == TRANSACTION) /* mailbox opened OK? */
611
syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s nmsgs=%ld/%ld",
612
t ? "Admin " : "",user,tcp_clienthost (),nmsgs,stream->nmsgs);
613
else syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s no mailbox",
614
t ? "Admin " : "",user,tcp_clienthost ());
618
#ifndef DISABLE_POP_PROXY
619
/* remote; build remote INBOX */
620
else if (anonymous_login (argc,argv)) {
621
syslog (LOG_INFO,"IMAP login to host=%.80s user=%.80s host=%.80s",host,
622
user,tcp_clienthost ());
623
sprintf (tmp,"{%.128s/user=%.128s}INBOX",host,user);
624
/* disable rimap just in case */
625
mail_parameters (NIL,SET_RSHTIMEOUT,0);
626
return mbxopen (tmp);
629
/* vague error message to confuse crackers */
630
PSOUT ("-ERR Bad login\015\012");
631
return AUTHORIZATION;
634
/* Authentication responder
636
* length of challenge
637
* pointer to response length return location if non-NIL
641
#define RESPBUFLEN 8*MAILTMPLEN
643
char *responder (void *challenge,unsigned long clen,unsigned long *rlen)
646
unsigned char *t,resp[RESPBUFLEN];
647
char tmp[MAILTMPLEN];
648
if (initial) { /* initial response given? */
649
if (clen) return NIL; /* not permitted */
650
/* set up response */
651
t = (unsigned char *) initial;
652
initial = NIL; /* no more initial response */
653
return (char *) rfc822_base64 (t,strlen ((char *) t),rlen ? rlen : &i);
656
for (t = rfc822_binary (challenge,clen,&i),j = 0; j < i; j++)
657
if (t[j] > ' ') PBOUT (t[j]);
658
fs_give ((void **) &t);
660
PFLUSH (); /* dump output buffer */
661
resp[RESPBUFLEN-1] = '\0'; /* last buffer character is guaranteed NUL */
662
alarm (LOGINTIMEOUT); /* get a response under timeout */
663
clearerr (stdin); /* clear stdin errors */
665
while (!PSIN ((char *) resp,RESPBUFLEN)) {
666
/* ignore if some interrupt */
667
if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
669
char *e = ferror (stdin) ?
670
strerror (errno) : "Command stream end of file";
671
alarm (0); /* disable all interrupts */
672
server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
673
sprintf (logout = tmp,"%.80s, while reading authentication",e);
679
if (!(t = (unsigned char *) strchr ((char *) resp,'\012'))) {
681
while ((c = PBIN ()) != '\012') if (c == EOF) {
682
/* ignore if some interrupt */
683
if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
685
char *e = ferror (stdin) ?
686
strerror (errno) : "Command stream end of file";
687
alarm (0); /* disable all interrupts */
688
server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
689
sprintf (logout = tmp,"%.80s, while reading auth char",e);
697
alarm (0); /* make sure timeout disabled */
698
if (t[-1] == '\015') --t; /* remove CR */
699
*t = '\0'; /* tie off buffer */
700
return (resp[0] != '*') ?
701
(char *) rfc822_base64 (resp,t-resp,rlen ? rlen : &i) : NIL;
705
* Accepts: mailbox name
709
int mbxopen (char *mailbox)
714
nmsgs = 0; /* no messages yet */
715
if (msg) fs_give ((void **) &msg);
717
if (stream = mail_open (stream,mailbox,NIL)) {
718
if (!stream->rdonly) { /* make sure not readonly */
719
if (j = stream->nmsgs) { /* if mailbox non-empty */
720
sprintf (tmp,"1:%lu",j);/* fetch fast information for all messages */
721
mail_fetch_fast (stream,tmp,NIL);
722
msg = (long *) fs_get ((stream->nmsgs + 1) * sizeof (long));
723
for (i = 1; i <= j; i++) if (!(elt = mail_elt (stream,i))->deleted) {
724
msg[++nmsgs] = i; /* note the presence of this message */
725
if (elt->seen) il = last = nmsgs;
728
sprintf (tmp,"+OK Mailbox open, %lu messages\015\012",nmsgs);
732
else goodbye = "-ERR Can't get lock. Mailbox in use\015\012";
734
else goodbye = "-ERR Unable to open user's INBOX\015\012";
735
syslog (LOG_INFO,"Error opening or locking INBOX user=%.80s host=%.80s",
736
user,tcp_clienthost ());
740
/* Blat a string with dot checking
742
* maximum number of lines if greater than zero
743
* maximum number of bytes to output
744
* alternative stringstruct
745
* Returns: number of lines output
747
* This routine is uglier and kludgier than it should be, just to be robust
748
* in the case of a message which doesn't end in a newline. Yes, this routine
749
* does truncate the last two bytes from the text. Since it is normally a
750
* newline and the main routine adds it back, it usually does not make a
751
* difference. But if it isn't, since the newline is required and the octet
752
* counts have to match, there's no choice but to truncate.
755
long blat (char *text,long lines,unsigned long size,STRING *st)
759
/* no-op if zero lines or empty string */
760
if (!(lines && (size-- > 2))) return 0;
762
c = *text++; d = *text++; /* collect first two bytes */
763
if (c == '.') PBOUT ('.'); /* double string-leading dot if necessary */
764
while (lines && --size) { /* copy loop */
765
e = *text++; /* get next byte */
766
PBOUT (c); /* output character */
767
if (c == '\012') { /* end of line? */
768
ret++; --lines; /* count another line */
769
/* double leading dot as necessary */
770
if (lines && size && (d == '.')) PBOUT ('.');
772
c = d; d = e; /* move to next character */
776
c = SNX (st); d = SNX (st); /* collect first two bytes */
777
if (c == '.') PBOUT ('.'); /* double string-leading dot if necessary */
778
while (lines && --size) { /* copy loop */
779
e = SNX (st); /* get next byte */
780
PBOUT (c); /* output character */
781
if (c == '\012') { /* end of line? */
782
ret++; --lines; /* count another line */
783
/* double leading dot as necessary */
784
if (lines && size && (d == '.')) PBOUT ('.');
786
c = d; d = e; /* move to next character */
799
if (nmsgs) { /* undelete and unmark all of our messages */
800
for (i = 1; i <= nmsgs; i++) { /* */
801
if (msg[i] < 0) { /* ugly and inefficient, but trustworthy */
802
sprintf (tmp,"%ld",msg[i] = -msg[i]);
803
mail_clearflag (stream,tmp,i <= il ? "\\Deleted" : "\\Deleted \\Seen");
806
sprintf (tmp,"%ld",msg[i]);
807
mail_clearflag (stream,tmp,"\\Seen");
812
ndele = 0; /* no more deleted messages */
815
/* Co-routines from MAIL library */
818
/* Message matches a search
819
* Accepts: MAIL stream
823
void mm_searched (MAILSTREAM *stream,unsigned long msgno)
829
/* Message exists (i.e. there are that many messages in the mailbox)
830
* Accepts: MAIL stream
834
void mm_exists (MAILSTREAM *stream,unsigned long number)
836
/* Can't use this mechanism. POP has no means of notifying the client of
837
new mail during the session. */
842
* Accepts: MAIL stream
846
void mm_expunged (MAILSTREAM *stream,unsigned long number)
848
unsigned long i = number + 1;
849
msg[number] = 0; /* I bet that this will annoy someone */
850
while (i <= nmsgs) --msg[i++];
854
/* Message flag status change
855
* Accepts: MAIL stream
859
void mm_flags (MAILSTREAM *stream,unsigned long number)
861
/* This isn't used */
866
* Accepts: MAIL stream
867
* hierarchy delimiter
872
void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
874
/* This isn't used */
878
/* Subscribe mailbox found
879
* Accepts: MAIL stream
880
* hierarchy delimiter
885
void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
887
/* This isn't used */
892
* Accepts: MAIL stream
897
void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
899
/* This isn't used */
902
/* Notification event
903
* Accepts: MAIL stream
908
void mm_notify (MAILSTREAM *stream,char *string,long errflg)
910
mm_log (string,errflg); /* just do mm_log action */
914
/* Log an event for the user to see
915
* Accepts: string to log
919
void mm_log (char *string,long errflg)
922
case NIL: /* information message */
923
case PARSE: /* parse glitch */
924
break; /* too many of these to log */
925
case WARN: /* warning */
926
syslog (LOG_DEBUG,"%s",string);
928
case BYE: /* driver broke connection */
929
if (state != UPDATE) {
930
char tmp[MAILTMPLEN];
931
alarm (0); /* disable all interrupts */
932
server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
933
sprintf (logout = tmp,"Mailbox closed (%.80s)",string);
939
case ERROR: /* error that broke command */
940
default: /* default should never happen */
941
syslog (LOG_NOTICE,"%s",string);
947
/* Log an event to debugging telemetry
948
* Accepts: string to log
951
void mm_dlog (char *string)
953
/* Not doing anything here for now */
957
/* Get user name and password for this host
958
* Accepts: parse of network mailbox name
959
* where to return user name
960
* where to return password
964
void mm_login (NETMBX *mb,char *username,char *password,long trial)
967
strncpy (username,*mb->user ? mb->user : user,NETMAXUSER-1);
969
strncpy (password,pass,255);/* and password */
970
fs_give ((void **) &pass);
972
else memset (password,0,256); /* no password to send, abort login */
973
username[NETMAXUSER] = password[255] = '\0';
976
/* About to enter critical code
980
void mm_critical (MAILSTREAM *stream)
986
/* About to exit critical code
990
void mm_nocritical (MAILSTREAM *stream)
999
* flag indicating that mailbox may be clobbered
1000
* Returns: abort flag
1003
long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
1005
if (serious) { /* try your damnest if clobberage likely */
1007
"Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
1008
user,tcp_clienthost (),
1009
(stream && stream->mailbox) ? stream->mailbox : "???",
1010
strerror (errcode));
1011
alarm (0); /* make damn sure timeout disabled */
1012
sleep (60); /* give it some time to clear up */
1015
syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
1016
user,tcp_clienthost (),
1017
(stream && stream->mailbox) ? stream->mailbox : "???",
1018
strerror (errcode));
1023
/* Log a fatal error event
1024
* Accepts: string to log
1027
void mm_fatal (char *string)
1029
mm_log (string,ERROR); /* shouldn't happen normally */