]> Pileus Git - ~andy/fetchmail/blob - pop3.c
Freeze bug fixes befoire adding more features.
[~andy/fetchmail] / pop3.c
1 /* Copyright 1993-95 by Carl Harris, Jr. Copyright 1996 by Eric S. Raymond
2  * All rights reserved.
3  * For license terms, see the file COPYING in this directory.
4  */
5
6 /***********************************************************************
7   module:       pop3.c
8   project:      popclient
9   programmer:   Carl Harris, ceharris@mal.com
10                 Hacks and bug fixes by esr.
11   description:  POP3 client code.
12
13  ***********************************************************************/
14
15 #include  <config.h>
16
17 #include  <stdio.h>
18 #if defined(STDC_HEADERS)
19 #include  <string.h>
20 #endif
21 #if defined(HAVE_UNISTD_H)
22 #include  <unistd.h>
23 #endif
24
25 #include  <sys/time.h>
26 #include  <ctype.h>
27 #include  <errno.h>
28
29 #include  "socket.h"
30 #include  "popclient.h"
31
32 #define   POP3_PORT     110
33
34 #ifdef HAVE_PROTOTYPES
35 /* prototypes for internal functions */
36 int POP3_OK (char *buf, int socket);
37 int POP3_auth (struct hostrec *options, int socket);
38 int POP3_sendQUIT (int socket);
39 int POP3_sendSTAT (int *msgcount, int socket);
40 int POP3_sendRETR (int msgnum, int socket);
41 int POP3_sendDELE (int msgnum, int socket);
42 int POP3_sendLAST (int *last, int socket);
43 int POP3_readmsg (int socket, int mboxfd, char *host, int topipe, int rewrite);
44 int POP3_BuildDigest (char *buf, struct hostrec *options);
45 #endif
46
47
48 /*********************************************************************
49   function:      doPOP3
50   description:   retrieve messages from the specified mail server
51                  using Post Office Protocol 3.
52
53   arguments:     
54     queryctl     fully-specified options (i.e. parsed, defaults invoked,
55                  etc).
56
57   return value:  exit code from the set of PS_.* constants defined in 
58                  popclient.h
59   calls:
60   globals:       reads outlevel.
61  *********************************************************************/
62
63 int doPOP3 (queryctl)
64 struct hostrec *queryctl;
65 {
66   int ok;
67   int mboxfd;
68   char buf [POPBUFSIZE];
69   int socket;
70   int first,number,count;
71
72
73   /* open/lock the folder if we're using a mailbox */
74   if (queryctl->output == TO_FOLDER) 
75     if ((mboxfd = openuserfolder(queryctl)) < 0) 
76       return(PS_IOERR);
77     
78   /* open the socket and get the greeting */
79   if ((socket = Socket(queryctl->servername,POP3_PORT)) < 0) {
80     perror("doPOP3: socket");
81     ok = PS_SOCKET;
82     goto closeUp;
83   }
84
85   ok = POP3_OK(buf,socket);
86   if (ok != 0) {
87     if (ok != PS_SOCKET)
88       POP3_sendQUIT(socket);
89     close(socket);
90     goto closeUp;
91   }
92
93   /* print the greeting */
94   if (outlevel > O_SILENT && outlevel < O_VERBOSE) 
95     fprintf(stderr,"%s\n",buf);
96   else 
97     ;
98
99 #if defined(HAVE_APOP_SUPPORT)
100   /* build MD5 digest from greeting timestamp + password */
101   if (queryctl->whichpop == P_APOP) 
102     if (POP3_BuildDigest(buf,queryctl) != 0) {
103       ok = PS_AUTHFAIL;
104       goto closeUp;
105     } else
106       ;
107   else
108     ;  /* not using APOP protocol this time */
109 #endif
110
111   /* try to get authorized */
112   ok = POP3_auth(queryctl,socket);
113   if (ok == PS_ERROR)
114     ok = PS_AUTHFAIL;
115   if (ok != 0)
116     goto cleanUp;
117
118   /* find out how many messages are waiting */
119   ok = POP3_sendSTAT(&count,socket);
120   if (ok != 0) {
121     goto cleanUp;
122   }
123
124   /* Ask for number of last message retrieved */
125   if (queryctl->fetchall) 
126     first = 1;
127   else {
128     ok = POP3_sendLAST(&first, socket);
129     if (ok != 0)
130       goto cleanUp;
131
132     first++;
133   }
134     
135   /* show them how many messages we'll be downloading */
136   if (outlevel > O_SILENT && outlevel < O_VERBOSE)
137     if (first > 1) 
138       fprintf(stderr,"%d messages in folder, %d new messages.\n", 
139                       count, count - first + 1);
140     else
141       fprintf(stderr,"%d new messages in folder.\n", count);
142   else
143     ;
144
145   if (count > 0) { 
146     for (number = (queryctl->flush || queryctl->fetchall)? 1 : first;  
147                    number <= count;  
148                    number++) {
149
150       /* open the mail pipe if we're using an MDA */
151       if (queryctl->output == TO_MDA
152            && (queryctl->fetchall || number >= first)) {
153         ok = (mboxfd = openmailpipe(queryctl)) < 0 ? -1 : 0;
154         if (ok != 0)
155           goto cleanUp;
156       }
157            
158       if (queryctl->flush && number < first && !queryctl->fetchall) 
159         ok = 0;  /* no command to send here, will delete message below */
160       else if (linelimit) 
161         ok = POP3_sendTOP(number,linelimit,socket);
162       else 
163         ok = POP3_sendRETR(number,socket);
164       if (ok != 0)
165         goto cleanUp;
166       
167       if (number >= first || queryctl->fetchall)
168         ok = POP3_readmsg(socket,mboxfd,
169                           queryctl->servername,
170                           queryctl->output == TO_MDA, 
171                           queryctl->rewrite);
172       else
173         ok = 0;
174       if (ok != 0)
175         goto cleanUp;
176
177       if ((number < first && queryctl->flush) || !queryctl->keep) {
178         if (outlevel > O_SILENT && outlevel < O_VERBOSE) 
179           fprintf(stderr,"flushing message %d\n", number);
180         else
181           ;
182         ok = POP3_sendDELE(number,socket);
183         if (ok != 0)
184           goto cleanUp;
185       }
186       else
187         ; /* message is kept */
188
189       /* close the mail pipe if we're using the system mailbox */
190       if (queryctl->output == TO_MDA
191            && (queryctl->fetchall || number >= first)) {
192         ok = closemailpipe(mboxfd);
193         if (ok != 0)
194           goto cleanUp;
195       }
196     }
197
198     ok = POP3_sendQUIT(socket);
199     if (ok == 0)
200       ok = PS_SUCCESS;
201     close(socket);
202     goto closeUp;
203   }
204   else {
205     ok = POP3_sendQUIT(socket);
206     if (ok == 0)
207       ok = PS_NOMAIL;
208     close(socket);
209     goto closeUp;
210   }
211
212 cleanUp:
213   if (ok != 0 && ok != PS_SOCKET)
214     POP3_sendQUIT(socket);
215
216 closeUp:
217   if (queryctl->output == TO_FOLDER)
218     if (closeuserfolder(mboxfd) < 0 && ok == 0)
219       ok = PS_IOERR;
220     
221   if (ok == PS_IOERR || ok == PS_SOCKET) 
222     perror("doPOP3: cleanUp");
223
224   return(ok);
225 }
226
227
228
229 /*********************************************************************
230   function:      POP3_OK
231   description:   get the server's response to a command, and return
232                  the extra arguments sent with the response.
233   arguments:     
234     argbuf       buffer to receive the argument string.
235     socket       socket to which the server is connected.
236
237   return value:  zero if okay, else return code.
238   calls:         SockGets
239   globals:       reads outlevel.
240  *********************************************************************/
241
242 int POP3_OK (argbuf,socket)
243 char *argbuf;
244 int socket;
245 {
246   int ok;
247   char buf [POPBUFSIZE];
248   char *bufp;
249
250   if (SockGets(socket, buf, sizeof(buf)) == 0) {
251     if (outlevel == O_VERBOSE)
252       fprintf(stderr,"%s\n",buf);
253
254     bufp = buf;
255     if (*bufp == '+' || *bufp == '-')
256       bufp++;
257     else
258       return(PS_PROTOCOL);
259
260     while (isalpha(*bufp))
261       bufp++;
262     *(bufp++) = '\0';
263
264     if (strcmp(buf,"+OK") == 0)
265       ok = 0;
266     else if (strcmp(buf,"-ERR") == 0)
267       ok = PS_ERROR;
268     else
269       ok = PS_PROTOCOL;
270
271     if (argbuf != NULL)
272       strcpy(argbuf,bufp);
273   }
274   else 
275     ok = PS_SOCKET;
276
277   return(ok);
278 }
279
280
281
282 /*********************************************************************
283   function:      POP3_auth
284   description:   send the USER and PASS commands to the server, and
285                  get the server's response.
286   arguments:     
287     queryctl     merged options record.
288     socket       socket to which the server is connected.
289
290   return value:  zero if success, else status code.
291   calls:         SockPrintf, POP3_OK.
292   globals:       read outlevel.
293  *********************************************************************/
294
295 int POP3_auth (queryctl,socket) 
296 struct hostrec *queryctl;
297 int socket;
298 {
299   char buf [POPBUFSIZE];
300
301   switch (queryctl->protocol) {
302     case P_POP3:
303       SockPrintf(socket,"USER %s\r\n",queryctl->remotename);
304       if (outlevel == O_VERBOSE)
305         fprintf(stderr,"> USER %s\n",queryctl->remotename);
306       if (POP3_OK(buf,socket) != 0)
307         goto badAuth;
308
309       SockPrintf(socket,"PASS %s\r\n",queryctl->password);
310       if (outlevel == O_VERBOSE)
311         fprintf(stderr,"> PASS password\n");
312       if (POP3_OK(buf,socket) != 0)
313         goto badAuth;
314     
315       break;
316
317 #if defined(HAVE_APOP_SUPPORT)
318     case P_APOP:
319       SockPrintf(socket,"APOP %s %s\r\n", 
320                  queryctl->remotename, queryctl->digest);
321       if (outlevel == O_VERBOSE)
322         fprintf(stderr,"> APOP %s %s\n",queryctl->remotename, queryctl->digest);
323       if (POP3_OK(buf,socket) != 0) 
324         goto badAuth;
325       break;
326 #endif  /* HAVE_APOP_SUPPORT */
327
328 #if defined(HAVE_RPOP_SUPPORT)
329     case P_RPOP:
330       SockPrintf(socket, "RPOP %s\r\n", queryctl->remotename);
331       if (POP3_OK(buf,socket) != 0)
332          goto badAuth;
333       if (outlevel == O_VERBOSE)
334         fprintf(stderr,"> RPOP %s %s\n",queryctl->remotename);
335       break;
336 #endif  /* HAVE_RPOP_SUPPORT */
337
338     default:
339       fprintf(stderr,"Undefined protocol request in POP3_auth\n");
340   }
341
342   /* we're approved */
343   return(0);
344
345   /*NOTREACHED*/
346
347 badAuth:
348   if (outlevel > O_SILENT && outlevel < O_VERBOSE)
349     fprintf(stderr,"%s\n",buf);
350   else
351     ; /* say nothing */
352
353   return(PS_ERROR);
354 }
355
356
357
358
359 /*********************************************************************
360   function:      POP3_sendQUIT
361   description:   send the QUIT command to the server and close 
362                  the socket.
363
364   arguments:     
365     socket       socket to which the server is connected.
366
367   return value:  none.
368   calls:         SockPuts, POP3_OK.
369   globals:       reads outlevel.
370  *********************************************************************/
371
372 int POP3_sendQUIT (socket)
373 int socket;
374 {
375   int ok;
376   char buf [POPBUFSIZE];
377
378   SockPuts(socket,"QUIT");
379
380   if (outlevel == O_VERBOSE)
381     fprintf(stderr,"> QUIT\n");
382   else
383     ;
384
385   ok = POP3_OK(buf,socket);
386   if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
387     fprintf(stderr,"%s\n",buf);
388
389   return(ok);
390 }
391
392
393
394 /*********************************************************************
395   function:      POP3_sendSTAT
396   description:   send the STAT command to the POP3 server to find
397                  out how many messages are waiting.
398   arguments:     
399     count        pointer to an integer to receive the message count.
400     socket       socket to which the POP3 server is connected.
401
402   return value:  return code from POP3_OK.
403   calls:         POP3_OK, SockPrintf
404   globals:       reads outlevel.
405  *********************************************************************/
406
407 int POP3_sendSTAT (msgcount,socket)
408 int *msgcount;
409 int socket;
410 {
411   int ok;
412   char buf [POPBUFSIZE];
413   int totalsize;
414
415   SockPrintf(socket,"STAT\r\n");
416   if (outlevel == O_VERBOSE)
417     fprintf(stderr,"> STAT\n");
418   
419   ok = POP3_OK(buf,socket);
420   if (ok == 0)
421     sscanf(buf,"%d %d",msgcount,&totalsize);
422   else if (outlevel > O_SILENT && outlevel < O_VERBOSE)
423     fprintf(stderr,"%s\n",buf);
424
425   return(ok);
426 }
427
428
429
430
431 /*********************************************************************
432   function:      POP3_sendRETR
433   description:   send the RETR command to the POP3 server.
434   arguments:     
435     msgnum       message ID number
436     socket       socket to which the POP3 server is connected.
437
438   return value:  return code from POP3_OK.
439   calls:         POP3_OK, SockPrintf
440   globals:       reads outlevel.
441  *********************************************************************/
442
443 int POP3_sendRETR (msgnum,socket)
444 int msgnum;
445 int socket;
446 {
447   int ok;
448   char buf [POPBUFSIZE];
449
450   SockPrintf(socket,"RETR %d\r\n",msgnum);
451   if (outlevel == O_VERBOSE)
452     fprintf(stderr,"> RETR %d\n",msgnum);
453   
454   ok = POP3_OK(buf,socket);
455   if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
456     fprintf(stderr,"%s\n",buf);
457
458   return(ok);
459 }
460
461
462 /*********************************************************************
463   function:      POP3_sendTOP
464   description:   send the TOP command to the POP3 server.
465   arguments:     
466     msgnum       message ID number
467     limit        maximum number of message body lines to retrieve.
468     socket       socket to which the POP3 server is connected.
469
470   return value:  return code from POP3_OK.
471   calls:         POP3_OK, SockPrintf
472   globals:       reads outlevel.
473  *********************************************************************/
474
475 int POP3_sendTOP (msgnum,limit,socket)
476 int msgnum;
477 int socket;
478 {
479   int ok;
480   char buf [POPBUFSIZE];
481
482   SockPrintf(socket,"TOP %d %d\r\n",msgnum,limit);
483   if (outlevel == O_VERBOSE)
484     fprintf(stderr,"> TOP %d %d\n",msgnum,limit);
485   
486   ok = POP3_OK(buf,socket);
487   if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
488     fprintf(stderr,"option --limit failed; server says '%s'\n",buf);
489
490   return(ok);
491 }
492
493
494
495
496 /*********************************************************************
497   function:      POP3_sendDELE
498   description:   send the DELE command to the POP3 server.
499   arguments:     
500     msgnum       message ID number
501     socket       socket to which the POP3 server is connected.
502
503   return value:  return code from POP3_OK.
504   calls:         POP3_OK, SockPrintF.
505   globals:       reads outlevel.
506  *********************************************************************/
507
508 int POP3_sendDELE (msgnum,socket)
509 int msgnum;
510 int socket;
511 {
512   int ok;
513   char buf [POPBUFSIZE];
514
515   SockPrintf(socket,"DELE %d\r\n",msgnum);
516   if (outlevel == O_VERBOSE)
517     fprintf(stderr,"> DELE %d\n",msgnum);
518   
519   ok = POP3_OK(buf,socket);
520   if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
521     fprintf(stderr,"%s\n",buf);
522
523   return(ok);
524 }
525
526
527
528 /*********************************************************************
529   function:      POP3_readmsg
530   description:   Read the message content as described in RFC 1225.
531   arguments:     
532     socket       ... to which the server is connected.
533     mboxfd       open file descriptor to which the retrieved message will
534                  be written. 
535     pophost      name of the POP host 
536     topipe       true if we're writing to the system mailbox pipe.
537
538   return value:  zero if success else PS_* return code.
539   calls:         SockGets.
540   globals:       reads outlevel. 
541  *********************************************************************/
542
543 int POP3_readmsg (socket,mboxfd,pophost,topipe,rewrite)
544 int socket;
545 int mboxfd;
546 char *pophost;
547 int topipe;
548 int rewrite;
549
550   char buf [MSGBUFSIZE]; 
551   char *bufp;
552   char savec;
553   char fromBuf[MSGBUFSIZE];
554   int needFrom;
555   int inheaders;
556   int lines,sizeticker;
557   time_t now;
558   /* This keeps the retrieved message count for display purposes */
559   static int msgnum = 0;  
560
561   /* set up for status message if outlevel allows it */
562   if (outlevel > O_SILENT && outlevel < O_VERBOSE) {
563     fprintf(stderr,"reading message %d",++msgnum);
564     /* won't do the '...' if retrieved messages are being sent to stdout */
565     if (mboxfd == 1)
566       fputs(".\n",stderr);
567     else
568       ;
569   }
570   else
571     ;
572
573   /* read the message content from the server */
574   inheaders = 1;
575   lines = 0;
576   sizeticker = MSGBUFSIZE;
577   while (1) {
578     if (SockGets(socket,buf,sizeof(buf)) < 0)
579       return(PS_SOCKET);
580     bufp = buf;
581     if (buf[0] == '\r' || buf[0] == '\n')
582       inheaders = 0;
583     if (*bufp == '.') {
584       bufp++;
585       if (*bufp == 0)
586         break;  /* end of message */
587     }
588     strcat(bufp,"\n");
589      
590     /* Check for Unix 'From' header, and add a bogus one if it's not
591        present -- only if not using an MDA.
592        XXX -- should probably parse real From: header and use its 
593               address field instead of bogus 'POPmail' string. 
594     */
595     if (!topipe && lines == 0) {
596       if (strlen(bufp) >= strlen("From ")) {
597         savec = *(bufp + 5);
598         *(bufp + 5) = 0;
599         needFrom = strcmp(bufp,"From ") != 0;
600         *(bufp + 5) = savec;
601       }
602       else
603         needFrom = 1;
604       if (needFrom) {
605         now = time(NULL);
606         sprintf(fromBuf,"From POPmail %s",ctime(&now));
607         if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) {
608           perror("POP3_readmsg: write");
609           return(PS_IOERR);
610         }
611       }
612     }
613
614     /*
615      * Edit some headers so that replies will work properly.
616      */
617     if (inheaders && rewrite)
618       reply_hack(bufp, pophost);
619
620     /* write this line to the file */
621     if (write(mboxfd,bufp,strlen(bufp)) < 0) {
622       perror("POP3_readmsg: write");
623       return(PS_IOERR);
624     }
625
626     sizeticker -= strlen(bufp);
627     if (sizeticker <= 0) {
628       if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
629         fputc('.',stderr);
630       sizeticker = MSGBUFSIZE;
631     }
632     lines++;
633   }
634
635   if (!topipe) {
636     /* The server may not write the extra newline required by the Unix
637        mail folder format, so we write one here just in case */
638     if (write(mboxfd,"\n",1) < 0) {
639       perror("POP3_readmsg: write");
640       return(PS_IOERR);
641     }
642   }
643   else {
644     /* The mail delivery agent may require a terminator.  Write it if
645        it has been defined */
646 #ifdef BINMAIL_TERM
647     if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
648       perror("POP3_readmsg: write");
649       return(PS_IOERR);
650     }
651 #endif
652     }
653
654   /* finish up display output */
655   if (outlevel == O_VERBOSE)
656     fprintf(stderr,"(%d lines of message content)\n",lines);
657   else if (outlevel > O_SILENT && mboxfd != 1) 
658     fputs(".\n",stderr);
659   else
660     ;
661   return(0);
662 }
663
664
665
666
667 /******************************************************************
668   function:     POP3_sendLAST
669   description:  send the LAST command to the server, which should
670                 return the number of the last message number retrieved 
671                 from the server.
672   arguments:
673     last        integer buffer to receive last message# 
674
675   ret. value:   non-zero on success, else zero.
676   globals:      SockPrintf, POP3_OK.
677   calls:        reads outlevel.
678  *****************************************************************/
679
680 int POP3_sendLAST (last, socket)
681 int *last;
682 int socket;
683 {
684   int ok;
685   char buf [POPBUFSIZE];
686
687   SockPrintf(socket,"LAST\r\n");
688   if (outlevel == O_VERBOSE)
689     fprintf(stderr,"> LAST\n");
690
691   ok = POP3_OK(buf,socket);
692   if (ok == 0 && sscanf(buf,"%d",last) == 0)
693     ok = PS_ERROR;
694
695   if (ok != 0 && outlevel > O_SILENT) 
696     fprintf(stderr,"Server says '%s' to LAST command.\n",buf);
697
698   return(ok);
699 }
700
701
702 /******************************************************************
703   function:     POP3_BuildDigest
704   description:  Construct the MD5 digest for the current session,
705                 using the user-specified password, and the time
706                 stamp in the POP3 greeting.
707   arguments:
708     buf         greeting string
709     queryctl    merged options record.
710
711   ret. value:   zero on success, nonzero if no timestamp found in
712                 greeting.
713   globals:      none.
714   calls:        MD5Digest.
715  *****************************************************************/
716
717 #if defined(HAVE_APOP_SUPPORT)
718 POP3_BuildDigest (buf,queryctl)
719 char *buf;
720 struct hostrec *queryctl;
721 {
722   char *start,*end;
723   char *msg;
724
725   /* find start of timestamp */
726   for (start = buf;  *start != 0 && *start != '<';  start++)
727     ;
728   if (*start == 0) {
729     fprintf(stderr,"Required APOP timestamp not found in greeting\n");
730     return(-1);
731   }
732
733   /* find end of timestamp */
734   for (end = start;  *end != 0  && *end != '>';  end++)
735     ;
736   if (*end == 0 || (end - start - 1) == 1) {
737     fprintf(stderr,"Timestamp syntax error in greeting\n");
738     return(-1);
739   }
740
741   /* copy timestamp and password into digestion buffer */
742   msg = (char *) malloc((end-start-1) + strlen(queryctl->password) + 1);
743   *(++end) = 0;
744   strcpy(msg,start);
745   strcat(msg,queryctl->password);
746
747   strcpy(queryctl->digest, MD5Digest(msg));
748   free(msg);
749   return(0);
750 }
751 #endif  /* HAVE_APOP_SUPPORT */
752