imapext-2007

view src/ipopd/ipop2d.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-2008 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: IPOP2D - IMAP to POP2 conversion server
16 *
17 * Author: Mark Crispin
18 * UW Technology
19 * University of Washington
20 * Seattle, WA 98195
21 * Internet: MRC@Washington.EDU
22 *
23 * Date: 28 October 1990
24 * Last Edited: 13 February 2008
25 */
28 /* Parameter files */
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <errno.h>
33 extern int errno; /* just in case */
34 #include <signal.h>
35 #include <time.h>
36 #include "c-client.h"
39 /* Autologout timer */
40 #define KODTIMEOUT 60*5
41 #define LOGINTIMEOUT 60*3
42 #define TIMEOUT 60*30
45 /* Size of temporary buffers */
46 #define TMPLEN 1024
49 /* Server states */
51 #define LISN 0
52 #define AUTH 1
53 #define MBOX 2
54 #define ITEM 3
55 #define NEXT 4
56 #define DONE 5
58 /* Global storage */
60 char *version = "75"; /* edit number of this server */
61 short state = LISN; /* server state */
62 short critical = NIL; /* non-zero if in critical code */
63 MAILSTREAM *stream = NIL; /* mailbox stream */
64 time_t idletime = 0; /* time we went idle */
65 unsigned long nmsgs = 0; /* number of messages */
66 unsigned long current = 1; /* current message number */
67 unsigned long size = 0; /* size of current message */
68 char status[MAILTMPLEN]; /* space for status string */
69 char *user = ""; /* user name */
70 char *pass = ""; /* password */
71 unsigned long *msg = NIL; /* message translation vector */
72 char *logout = "Logout";
73 char *goodbye = "+ Sayonara\015\012";
76 /* Function prototypes */
78 int main (int argc,char *argv[]);
79 void sayonara (int status);
80 void clkint ();
81 void kodint ();
82 void hupint ();
83 void trmint ();
84 short c_helo (char *t,int argc,char *argv[]);
85 short c_fold (char *t);
86 short c_read (char *t);
87 short c_retr (char *t);
88 short c_acks (char *t);
89 short c_ackd (char *t);
90 short c_nack (char *t);
92 /* Main program */
94 int main (int argc,char *argv[])
95 {
96 char *s,*t;
97 char cmdbuf[TMPLEN];
98 char *pgmname = (argc && argv[0]) ?
99 (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ?
100 s+1 : argv[0]) : "ipop2d";
101 /* set service name before linkage */
102 mail_parameters (NIL,SET_SERVICENAME,(void *) "pop");
103 #include "linkage.c"
104 if (mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL)) {
105 goodbye = "- POP2 server disabled on this system\015\012";
106 sayonara (1);
107 }
108 /* initialize server */
109 server_init (pgmname,"pop",NIL,clkint,kodint,hupint,trmint,NIL);
110 /* There are reports of POP2 clients which get upset if anything appears
111 * between the "+" and the "POP2" in the greeting.
112 */
113 printf ("+ POP2 %s %s.%s server ready\015\012",tcp_serverhost (),
114 CCLIENTVERSION,version);
115 fflush (stdout); /* dump output buffer */
116 state = AUTH; /* initial server state */
117 while (state != DONE) { /* command processing loop */
118 idletime = time (0); /* get a command under timeout */
119 alarm ((state != AUTH) ? TIMEOUT : LOGINTIMEOUT);
120 clearerr (stdin); /* clear stdin errors */
121 while (!fgets (cmdbuf,TMPLEN-1,stdin)) {
122 if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
123 else {
124 char *e = ferror (stdin) ?
125 strerror (errno) : "Unexpected client disconnect";
126 alarm (0); /* disable all interrupts */
127 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
128 sprintf (logout = cmdbuf,"%.80s while reading line",e);
129 state = DONE;
130 stream = mail_close (stream);
131 goodbye = NIL;
132 sayonara (1);
133 }
134 }
135 alarm (0); /* make sure timeout disabled */
136 idletime = 0; /* no longer idle */
137 /* find end of line */
138 if (!strchr (cmdbuf,'\012')) {
139 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
140 logout = "- Command line too long\015\012";
141 state = DONE;
142 }
143 else if (!(s = strtok (cmdbuf," \015\012"))) {
144 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
145 goodbye = "- Missing or null command\015\012";
146 state = DONE;
147 }
148 else { /* dispatch based on command */
149 ucase (s); /* canonicalize case */
150 /* snarf argument */
151 t = strtok (NIL,"\015\012");
152 if ((state == AUTH) && !strcmp (s,"HELO")) state = c_helo (t,argc,argv);
153 else if ((state == MBOX || state == ITEM) && !strcmp (s,"FOLD"))
154 state = c_fold (t);
155 else if ((state == MBOX || state == ITEM) && !strcmp (s,"READ"))
156 state = c_read (t);
157 else if ((state == ITEM) && !strcmp (s,"RETR")) state = c_retr (t);
158 else if ((state == NEXT) && !strcmp (s,"ACKS")) state = c_acks (t);
159 else if ((state == NEXT) && !strcmp (s,"ACKD")) state = c_ackd (t);
160 else if ((state == NEXT) && !strcmp (s,"NACK")) state = c_nack (t);
161 else if ((state == AUTH || state == MBOX || state == ITEM) &&
162 !strcmp (s,"QUIT")) {
163 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
164 state = DONE; /* done in either case */
165 if (t) goodbye = "- Bogus argument given to QUIT\015\012";
166 else { /* expunge the stream */
167 if (stream && nmsgs) stream = mail_close_full (stream,CL_EXPUNGE);
168 stream = NIL; /* don't repeat it */
169 }
170 }
171 else { /* some other or inappropriate command */
172 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
173 goodbye = "- Bogus or out of sequence command\015\012";
174 state = DONE;
175 }
176 }
177 fflush (stdout); /* make sure output blatted */
178 }
179 /* clean up the stream */
180 if (stream) mail_close (stream);
181 sayonara (0);
182 return 0; /* stupid compilers */
183 }
186 /* Say goodbye
187 * Accepts: exit status
188 *
189 * Does not return
190 */
192 void sayonara (int status)
193 {
194 logouthook_t lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL);
195 if (goodbye) { /* have a goodbye message? */
196 fputs (goodbye,stdout);
197 fflush (stdout); /* make sure blatted */
198 }
199 syslog (LOG_INFO,"%s user=%.80s host=%.80s",logout,
200 user ? (char *) user : "???",tcp_clienthost ());
201 /* do logout hook if needed */
202 if (lgoh) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL));
203 _exit (status); /* all done */
204 }
206 /* Clock interrupt
207 */
209 void clkint ()
210 {
211 alarm (0); /* disable all interrupts */
212 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
213 goodbye = "- Autologout; idle for too long\015\012";
214 logout = "Autologout";
215 state = DONE; /* mark state done in either case */
216 if (!critical) { /* badly host if in critical code */
217 if (stream && !stream->lock) mail_close (stream);
218 stream = NIL;
219 sayonara (1); /* die die die */
220 }
221 }
224 /* Kiss Of Death interrupt
225 */
227 void kodint ()
228 {
229 /* only if in command wait */
230 if (idletime && ((time (0) - idletime) > KODTIMEOUT)) {
231 alarm (0); /* disable all interrupts */
232 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
233 goodbye = "- Killed (lost mailbox lock)\015\012";
234 logout = "Killed (lost mailbox lock)";
235 state = DONE; /* mark state done in either case */
236 if (!critical) { /* badly host if in critical code */
237 if (stream && !stream->lock) mail_close (stream);
238 stream = NIL;
239 sayonara (1); /* die die die */
240 }
241 }
242 }
245 /* Hangup interrupt
246 */
248 void hupint ()
249 {
250 alarm (0); /* disable all interrupts */
251 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
252 goodbye = NIL;
253 logout = "Hangup";
254 state = DONE; /* mark state done in either case */
255 if (!critical) { /* badly host if in critical code */
256 if (stream && !stream->lock) mail_close (stream);
257 stream = NIL;
258 sayonara (1); /* die die die */
259 }
260 }
263 /* Termination interrupt
264 */
266 void trmint ()
267 {
268 alarm (0); /* disable all interrupts */
269 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
270 goodbye = "- Killed (terminated)\015\012";
271 logout = "Killed (terminated)";
272 if (critical) state = DONE; /* mark state done in either case */
273 /* Make no attempt at graceful closure since a shutdown may be in
274 * progress, and we won't have any time to do mail_close() actions.
275 */
276 else sayonara (1); /* die die die */
277 }
279 /* Parse HELO command
280 * Accepts: pointer to command argument
281 * Returns: new state
282 */
284 short c_helo (char *t,int argc,char *argv[])
285 {
286 char *s,*u,*p;
287 char tmp[TMPLEN];
288 if ((!(t && *t && (u = strtok (t," ")) && (p = strtok (NIL,"\015\012")))) ||
289 (strlen (p) >= TMPLEN)) { /* get user name and password */
290 fputs ("- Missing user or password\015\012",stdout);
291 return DONE;
292 }
293 /* copy password, handle quoting */
294 for (s = tmp; *p; p++) *s++ = (*p == '\\') ? *++p : *p;
295 *s = '\0'; /* tie off string */
296 pass = cpystr (tmp);
297 if (!(s = strchr (u,':'))) { /* want remote mailbox? */
298 /* no, delimit user from possible admin */
299 if (s = strchr (u,'*')) *s++ = '\0';
300 if (server_login (user = cpystr (u),pass,s,argc,argv)) {
301 syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s",s ? "Admin " : "",
302 user,tcp_clienthost ());
303 return c_fold ("INBOX"); /* local; select INBOX */
304 }
305 }
306 #ifndef DISABLE_POP_PROXY
307 /* can't do if can't log in as anonymous */
308 else if (anonymous_login (argc,argv)) {
309 *s++ = '\0'; /* separate host name from user name */
310 user = cpystr (s); /* note user name */
311 syslog (LOG_INFO,"IMAP login to host=%.80s user=%.80s host=%.80s",u,user,
312 tcp_clienthost ());
313 /* initially remote INBOX */
314 sprintf (tmp,"{%.128s/user=%.128s}INBOX",u,user);
315 /* disable rimap just in case */
316 mail_parameters (NIL,SET_RSHTIMEOUT,0);
317 return c_fold (tmp);
318 }
319 #endif
320 fputs ("- Bad login\015\012",stdout);
321 return DONE;
322 }
324 /* Parse FOLD command
325 * Accepts: pointer to command argument
326 * Returns: new state
327 */
329 short c_fold (char *t)
330 {
331 unsigned long i,j,flags;
332 char *s = NIL,tmp[2*TMPLEN];
333 NETMBX mb;
334 if (!(t && *t)) { /* make sure there's an argument */
335 fputs ("- Missing mailbox name\015\012",stdout);
336 return DONE;
337 }
338 myusername_full (&flags); /* get user type flags */
339 /* expunge old stream */
340 if (stream && nmsgs) mail_expunge (stream);
341 nmsgs = 0; /* no more messages */
342 if (msg) fs_give ((void **) &msg);
343 #ifndef DISABLE_POP_PROXY
344 if (flags == MU_ANONYMOUS) { /* don't permit proxy to leave IMAP */
345 if (stream) { /* not first time */
346 if (!(stream->mailbox && (s = strchr (stream->mailbox,'}'))))
347 fatal ("bad previous mailbox name");
348 strncpy (tmp,stream->mailbox,i = (++s - stream->mailbox));
349 if (i >= TMPLEN) fatal ("ridiculous network prefix");
350 strcpy (tmp+i,t); /* append mailbox to initial spec */
351 t = tmp;
352 }
353 /* must be net name first time */
354 else if (!mail_valid_net_parse (t,&mb)) fatal ("anonymous folder bogon");
355 }
356 #endif
357 /* open mailbox, note # of messages */
358 if (j = (stream = mail_open (stream,t,NIL)) ? stream->nmsgs : 0) {
359 sprintf (tmp,"1:%lu",j); /* fetch fast information for all messages */
360 mail_fetch_fast (stream,tmp,NIL);
361 msg = (unsigned long *) fs_get ((stream->nmsgs + 1) *
362 sizeof (unsigned long));
363 for (i = 1; i <= j; i++) /* find undeleted messages, add to vector */
364 if (!mail_elt (stream,i)->deleted) msg[++nmsgs] = i;
365 }
366 #ifndef DISABLE_POP_PROXY
367 if (!stream && (flags == MU_ANONYMOUS)) {
368 fputs ("- Bad login\015\012",stdout);
369 return DONE;
370 }
371 #endif
372 printf ("#%lu messages in %s\015\012",nmsgs,stream ? stream->mailbox :
373 "<none>");
374 return MBOX;
375 }
377 /* Parse READ command
378 * Accepts: pointer to command argument
379 * Returns: new state
380 */
382 short c_read (char *t)
383 {
384 MESSAGECACHE *elt = NIL;
385 if (t && *t) { /* have a message number argument? */
386 /* validity check message number */
387 if (((current = strtoul (t,NIL,10)) < 1) || (current > nmsgs)) {
388 fputs ("- Invalid message number given to READ\015\012",stdout);
389 return DONE;
390 }
391 }
392 else if (current > nmsgs) { /* at end of mailbox? */
393 fputs ("=0 No more messages\015\012",stdout);
394 return MBOX;
395 }
396 /* set size if message valid and exists */
397 size = msg[current] ? (elt = mail_elt(stream,msg[current]))->rfc822_size : 0;
398 if (elt) sprintf (status,"Status: %s%s\015\012",
399 elt->seen ? "R" : " ",elt->recent ? " " : "O");
400 else status[0] = '\0'; /* no status */
401 size += strlen (status); /* update size to reflect status */
402 /* display results */
403 printf ("=%lu characters in message %lu\015\012",size + 2,current);
404 return ITEM;
405 }
408 /* Parse RETR command
409 * Accepts: pointer to command argument
410 * Returns: new state
411 */
413 short c_retr (char *t)
414 {
415 unsigned long i,j;
416 STRING *bs;
417 if (t) { /* disallow argument */
418 fputs ("- Bogus argument given to RETR\015\012",stdout);
419 return DONE;
420 }
421 if (size) { /* message size valid? */
422 t = mail_fetch_header (stream,msg[current],NIL,NIL,&i,FT_PEEK);
423 if (i > 2) { /* only if there is something */
424 i -= 2; /* lop off last two octets */
425 while (i) { /* blat the header */
426 if (!(j = fwrite (t,sizeof (char),i,stdout))) return DONE;
427 if (i -= j) t += j; /* advance to incomplete data */
428 }
429 }
430 fputs (status,stdout); /* yes, output message */
431 fputs ("\015\012",stdout); /* delimit header from text */
432 if (t = mail_fetch_text (stream,msg[current],NIL,&i,FT_RETURNSTRINGSTRUCT))
433 while (i) { /* blat the text */
434 if (!(j = fwrite (t,sizeof (char),i,stdout))) return DONE;
435 if (i -= j) t += j; /* advance to incomplete data */
436 }
437 else for (bs = &stream->private.string; i--; )
438 if (putc (SNX (bs),stdout) == EOF) return DONE;
439 fputs ("\015\012",stdout); /* trailer to coddle PCNFS' NFSMAIL */
440 }
441 else return DONE; /* otherwise go away */
442 return NEXT;
443 }
445 /* Parse ACKS command
446 * Accepts: pointer to command argument
447 * Returns: new state
448 */
450 short c_acks (char *t)
451 {
452 char tmp[TMPLEN];
453 if (t) { /* disallow argument */
454 fputs ("- Bogus argument given to ACKS\015\012",stdout);
455 return DONE;
456 }
457 /* mark message as seen */
458 sprintf (tmp,"%lu",msg[current++]);
459 mail_setflag (stream,tmp,"\\Seen");
460 return c_read (NIL); /* end message reading transaction */
461 }
464 /* Parse ACKD command
465 * Accepts: pointer to command argument
466 * Returns: new state
467 */
469 short c_ackd (char *t)
470 {
471 char tmp[TMPLEN];
472 if (t) { /* disallow argument */
473 fputs ("- Bogus argument given to ACKD\015\012",stdout);
474 return DONE;
475 }
476 /* mark message as seen and deleted */
477 sprintf (tmp,"%lu",msg[current]);
478 mail_setflag (stream,tmp,"\\Seen \\Deleted");
479 msg[current++] = 0; /* mark message as deleted */
480 return c_read (NIL); /* end message reading transaction */
481 }
484 /* Parse NACK command
485 * Accepts: pointer to command argument
486 * Returns: new state
487 */
489 short c_nack (char *t)
490 {
491 if (t) { /* disallow argument */
492 fputs ("- Bogus argument given to NACK\015\012",stdout);
493 return DONE;
494 }
495 return c_read (NIL); /* end message reading transaction */
496 }
498 /* Co-routines from MAIL library */
501 /* Message matches a search
502 * Accepts: MAIL stream
503 * message number
504 */
506 void mm_searched (MAILSTREAM *stream,unsigned long msgno)
507 {
508 /* Never called */
509 }
512 /* Message exists (i.e. there are that many messages in the mailbox)
513 * Accepts: MAIL stream
514 * message number
515 */
517 void mm_exists (MAILSTREAM *stream,unsigned long number)
518 {
519 /* Can't use this mechanism. POP has no means of notifying the client of
520 new mail during the session. */
521 }
524 /* Message expunged
525 * Accepts: MAIL stream
526 * message number
527 */
529 void mm_expunged (MAILSTREAM *stream,unsigned long number)
530 {
531 if (state != DONE) { /* ignore if closing */
532 /* someone else screwed us */
533 goodbye = "- Mailbox expunged from under me!\015\012";
534 if (stream && !stream->lock) mail_close (stream);
535 stream = NIL;
536 sayonara (1);
537 }
538 }
541 /* Message status changed
542 * Accepts: MAIL stream
543 * message number
544 */
546 void mm_flags (MAILSTREAM *stream,unsigned long number)
547 {
548 /* This isn't used */
549 }
552 /* Mailbox found
553 * Accepts: MAIL stream
554 * hierarchy delimiter
555 * mailbox name
556 * mailbox attributes
557 */
559 void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
560 {
561 /* This isn't used */
562 }
565 /* Subscribe mailbox found
566 * Accepts: MAIL stream
567 * hierarchy delimiter
568 * mailbox name
569 * mailbox attributes
570 */
572 void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
573 {
574 /* This isn't used */
575 }
578 /* Mailbox status
579 * Accepts: MAIL stream
580 * mailbox name
581 * mailbox status
582 */
584 void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
585 {
586 /* This isn't used */
587 }
589 /* Notification event
590 * Accepts: MAIL stream
591 * string to log
592 * error flag
593 */
595 void mm_notify (MAILSTREAM *stream,char *string,long errflg)
596 {
597 mm_log (string,errflg); /* just do mm_log action */
598 }
601 /* Log an event for the user to see
602 * Accepts: string to log
603 * error flag
604 */
606 void mm_log (char *string,long errflg)
607 {
608 switch (errflg) {
609 case NIL: /* information message */
610 case PARSE: /* parse glitch */
611 break; /* too many of these to log */
612 case WARN: /* warning */
613 syslog (LOG_DEBUG,"%s",string);
614 break;
615 case BYE: /* driver broke connection */
616 if (state != DONE) {
617 char tmp[MAILTMPLEN];
618 alarm (0); /* disable all interrupts */
619 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
620 sprintf (logout = tmp,"Mailbox closed (%.80s)",string);
621 sayonara (1);
622 }
623 break;
624 case ERROR: /* error that broke command */
625 default: /* default should never happen */
626 syslog (LOG_NOTICE,"%s",string);
627 break;
628 }
629 }
632 /* Log an event to debugging telemetry
633 * Accepts: string to log
634 */
636 void mm_dlog (char *string)
637 {
638 /* Not doing anything here for now */
639 }
642 /* Get user name and password for this host
643 * Accepts: parse of network mailbox name
644 * where to return user name
645 * where to return password
646 * trial count
647 */
649 void mm_login (NETMBX *mb,char *username,char *password,long trial)
650 {
651 /* set user name */
652 strncpy (username,*mb->user ? mb->user : user,NETMAXUSER-1);
653 strncpy (password,pass,255); /* and password */
654 username[NETMAXUSER] = password[255] = '\0';
655 }
657 /* About to enter critical code
658 * Accepts: stream
659 */
661 void mm_critical (MAILSTREAM *stream)
662 {
663 ++critical;
664 }
667 /* About to exit critical code
668 * Accepts: stream
669 */
671 void mm_nocritical (MAILSTREAM *stream)
672 {
673 --critical;
674 }
677 /* Disk error found
678 * Accepts: stream
679 * system error code
680 * flag indicating that mailbox may be clobbered
681 * Returns: abort flag
682 */
684 long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
685 {
686 if (serious) { /* try your damnest if clobberage likely */
687 syslog (LOG_ALERT,
688 "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
689 user,tcp_clienthost (),
690 (stream && stream->mailbox) ? stream->mailbox : "???",
691 strerror (errcode));
692 alarm (0); /* make damn sure timeout disabled */
693 sleep (60); /* give it some time to clear up */
694 return NIL;
695 }
696 syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
697 user,tcp_clienthost (),
698 (stream && stream->mailbox) ? stream->mailbox : "???",
699 strerror (errcode));
700 return T;
701 }
704 /* Log a fatal error event
705 * Accepts: string to log
706 */
708 void mm_fatal (char *string)
709 {
710 mm_log (string,ERROR); /* shouldn't happen normally */
711 }

UW-IMAP'd extensions by yuuji