1 /* ========================================================================
2 * Copyright 1988-2008 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
19 * University of Washington
21 * Internet: MRC@Washington.EDU
23 * Date: 1 November 1990
24 * Last Edited: 19 February 2008
32 extern int errno
; /* just in case */
38 #define CRLF PSOUT ("\015\012") /* primary output terpri */
41 /* Autologout timer */
42 #define KODTIMEOUT 60*5
43 #define LOGINTIMEOUT 60*3
49 #define AUTHORIZATION 0
56 #define STATUS "Status: %s%s\015\012"
57 #define SLEN (sizeof (STATUS)-3)
62 char *version
= "104"; /* edit number of this server */
63 short state
= AUTHORIZATION
; /* server state */
64 short critical
= NIL
; /* non-zero if in critical code */
65 MAILSTREAM
*stream
= NIL
; /* mailbox stream */
66 time_t idletime
= 0; /* time we went idle */
67 unsigned long nmsgs
= 0; /* current number of messages */
68 unsigned long ndele
= 0; /* number of deletes */
69 unsigned long nseen
= 0; /* number of mark-seens */
70 unsigned long last
= 0; /* highest message accessed */
71 unsigned long il
= 0; /* initial last message */
72 char challenge
[128]; /* challenge */
73 char *host
= NIL
; /* remote host name */
74 char *user
= NIL
; /* user name */
75 char *pass
= NIL
; /* password */
76 char *initial
= NIL
; /* initial response */
77 long *msg
= NIL
; /* message translation vector */
78 short *flags
= NIL
; /* flags */
79 char *logout
= "Logout";
80 char *goodbye
= "+OK Sayonara\015\012";
89 /* Function prototypes */
91 int main (int argc
,char *argv
[]);
92 void sayonara (int status
);
97 int pass_login (char *t
,int argc
,char *argv
[]);
98 char *apop_login (char *chal
,char *user
,char *md5
,int argc
,char *argv
[]);
99 char *responder (void *challenge
,unsigned long clen
,unsigned long *rlen
);
100 int mbxopen (char *mailbox
);
101 long blat (char *text
,long lines
,unsigned long size
,STRING
*st
);
106 int main (int argc
,char *argv
[])
110 char tmp
[MAILTMPLEN
];
111 time_t autologouttime
;
112 char *pgmname
= (argc
&& argv
[0]) ?
113 (((s
= strrchr (argv
[0],'/')) || (s
= strrchr (argv
[0],'\\'))) ?
114 s
+1 : argv
[0]) : "ipop3d";
115 /* set service name before linkage */
116 mail_parameters (NIL
,SET_SERVICENAME
,(void *) "pop");
118 /* initialize server */
119 server_init (pgmname
,"pop3","pop3s",clkint
,kodint
,hupint
,trmint
,NIL
);
120 mail_parameters (NIL
,SET_BLOCKENVINIT
,VOIDT
);
121 s
= myusername_full (&i
); /* get user name and flags */
122 mail_parameters (NIL
,SET_BLOCKENVINIT
,NIL
);
123 if (i
== MU_LOGGEDIN
) { /* allow EXTERNAL if logged in already */
124 mail_parameters (NIL
,UNHIDE_AUTHENTICATOR
,(void *) "EXTERNAL");
125 mail_parameters (NIL
,SET_EXTERNALAUTHID
,(void *) s
);
127 { /* set up MD5 challenge */
128 AUTHENTICATOR
*auth
= mail_lookup_auth (1);
129 while (auth
&& compare_cstring (auth
->name
,"CRAM-MD5")) auth
= auth
->next
;
130 /* build challenge -- less than 128 chars */
131 if (auth
&& auth
->server
&& !(auth
->flags
& AU_DISABLE
))
132 sprintf (challenge
,"<%lx.%lx@%.64s>",(unsigned long) getpid (),
133 (unsigned long) time (0),tcp_serverhost ());
134 else challenge
[0] = '\0'; /* no MD5 authentication */
136 /* There are reports of POP3 clients which get upset if anything appears
137 * between the "+OK" and the "POP3" in the greeting.
140 if (!challenge
[0]) { /* if no MD5 enable, output host name */
141 PSOUT (tcp_serverhost ());
144 PSOUT (CCLIENTVERSION
);
147 PSOUT (" server ready");
148 if (challenge
[0]) { /* if MD5 enable, output challenge here */
153 PFLUSH (); /* dump output buffer */
154 autologouttime
= time (0) + LOGINTIMEOUT
;
155 /* command processing loop */
156 while ((state
!= UPDATE
) && (state
!= LOGOUT
)) {
157 idletime
= time (0); /* get a command under timeout */
158 alarm ((state
== TRANSACTION
) ? TIMEOUT
: LOGINTIMEOUT
);
159 clearerr (stdin
); /* clear stdin errors */
160 /* read command line */
161 while (!PSIN (tmp
,MAILTMPLEN
)) {
162 /* ignore if some interrupt */
163 if (ferror (stdin
) && (errno
== EINTR
)) clearerr (stdin
);
165 char *e
= ferror (stdin
) ?
166 strerror (errno
) : "Unexpected client disconnect";
167 alarm (0); /* disable all interrupts */
168 server_init (NIL
,NIL
,NIL
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
);
169 sprintf (logout
= tmp
,"%.80s, while reading line",e
);
171 rset (); /* try to gracefully close the stream */
172 if (state
== TRANSACTION
) mail_close (stream
);
178 alarm (0); /* make sure timeout disabled */
179 idletime
= 0; /* no longer idle */
181 if (!strchr (tmp
,'\012')) /* find end of line */
182 PSOUT ("-ERR Command line too long\015\012");
183 else if (!(s
= strtok (tmp
," \015\012")))
184 PSOUT ("-ERR Null command\015\012");
185 else { /* dispatch based on command */
186 ucase (s
); /* canonicalize case */
188 t
= strtok (NIL
,"\015\012");
189 /* QUIT command always valid */
190 if (!strcmp (s
,"QUIT")) state
= UPDATE
;
191 else if (!strcmp (s
,"CAPA")) {
193 PSOUT ("+OK Capability list follows:\015\012");
194 PSOUT ("TOP\015\012LOGIN-DELAY 180\015\012UIDL\015\012");
195 if ((s
= ssl_start_tls (NIL
)) != NULL
) fs_give ((void **) &s
);
196 else PSOUT ("STLS\015\012");
197 if ((i
= !mail_parameters (NIL
,GET_DISABLEPLAINTEXT
,NIL
)) != 0L)
198 PSOUT ("USER\015\012");
199 /* display secure server authenticators */
200 for (auth
= mail_lookup_auth (1), s
= "SASL"; auth
; auth
= auth
->next
)
201 if (auth
->server
&& !(auth
->flags
& AU_DISABLE
) &&
202 !(auth
->flags
& AU_HIDE
) && (i
|| (auth
->flags
& AU_SECURE
))) {
210 PSOUT (s
? ".\015\012" : "\015\012.\015\012");
213 else switch (state
) { /* else dispatch based on state */
214 case AUTHORIZATION
: /* waiting to get logged in */
215 if (!strcmp (s
,"AUTH")) {
216 if (t
&& *t
) { /* mechanism given? */
217 if (host
) fs_give ((void **) &host
);
218 if (user
) fs_give ((void **) &user
);
219 if (pass
) fs_give ((void **) &pass
);
220 s
= strtok (t
," "); /* get mechanism name */
221 /* get initial response */
222 if ((initial
= strtok (NIL
,"\015\012")) != NULL
) {
223 if ((*initial
== '=') && !initial
[1]) ++initial
;
224 else if (!*initial
) initial
= NIL
;
226 if (!(user
= cpystr (mail_auth (s
,responder
,argc
,argv
)))) {
227 PSOUT ("-ERR Bad authentication\015\012");
228 syslog (LOG_INFO
,"AUTHENTICATE %s failure host=%.80s",s
,
231 else if ((state
= mbxopen ("INBOX")) == TRANSACTION
)
232 syslog (LOG_INFO
,"Auth user=%.80s host=%.80s nmsgs=%lu/%lu",
233 user
,tcp_clienthost (),nmsgs
,stream
->nmsgs
);
234 else syslog (LOG_INFO
,"Auth user=%.80s host=%.80s no mailbox",
235 user
,tcp_clienthost ());
239 PSOUT ("+OK Supported authentication mechanisms:\015\012");
240 i
= !mail_parameters (NIL
,GET_DISABLEPLAINTEXT
,NIL
);
241 for (auth
= mail_lookup_auth (1); auth
; auth
= auth
->next
)
242 if (auth
->server
&& !(auth
->flags
& AU_DISABLE
) &&
243 !(auth
->flags
& AU_HIDE
) &&
244 (i
|| (auth
->flags
& AU_SECURE
))) {
253 else if (!strcmp (s
,"APOP")) {
254 if (challenge
[0]) { /* can do it if have an MD5 challenge */
255 if (host
) fs_give ((void **) &host
);
256 if (user
) fs_give ((void **) &user
);
257 if (pass
) fs_give ((void **) &pass
);
259 if (!(t
&& *t
&& (s
= strtok (t
," ")) && (t
= strtok(NIL
,"\012"))))
260 PSOUT ("-ERR Missing APOP argument\015\012");
261 else if (!(user
= apop_login (challenge
,s
,t
,argc
,argv
)))
262 PSOUT ("-ERR Bad APOP\015\012");
263 else if ((state
= mbxopen ("INBOX")) == TRANSACTION
)
264 syslog (LOG_INFO
,"APOP user=%.80s host=%.80s nmsgs=%lu/%lu",
265 user
,tcp_clienthost (),nmsgs
,stream
->nmsgs
);
266 else syslog (LOG_INFO
,"APOP user=%.80s host=%.80s no mailbox",
267 user
,tcp_clienthost ());
269 else PSOUT ("-ERR Not supported\015\012");
272 else if (!strcmp (s
,"RPOP"))
273 PSOUT ("-ERR Nice try, bunkie\015\012");
274 else if (!strcmp (s
,"STLS")) {
275 if ((t
= ssl_start_tls (pgmname
)) != NULL
) {
276 PSOUT ("-ERR STLS failed: ");
280 else PSOUT ("+OK STLS completed\015\012");
282 else if (!mail_parameters (NIL
,GET_DISABLEPLAINTEXT
,NIL
) &&
283 !strcmp (s
,"USER")) {
284 if (host
) fs_give ((void **) &host
);
285 if (user
) fs_give ((void **) &user
);
286 if (pass
) fs_give ((void **) &pass
);
287 if (t
&& *t
) { /* if user name given */
288 /* skip leading whitespace (bogus clients!) */
289 while (*t
== ' ') ++t
;
290 /* remote user name? */
291 if ((s
= strchr (t
,':')) != NULL
) {
292 *s
++ = '\0'; /* tie off host name */
293 host
= cpystr (t
);/* copy host name */
294 user
= cpystr (s
);/* copy user name */
296 /* local user name */
297 else user
= cpystr (t
);
298 PSOUT ("+OK User name accepted, password please\015\012");
300 else PSOUT ("-ERR Missing username argument\015\012");
302 else if (!mail_parameters (NIL
,GET_DISABLEPLAINTEXT
,NIL
) &&
303 user
&& *user
&& !strcmp (s
,"PASS"))
304 state
= pass_login (t
,argc
,argv
);
305 else PSOUT ("-ERR Unknown AUTHORIZATION state command\015\012");
308 case TRANSACTION
: /* logged in */
309 if (!strcmp (s
,"STAT")) {
310 for (i
= 1,j
= 0,k
= 0; i
<= nmsgs
; i
++)
311 /* message still exists? */
312 if (msg
[i
] && !(flags
[i
] & DELE
)) {
313 j
++; /* count one more undeleted message */
314 k
+= mail_elt (stream
,msg
[i
])->rfc822_size
+ SLEN
;
316 sprintf (tmp
,"+OK %lu %lu\015\012",j
,k
);
319 else if (!strcmp (s
,"LIST")) {
320 if (t
&& *t
) { /* argument do single message */
321 if ((i
= strtoul (t
,NIL
,10)) && (i
<= nmsgs
) && msg
[i
] &&
322 !(flags
[i
] & DELE
)) {
323 sprintf (tmp
,"+OK %lu %lu\015\012",i
,
324 mail_elt(stream
,msg
[i
])->rfc822_size
+ SLEN
);
327 else PSOUT ("-ERR No such message\015\012");
329 else { /* entire mailbox */
330 PSOUT ("+OK Mailbox scan listing follows\015\012");
331 for (i
= 1,j
= 0,k
= 0; i
<= nmsgs
; i
++)
332 if (msg
[i
] && !(flags
[i
] & DELE
)) {
333 sprintf (tmp
,"%lu %lu\015\012",i
,
334 mail_elt (stream
,msg
[i
])->rfc822_size
+ SLEN
);
337 PBOUT ('.'); /* end of list */
341 else if (!strcmp (s
,"UIDL")) {
342 if (t
&& *t
) { /* argument do single message */
343 if ((i
= strtoul (t
,NIL
,10)) && (i
<= nmsgs
) && msg
[i
] &&
344 !(flags
[i
] & DELE
)) {
345 sprintf (tmp
,"+OK %lu %08lx%08lx\015\012",i
,stream
->uid_validity
,
346 mail_uid (stream
,msg
[i
]));
349 else PSOUT ("-ERR No such message\015\012");
351 else { /* entire mailbox */
352 PSOUT ("+OK Unique-ID listing follows\015\012");
353 for (i
= 1,j
= 0,k
= 0; i
<= nmsgs
; i
++)
354 if (msg
[i
] && !(flags
[i
] & DELE
)) {
355 sprintf (tmp
,"%lu %08lx%08lx\015\012",i
,stream
->uid_validity
,
356 mail_uid (stream
,msg
[i
]));
359 PBOUT ('.'); /* end of list */
364 else if (!strcmp (s
,"RETR")) {
365 if (t
&& *t
) { /* must have an argument */
366 if ((i
= strtoul (t
,NIL
,10)) && (i
<= nmsgs
) && msg
[i
] &&
367 !(flags
[i
] & DELE
)) {
369 /* update highest message accessed */
370 if (i
> last
) last
= i
;
371 sprintf (tmp
,"+OK %lu octets\015\012",
372 (elt
= mail_elt (stream
,msg
[i
]))->rfc822_size
+ SLEN
);
374 /* if not marked seen or noted to be marked */
375 if (!(elt
->seen
|| (flags
[i
] & SEEN
))) {
376 ++nseen
; /* note that we need to mark it seen */
380 t
= mail_fetch_header (stream
,msg
[i
],NIL
,NIL
,&k
,FT_PEEK
);
381 blat (t
,-1,k
,NIL
);/* write up to trailing CRLF */
383 sprintf (tmp
,STATUS
,elt
->seen
? "R" : " ",
384 elt
->recent
? " " : "O");
385 if (k
< 4) CRLF
; /* don't write Status: if no header */
386 /* normal header ending with CRLF CRLF? */
387 else if (t
[k
-3] == '\012') {
388 PSOUT (tmp
); /* write status */
389 CRLF
; /* then write second CRLF */
391 else { /* abnormal - no blank line at end of header */
392 CRLF
; /* write CRLF first then */
396 t
= mail_fetch_text (stream
,msg
[i
],NIL
,&k
,
397 FT_RETURNSTRINGSTRUCT
| FT_PEEK
);
398 if (k
) { /* only if there is a text body */
399 blat (t
,-1,k
,&stream
->private.string
);
400 CRLF
; /* end of list */
405 else PSOUT ("-ERR No such message\015\012");
407 else PSOUT ("-ERR Missing message number argument\015\012");
410 else if (!strcmp (s
,"DELE")) {
411 if (t
&& *t
) { /* must have an argument */
412 if ((i
= strtoul (t
,NIL
,10)) && (i
<= nmsgs
) && msg
[i
] &&
413 !(flags
[i
] & DELE
)) {
414 /* update highest message accessed */
415 if (i
> last
) last
= i
;
416 flags
[i
] |= DELE
; /* note that deletion is requested */
417 PSOUT ("+OK Message deleted\015\012");
418 ++ndele
; /* one more message deleted */
420 else PSOUT ("-ERR No such message\015\012");
422 else PSOUT ("-ERR Missing message number argument\015\012");
424 else if (!strcmp (s
,"NOOP"))
425 PSOUT ("+OK No-op to you too!\015\012");
426 else if (!strcmp (s
,"LAST")) {
427 sprintf (tmp
,"+OK %lu\015\012",last
);
430 else if (!strcmp (s
,"RSET")) {
431 rset (); /* reset the mailbox */
432 PSOUT ("+OK Reset state\015\012");
435 else if (!strcmp (s
,"TOP")) {
436 if (t
&& *t
&& (i
=strtoul (t
,&s
,10)) && (i
<= nmsgs
) && msg
[i
] &&
437 !(flags
[i
] & DELE
)) {
438 /* skip whitespace */
439 while (*s
== ' ') s
++;
440 /* make sure line count argument good */
441 if ((*s
>= '0') && (*s
<= '9')) {
442 MESSAGECACHE
*elt
= mail_elt (stream
,msg
[i
]);
443 j
= strtoul (s
,NIL
,10);
444 /* update highest message accessed */
445 if (i
> last
) last
= i
;
446 PSOUT ("+OK Top of message follows\015\012");
448 t
= mail_fetch_header (stream
,msg
[i
],NIL
,NIL
,&k
,FT_PEEK
);
449 blat (t
,-1,k
,NIL
);/* write up to trailing CRLF */
451 sprintf (tmp
,STATUS
,elt
->seen
? "R" : " ",
452 elt
->recent
? " " : "O");
453 if (k
< 4) CRLF
; /* don't write Status: if no header */
454 /* normal header ending with CRLF CRLF? */
455 else if (t
[k
-3] == '\012') {
456 PSOUT (tmp
); /* write status */
457 CRLF
; /* then write second CRLF */
459 else { /* abnormal - no blank line at end of header */
460 CRLF
; /* write CRLF first then */
463 if (j
) { /* want any text lines? */
465 t
= mail_fetch_text (stream
,msg
[i
],NIL
,&k
,
466 FT_PEEK
| FT_RETURNSTRINGSTRUCT
);
467 /* tie off final line if full text output */
468 if (k
&& (j
-= blat (t
,j
,k
,&stream
->private.string
))) CRLF
;
470 PBOUT ('.'); /* end of list */
473 else PSOUT ("-ERR Bad line count argument\015\012");
475 else PSOUT ("-ERR Bad message number argument\015\012");
478 else if (!strcmp (s
,"XTND"))
479 PSOUT ("-ERR Sorry I can't do that\015\012");
480 else PSOUT ("-ERR Unknown TRANSACTION state command\015\012");
483 PSOUT ("-ERR Server in unknown state\015\012");
487 PFLUSH (); /* make sure output finished */
488 if (autologouttime
) { /* have an autologout in effect? */
489 /* cancel if no longer waiting for login */
490 if (state
!= AUTHORIZATION
) autologouttime
= 0;
491 /* took too long to login */
492 else if (autologouttime
< time (0)) {
493 goodbye
= "-ERR Autologout\015\012";
494 logout
= "Autologout";
495 state
= LOGOUT
; /* sayonara */
500 /* open and need to update? */
501 if (stream
&& (state
== UPDATE
)) {
502 if (nseen
) { /* only bother if messages need marking seen */
503 *(s
= tmp
) = '\0'; /* clear sequence */
504 for (i
= 1; i
<= nmsgs
; ++i
) if (flags
[i
] & SEEN
) {
505 for (j
= i
+ 1, k
= 0; (j
<= nmsgs
) && (flags
[j
] & SEEN
); ++j
) k
= j
;
506 if (k
) sprintf (s
,",%lu:%lu",i
,k
);
507 else sprintf (s
,",%lu",i
);
508 s
+= strlen (s
); /* point to end of string */
509 if ((s
- tmp
) > (MAILTMPLEN
- 30)) {
510 mail_setflag (stream
,tmp
+ 1,"\\Seen");
511 *(s
= tmp
) = '\0'; /* restart sequence */
513 i
= j
; /* continue after the range */
515 if (tmp
[0]) mail_setflag (stream
,tmp
+ 1,"\\Seen");
517 if (ndele
) { /* any messages to delete? */
518 *(s
= tmp
) = '\0'; /* clear sequence */
519 for (i
= 1; i
<= nmsgs
; ++i
) if (flags
[i
] & DELE
) {
520 for (j
= i
+ 1, k
= 0; (j
<= nmsgs
) && (flags
[j
] & DELE
); ++j
) k
= j
;
521 if (k
) sprintf (s
,",%lu:%lu",i
,k
);
522 else sprintf (s
,",%lu",i
);
523 s
+= strlen (s
); /* point to end of string */
524 if ((s
- tmp
) > (MAILTMPLEN
- 30)) {
525 mail_setflag (stream
,tmp
+ 1,"\\Deleted");
526 *(s
= tmp
) = '\0'; /* restart sequence */
528 i
= j
; /* continue after the range */
530 if (tmp
[0]) mail_setflag (stream
,tmp
+ 1,"\\Deleted");
531 mail_expunge (stream
);
533 syslog (LOG_INFO
,"Update user=%.80s host=%.80s nmsgs=%lu ndele=%lu nseen=%lu",
534 user
,tcp_clienthost (),stream
->nmsgs
,ndele
,nseen
);
538 return 0; /* stupid compilers */
543 * Accepts: exit status
548 void sayonara (int status
)
550 logouthook_t lgoh
= (logouthook_t
) mail_parameters (NIL
,GET_LOGOUTHOOK
,NIL
);
551 if (goodbye
) { /* have a goodbye message? */
553 PFLUSH (); /* make sure blatted */
555 syslog (LOG_INFO
,"%s user=%.80s host=%.80s",logout
,
556 user
? (char *) user
: "???",tcp_clienthost ());
557 /* do logout hook if needed */
558 if (lgoh
) (*lgoh
) (mail_parameters (NIL
,GET_LOGOUTDATA
,NIL
));
559 _exit (status
); /* all done */
567 alarm (0); /* disable all interrupts */
568 server_init (NIL
,NIL
,NIL
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
);
569 goodbye
= "-ERR Autologout; idle for too long\015\012";
570 logout
= "Autologout";
571 if (critical
) state
= LOGOUT
; /* badly hosed if in critical code */
572 else { /* try to gracefully close the stream */
573 if ((state
== TRANSACTION
) && !stream
->lock
) {
584 /* Kiss Of Death interrupt
590 if (idletime
&& ((time (0) - idletime
) > KODTIMEOUT
)) {
591 alarm (0); /* disable all interrupts */
592 server_init (NIL
,NIL
,NIL
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
);
593 goodbye
= "-ERR Received Kiss of Death\015\012";
594 logout
= "Killed (lost mailbox lock)";
595 if (critical
) state
=LOGOUT
;/* must defer if in critical code */
596 else { /* try to gracefully close the stream */
597 if ((state
== TRANSACTION
) && !stream
->lock
) {
603 sayonara (1); /* die die die */
614 alarm (0); /* disable all interrupts */
615 server_init (NIL
,NIL
,NIL
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
);
616 goodbye
= NIL
; /* nobody left to talk to */
618 if (critical
) state
= LOGOUT
; /* must defer if in critical code */
619 else { /* try to gracefully close the stream */
620 if ((state
== TRANSACTION
) && !stream
->lock
) {
626 sayonara (1); /* die die die */
631 /* Termination interrupt
636 alarm (0); /* disable all interrupts */
637 server_init (NIL
,NIL
,NIL
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
);
638 goodbye
= "-ERR Killed\015\012";
640 if (critical
) state
= LOGOUT
; /* must defer if in critical code */
641 /* Make no attempt at graceful closure since a shutdown may be in
642 * progress, and we won't have any time to do mail_close() actions.
644 else sayonara (1); /* die die die */
647 /* Parse PASS command
648 * Accepts: pointer to command argument
652 int pass_login (char *t
,int argc
,char *argv
[])
654 char tmp
[MAILTMPLEN
];
655 /* flush old passowrd */
656 if (pass
) fs_give ((void **) &pass
);
657 if (!(t
&& *t
)) { /* if no password given */
658 PSOUT ("-ERR Missing password argument\015\012");
659 return AUTHORIZATION
;
661 pass
= cpystr (t
); /* copy password argument */
662 if (!host
) { /* want remote mailbox? */
663 /* no, delimit user from possible admin */
664 if ((t
= strchr (user
,'*')) != NULL
) *t
++ ='\0';
665 /* attempt the login */
666 if (server_login (user
,pass
,t
,argc
,argv
)) {
667 int ret
= mbxopen ("INBOX");
668 if (ret
== TRANSACTION
) /* mailbox opened OK? */
669 syslog (LOG_INFO
,"%sLogin user=%.80s host=%.80s nmsgs=%lu/%lu",
670 t
? "Admin " : "",user
,tcp_clienthost (),nmsgs
,stream
->nmsgs
);
671 else syslog (LOG_INFO
,"%sLogin user=%.80s host=%.80s no mailbox",
672 t
? "Admin " : "",user
,tcp_clienthost ());
676 #ifndef DISABLE_POP_PROXY
677 /* remote; build remote INBOX */
678 else if (anonymous_login (argc
,argv
)) {
679 syslog (LOG_INFO
,"IMAP login to host=%.80s user=%.80s host=%.80s",host
,
680 user
,tcp_clienthost ());
681 sprintf (tmp
,"{%.128s/user=%.128s}INBOX",host
,user
);
682 /* disable rimap just in case */
683 mail_parameters (NIL
,SET_RSHTIMEOUT
,0);
684 return mbxopen (tmp
);
687 /* vague error message to confuse crackers */
688 PSOUT ("-ERR Bad login\015\012");
689 return AUTHORIZATION
;
692 /* Authentication responder
694 * length of challenge
695 * pointer to response length return location if non-NIL
699 #define RESPBUFLEN 8*MAILTMPLEN
701 char *responder (void *challenge
,unsigned long clen
,unsigned long *rlen
)
704 unsigned char *t
,resp
[RESPBUFLEN
];
705 char tmp
[MAILTMPLEN
];
706 if (initial
) { /* initial response given? */
707 if (clen
) return NIL
; /* not permitted */
708 /* set up response */
709 t
= (unsigned char *) initial
;
710 initial
= NIL
; /* no more initial response */
711 return (char *) rfc822_base64 (t
,strlen ((char *) t
),rlen
? rlen
: &i
);
714 for (t
= rfc822_binary (challenge
,clen
,&i
),j
= 0; j
< i
; j
++)
715 if (t
[j
] > ' ') PBOUT (t
[j
]);
716 fs_give ((void **) &t
);
718 PFLUSH (); /* dump output buffer */
719 resp
[RESPBUFLEN
-1] = '\0'; /* last buffer character is guaranteed NUL */
720 alarm (LOGINTIMEOUT
); /* get a response under timeout */
721 clearerr (stdin
); /* clear stdin errors */
723 while (!PSIN ((char *) resp
,RESPBUFLEN
)) {
724 /* ignore if some interrupt */
725 if (ferror (stdin
) && (errno
== EINTR
)) clearerr (stdin
);
727 char *e
= ferror (stdin
) ?
728 strerror (errno
) : "Command stream end of file";
729 alarm (0); /* disable all interrupts */
730 server_init (NIL
,NIL
,NIL
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
);
731 sprintf (logout
= tmp
,"%.80s, while reading authentication",e
);
737 if (!(t
= (unsigned char *) strchr ((char *) resp
,'\012'))) {
739 while ((c
= PBIN ()) != '\012') if (c
== EOF
) {
740 /* ignore if some interrupt */
741 if (ferror (stdin
) && (errno
== EINTR
)) clearerr (stdin
);
743 char *e
= ferror (stdin
) ?
744 strerror (errno
) : "Command stream end of file";
745 alarm (0); /* disable all interrupts */
746 server_init (NIL
,NIL
,NIL
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
);
747 sprintf (logout
= tmp
,"%.80s, while reading auth char",e
);
755 alarm (0); /* make sure timeout disabled */
756 if (t
[-1] == '\015') --t
; /* remove CR */
757 *t
= '\0'; /* tie off buffer */
758 return (resp
[0] != '*') ?
759 (char *) rfc822_base64 (resp
,t
-resp
,rlen
? rlen
: &i
) : NIL
;
763 * Accepts: mailbox name
767 int mbxopen (char *mailbox
)
770 char tmp
[MAILTMPLEN
];
772 if (msg
) fs_give ((void **) &msg
);
774 if (!(stream
= mail_open (stream
,mailbox
,NIL
)))
775 goodbye
= "-ERR Unable to open user's INBOX\015\012";
776 else if (stream
->rdonly
) /* make sure not readonly */
777 goodbye
= "-ERR Can't get lock. Mailbox in use\015\012";
779 nmsgs
= 0; /* no messages yet */
780 if ((j
= stream
->nmsgs
) != 0L) { /* if mailbox non-empty */
781 sprintf (tmp
,"1:%lu",j
); /* fetch fast information for all messages */
782 mail_fetch_fast (stream
,tmp
,NIL
);
784 /* create 1-origin tables */
785 msg
= (long *) fs_get (++j
* sizeof (long));
786 flags
= (short *) fs_get (j
* sizeof (short));
788 for (i
= 1; i
< j
; ++i
) if (!(elt
= mail_elt (stream
,i
))->deleted
) {
789 msg
[++nmsgs
] = i
; /* note the presence of this message */
790 if (elt
->seen
) il
= nmsgs
;/* and set up initial LAST */
792 /* make sure unused map entries are zero */
793 for (i
= nmsgs
+ 1; i
< j
; ++i
) msg
[i
] = 0;
794 rset (); /* do implicit RSET */
795 sprintf (tmp
,"+OK Mailbox open, %lu messages\015\012",nmsgs
);
799 syslog (LOG_INFO
,"Error opening or locking INBOX user=%.80s host=%.80s",
800 user
,tcp_clienthost ());
804 /* Blat a string with dot checking
806 * maximum number of lines if greater than zero
807 * maximum number of bytes to output
808 * alternative stringstruct
809 * Returns: number of lines output
811 * This routine is uglier and kludgier than it should be, just to be robust
812 * in the case of a message which doesn't end in a newline. Yes, this routine
813 * does truncate the last two bytes from the text. Since it is normally a
814 * newline and the main routine adds it back, it usually does not make a
815 * difference. But if it isn't, since the newline is required and the octet
816 * counts have to match, there's no choice but to truncate.
819 long blat (char *text
,long lines
,unsigned long size
,STRING
*st
)
823 /* no-op if zero lines or empty string */
824 if (!(lines
&& (size
-- > 2))) return 0;
826 c
= *text
++; d
= *text
++; /* collect first two bytes */
827 if (c
== '.') PBOUT ('.'); /* double string-leading dot if necessary */
828 while (lines
&& --size
) { /* copy loop */
829 e
= *text
++; /* get next byte */
830 PBOUT (c
); /* output character */
831 if (c
== '\012') { /* end of line? */
832 ret
++; --lines
; /* count another line */
833 /* double leading dot as necessary */
834 if (lines
&& size
&& (d
== '.')) PBOUT ('.');
836 c
= d
; d
= e
; /* move to next character */
840 c
= SNX (st
); d
= SNX (st
); /* collect first two bytes */
841 if (c
== '.') PBOUT ('.'); /* double string-leading dot if necessary */
842 while (lines
&& --size
) { /* copy loop */
843 e
= SNX (st
); /* get next byte */
844 PBOUT (c
); /* output character */
845 if (c
== '\012') { /* end of line? */
846 ret
++; --lines
; /* count another line */
847 /* double leading dot as necessary */
848 if (lines
&& size
&& (d
== '.')) PBOUT ('.');
850 c
= d
; d
= e
; /* move to next character */
861 /* clear all flags */
862 if (flags
) memset ((void *) flags
,0,(nmsgs
+ 1) * sizeof (short));
863 ndele
= nseen
= 0; /* no more deleted or seen messages */
864 last
= il
; /* restore previous LAST value */
867 /* Co-routines from MAIL library */
870 /* Message matches a search
871 * Accepts: MAIL stream
875 void mm_searched (MAILSTREAM
*stream
,unsigned long msgno
)
881 /* Message exists (i.e. there are that many messages in the mailbox)
882 * Accepts: MAIL stream
886 void mm_exists (MAILSTREAM
*stream
,unsigned long number
)
888 /* Can't use this mechanism. POP has no means of notifying the client of
889 new mail during the session. */
894 * Accepts: MAIL stream
898 void mm_expunged (MAILSTREAM
*stream
,unsigned long number
)
900 unsigned long i
= number
+ 1;
901 msg
[number
] = 0; /* I bet that this will annoy someone */
902 while (i
<= nmsgs
) --msg
[i
++];
906 /* Message flag status change
907 * Accepts: MAIL stream
911 void mm_flags (MAILSTREAM
*stream
,unsigned long number
)
913 /* This isn't used */
918 * Accepts: MAIL stream
919 * hierarchy delimiter
924 void mm_list (MAILSTREAM
*stream
,int delimiter
,char *name
,long attributes
)
926 /* This isn't used */
930 /* Subscribe mailbox found
931 * Accepts: MAIL stream
932 * hierarchy delimiter
937 void mm_lsub (MAILSTREAM
*stream
,int delimiter
,char *name
,long attributes
)
939 /* This isn't used */
944 * Accepts: MAIL stream
949 void mm_status (MAILSTREAM
*stream
,char *mailbox
,MAILSTATUS
*status
)
951 /* This isn't used */
954 /* Notification event
955 * Accepts: MAIL stream
960 void mm_notify (MAILSTREAM
*stream
,char *string
,long errflg
)
962 mm_log (string
,errflg
); /* just do mm_log action */
966 /* Log an event for the user to see
967 * Accepts: string to log
971 void mm_log (char *string
,long errflg
)
974 case NIL
: /* information message */
975 case PARSE
: /* parse glitch */
976 break; /* too many of these to log */
977 case WARN
: /* warning */
978 syslog (LOG_DEBUG
,"%s",string
);
980 case BYE
: /* driver broke connection */
981 if (state
!= UPDATE
) {
982 char tmp
[MAILTMPLEN
];
983 alarm (0); /* disable all interrupts */
984 server_init (NIL
,NIL
,NIL
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
,SIG_IGN
);
985 sprintf (logout
= tmp
,"Mailbox closed (%.80s)",string
);
991 case ERROR
: /* error that broke command */
992 default: /* default should never happen */
993 syslog (LOG_NOTICE
,"%s",string
);
999 /* Log an event to debugging telemetry
1000 * Accepts: string to log
1003 void mm_dlog (char *string
)
1005 /* Not doing anything here for now */
1009 /* Get user name and password for this host
1010 * Accepts: parse of network mailbox name
1011 * where to return user name
1012 * where to return password
1016 void mm_login (NETMBX
*mb
,char *username
,char *password
,long trial
)
1019 strncpy (username
,*mb
->user
? mb
->user
: user
,NETMAXUSER
-1);
1021 strncpy (password
,pass
,255);/* and password */
1022 fs_give ((void **) &pass
);
1024 else memset (password
,0,256); /* no password to send, abort login */
1025 username
[NETMAXUSER
] = password
[255] = '\0';
1028 /* About to enter critical code
1032 void mm_critical (MAILSTREAM
*stream
)
1038 /* About to exit critical code
1042 void mm_nocritical (MAILSTREAM
*stream
)
1051 * flag indicating that mailbox may be clobbered
1052 * Returns: abort flag
1055 long mm_diskerror (MAILSTREAM
*stream
,long errcode
,long serious
)
1057 if (serious
) { /* try your damnest if clobberage likely */
1059 "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
1060 user
,tcp_clienthost (),
1061 (stream
&& stream
->mailbox
) ? stream
->mailbox
: "???",
1062 strerror (errcode
));
1063 alarm (0); /* make damn sure timeout disabled */
1064 sleep (60); /* give it some time to clear up */
1067 syslog (LOG_ALERT
,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
1068 user
,tcp_clienthost (),
1069 (stream
&& stream
->mailbox
) ? stream
->mailbox
: "???",
1070 strerror (errcode
));
1075 /* Log a fatal error event
1076 * Accepts: string to log
1079 void mm_fatal (char *string
)
1081 mm_log (string
,ERROR
); /* shouldn't happen normally */