]> Pileus Git - ~andy/fetchmail/blob - pop2.c
Initial revision
[~andy/fetchmail] / pop2.c
1 /* Copyright 1993-95 by Carl Harris, Jr.
2  * All rights reserved
3  *
4  * Distribute freely, except: don't remove my name from the source or
5  * documentation (don't take credit for my work), mark your changes (don't
6  * get me blamed for your possible bugs), don't alter or remove this
7  * notice.  May be sold if buildable source is provided to buyer.  No
8  * warrantee of any kind, express or implied, is included with this
9  * software; use at your own risk, responsibility for damages (if any) to
10  * anyone resulting from the use of this software rests entirely with the
11  * user.
12  *
13  * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
14  * I'll try to keep a version up to date.  I can be reached as follows:
15  * Carl Harris <ceharris@mal.com>
16  */
17
18
19 /***********************************************************************
20   module:       pop2.c
21   project:      popclient
22   programmer:   Carl Harris, ceharris@mal.com
23   description:  POP2 client code.
24
25   $Log: pop2.c,v $
26   Revision 1.1  1996/06/24 19:00:51  esr
27   Initial revision
28
29   Revision 1.6  1995/08/14 18:36:40  ceharris
30   Patches to support POP3's LAST command.
31   Final revisions for beta3 release.
32
33   Revision 1.5  1995/08/10 00:32:36  ceharris
34   Preparation for 3.0b3 beta release:
35   -     added code for --kill/--keep, --limit, --protocol, --flush
36         options; --pop2 and --pop3 options now obsoleted by --protocol.
37   -     added support for APOP authentication, including --with-APOP
38         argument for configure.
39   -     provisional and broken support for RPOP
40   -     added buffering to SockGets and SockRead functions.
41   -     fixed problem of command-line options not being correctly
42         carried into the merged options record.
43
44   Revision 1.4  1995/08/09 01:32:53  ceharris
45   Version 3.0 beta 2 release.
46   Added
47   -     .poprc functionality
48   -     GNU long options
49   -     multiple servers on the command line.
50   Fixed
51   -     Passwords showing up in ps output.
52
53   Revision 1.3  1995/08/08 01:01:22  ceharris
54   Added GNU-style long options processing.
55   Fixed password in 'ps' output problem.
56   Fixed various RCS tag blunders.
57   Integrated .poprc parser, lexer, etc into Makefile processing.
58
59  ***********************************************************************/
60
61 #include  <config.h>
62
63 #include  <stdio.h>
64 #if defined(STDC_HEADERS)
65 #include  <string.h>
66 #endif
67 #if defined(HAVE_UNISTD_H)
68 #include  <unistd.h>
69 #endif
70
71 #include  <sys/time.h>
72 #include  <errno.h>
73
74 #include  "socket.h"
75 #include  "popclient.h"
76
77
78 /* TCP port number for POP2 as defined by RFC 937 */
79 #define   POP2_PORT     109
80
81 #if HAVE_PROTOTYPES
82 /* prototypes for internal functions */
83 int POP2_sendcmd (char *cmd, int socket);
84 int POP2_sendHELO (char *userid, char *password, int socket);
85 int POP2_sendFOLD (char *folder, int socket);
86 int POP2_quit (int socket);
87 int POP2_stateGREET (int socket);
88 int POP2_stateNMBR (int socket);
89 int POP2_stateSIZE (int socket);
90 int POP2_stateXFER (int msgsize, int socket, int mboxfd, int topipe);
91 #endif
92
93
94 /*********************************************************************
95   function:      doPOP2
96   description:   retrieve messages from the specified mail server
97                  using Post Office Protocol 2.
98
99   arguments:     
100     servername   name of the server to which we'll connect.
101     options      fully-specified options (i.e. parsed, defaults invoked,
102                  etc).
103
104   return value:  exit code from the set of PS_.* constants defined in 
105                  popclient.h
106   calls:         POP2_stateGREET, POP2_stateNMBR, POP2_stateSIZE,
107                  POP2_stateXFER, POP2_sendcmd, POP2_sendHELO,
108                  POP2_sendFOLD, POP2_quit, Socket, openuserfolder,
109                  closeuserfolder, openmailpipe, closemailpipe.
110   globals:       reads outlevel.
111  *********************************************************************/
112
113 int doPOP2 (servername,options)
114 char *servername;
115 struct optrec *options;
116 {
117   int mboxfd;
118   int socket;
119   int number,msgsize,actsize;
120   int status = PS_UNDEFINED;
121
122   /* check for unsupported options */
123   if (options->limit) {
124     fprintf(stderr,"Option --limit is not supported in POP2\n");
125     return(PS_SYNTAX);
126   }
127   else if (options->flush) {
128     fprintf(stderr,"Option --flush is not supported in POP2\n");
129     return(PS_SYNTAX);
130   }
131   else if (options->fetchall) {
132     fprintf(stderr,"Option --all is not supported in POP2\n");
133     return(PS_SYNTAX);
134   }
135   else
136     ;
137
138   /* open the socket to the POP server */
139   if ((socket = Socket(servername,POP2_PORT)) < 0) {
140     perror("doPOP2: socket");
141     return(PS_SOCKET);
142   }
143     
144   /* open/lock the folder if it is a user folder or stdout */
145   if (options->foldertype != OF_SYSMBOX)
146     if ((mboxfd = openuserfolder(options)) < 0) 
147       return(PS_IOERR);
148  
149   /* wait for the POP2 greeting */
150   if (POP2_stateGREET(socket) != 0) {
151     POP2_quit(socket);
152     return(PS_PROTOCOL);
153   }
154
155   /* log the user onto the server */
156   POP2_sendHELO(options->userid,options->password,socket);
157   if ((number = POP2_stateNMBR(socket)) < 0) {
158     POP2_quit(socket);
159     return(PS_AUTHFAIL);
160   }
161
162   /* set the remote folder if selected */
163   if (*options->remotefolder != 0) {
164     POP2_sendFOLD(options->remotefolder,socket);
165     if ((number = POP2_stateNMBR(socket)) < 0) {
166       POP2_quit(socket);
167       return(PS_PROTOCOL);
168     }
169   }
170
171   /* tell 'em how many messages are waiting */
172   if (outlevel > O_SILENT && outlevel < O_VERBOSE)
173     fprintf(stderr,"%d messages in folder %s\n",number,options->remotefolder);
174   else
175     ;
176
177   /* fall into a retrieve/acknowledge loop */
178   if (number > 0) { 
179
180     POP2_sendcmd("READ",socket);
181     msgsize = POP2_stateSIZE(socket);
182     while (msgsize > 0) {
183
184       /* open the pipe */
185       if (options->foldertype == OF_SYSMBOX)
186         if ((mboxfd = openmailpipe(options)) < 0) {   
187           POP2_quit(socket);
188           return(PS_IOERR);
189         }
190
191       POP2_sendcmd("RETR",socket);
192       actsize = POP2_stateXFER(msgsize,socket,mboxfd,
193                                options->foldertype == OF_SYSMBOX);
194       if (actsize == msgsize) 
195         if (options->keep)
196           POP2_sendcmd("ACKS",socket);
197         else
198           POP2_sendcmd("ACKD",socket);
199       else if (actsize >= 0) 
200         POP2_sendcmd("NACK",socket);
201       else {
202         POP2_quit(socket);
203         return(PS_SOCKET);
204       }
205
206       /* close the pipe */
207       if (options->foldertype == OF_SYSMBOX)
208         if (closemailpipe(mboxfd) < 0) {
209           POP2_quit(socket);
210           return(PS_IOERR);
211         }
212     
213       msgsize = POP2_stateSIZE(socket);
214     }
215     POP2_quit(socket);
216     status = msgsize == 0 ? PS_SUCCESS : PS_PROTOCOL;
217   }
218   else {
219     POP2_quit(socket);
220     status = PS_NOMAIL;
221   }
222
223   if (options->foldertype != OF_SYSMBOX)
224     closeuserfolder(mboxfd);
225
226   return(status);
227 }
228
229
230
231 /*********************************************************************
232   function:      POP2_sendcmd
233   description:   send a command string (with no arguments) a server.
234   arguments:     
235     cmd          command string to send.
236     socket       socket to which the server is connected.
237
238   return value:  none.
239   calls:         SockPuts.
240   globals:       reads outlevel.
241  *********************************************************************/
242
243 int POP2_sendcmd (cmd,socket) 
244 char *cmd;
245 int socket;
246 {
247   SockPuts(socket,cmd);
248
249   if (outlevel == O_VERBOSE)
250     fprintf(stderr,"> %s\n",cmd);
251   else
252     ;
253 }
254
255
256 /*********************************************************************
257   function:      POP2_sendHELO
258   description:   send the HELO command to the server.
259   arguments:     
260     userid       user's mailserver id.
261     password     user's mailserver password.
262     socket       socket to which the server is connected.
263
264   return value:  none.
265   calls:         SockPrintf.
266   globals:       read outlevel.
267  *********************************************************************/
268
269 int POP2_sendHELO (userid,password,socket) 
270 char *userid, *password;
271 int socket;
272 {
273   SockPrintf(socket,"HELO %s %s\r\n",userid,password);
274     
275
276   if (outlevel == O_VERBOSE)
277     fprintf(stderr,"> HELO %s password\n",userid);
278   else
279     ;
280 }
281
282
283 /*********************************************************************
284   function:      POP2_sendFOLD
285   description:   send the FOLD command to the server.
286   arguments:     
287     folder       name of the folder to open on the server.
288     socket       socket to which the server is connected.  
289
290   return value:  none.
291   calls:         SockPrintf.
292   globals:       reads outlevel.
293  *********************************************************************/
294
295 int POP2_sendFOLD (folder,socket)
296 char *folder;
297 int socket;
298 {
299   SockPrintf(socket,"FOLD %s\r\n",folder);
300
301   if (outlevel == O_VERBOSE)
302     fprintf(stderr,"> FOLD %s\n",folder);
303   else
304     ;
305 }
306
307
308 /*********************************************************************
309   function:      POP2_quit
310   description:   send the QUIT command to the server and close 
311                  the socket.
312
313   arguments:     
314     socket       socket to which the server is connected.
315
316   return value:  none.
317   calls:         SockPuts.
318   globals:       reads outlevel.
319  *********************************************************************/
320
321 int POP2_quit (socket)
322 int socket;
323 {
324   SockPuts(socket,"QUIT");
325   close(socket);
326
327   if (outlevel == O_VERBOSE)
328     fprintf(stderr,"> QUIT\n");
329   else
330     ;
331 }
332
333
334 /*********************************************************************
335   function:      POP2_stateGREET
336   description:   process the GREET state as described in RFC 937.
337   arguments:     
338     socket       ...to which server is connected.
339
340   return value:  zero if server's handling of the GREET state was 
341                  correct, else non-zero (may indicate difficulty
342                  at the socket).
343   calls:         SockGets.
344   globals:       reads outlevel.
345  *********************************************************************/
346
347 int POP2_stateGREET (socket)
348 int socket;
349 {
350   char buf [POPBUFSIZE];
351  
352   /* read the greeting from the server */
353   if (SockGets(socket, buf, sizeof(buf)) == 0) {
354
355     /* echo the server's greeting to the user */
356     if (outlevel > O_SILENT)
357       fprintf(stderr,"%s\n",buf);
358     else
359       ;
360     /* is the greeting in the correct format? */
361     if (*buf == '+')
362       return(0);
363     else
364       return(-1);
365   }
366   else {
367     /* an error at the socket */ 
368     if (outlevel > O_SILENT)
369       perror("error reading socket\n");
370     else
371       ;
372     return(-1);
373   }
374 }
375
376
377 /*********************************************************************
378   function:      POP2_stateNMBR
379   description:   process the NMBR state as described in RFC 937.
380   arguments:     
381     socket       ...to which the server is connected.
382
383   return value:  zero if the expected NMBR state action occured, else
384                  non-zero.  Following HELO, a non-zero return value 
385                  usually here means the user authorization at the server
386                  failed.
387   calls:         SockGets.
388   globals:       reads outlevel.
389  *********************************************************************/
390
391 int POP2_stateNMBR (socket)
392 int socket;
393 {
394   int number;
395   char buf [POPBUFSIZE];
396
397   /* read the NMBR (#ccc) message from the server */
398   if (SockGets(socket, buf, sizeof(buf)) == 0) {
399
400     /* is the message in the proper format? */
401     if (*buf == '#') {
402       number = atoi(buf + 1);
403       if (outlevel == O_VERBOSE)
404         fprintf(stderr,"%s\n",buf);
405       else
406         ;
407     }
408     else {
409       number = -1;
410       if (outlevel > O_SILENT) 
411         fprintf(stderr,"%s\n",buf);
412       else
413         ;
414     }
415   }
416   else {
417     /* socket problem */
418     number = -1;
419     if (outlevel == O_VERBOSE) 
420       perror("socket read error\n");
421     else
422       ;
423   }
424   return(number);
425 }
426
427
428 /*********************************************************************
429   function:      POP2_stateSIZE
430   description:   process the SIZE state as described in RFC 937.
431   arguments:     
432     socket       ...to which the server is connected.
433
434   return value:  zero if the expected SIZE state action occured, else
435                  non-zero (usually indicates a protocol violation).
436   calls:         SockGets.
437   globals:       reads outlevel.
438  *********************************************************************/
439
440 int POP2_stateSIZE (socket)
441 int socket;
442 {
443   int msgsize;
444   char buf [POPBUFSIZE];
445
446   /* read the SIZE message (=ccc) from the server */
447   if (SockGets(socket, buf, sizeof(buf)) == 0) 
448     /* is the message in the correct format? */
449     if (*buf == '=') {
450       msgsize = atoi(buf + 1);
451       if (outlevel == O_VERBOSE)
452         fprintf(stderr,"%s\n",buf);
453       else
454         ;
455     }
456     else {
457       msgsize = -1;
458       if (outlevel > O_SILENT) 
459         fprintf(stderr,"%s\n",buf);
460       else
461         ;
462     }
463   else {
464     /* socket problem */
465     msgsize = -1;
466     if (outlevel == O_VERBOSE) 
467       perror("socket read error\n");
468     else
469       ;
470   }
471
472   return(msgsize);
473 }
474
475
476 /*********************************************************************
477   function:      POP2_stateXFER
478   description:   process the XFER state as described in RFC 937.
479   arguments:     
480     msgsize      content length of the message as reported in the 
481                  SIZE state.
482     socket       ... to which the server is connected.
483     mboxfd       open file descriptor to which the retrieved message will
484                  be written.  
485     topipe       true if we're writing to a the /bin/mail pipe.
486
487   return value:  
488     >= 0         actual length of the message received. 
489     < 0          socket I/O problem.
490
491   calls:         SockRead.
492   globals:       reads outlevel. 
493  *********************************************************************/
494
495 int POP2_stateXFER (msgsize,socket,mboxfd,topipe)
496 int msgsize;
497 int socket;
498 int mboxfd;
499 int topipe;
500 {
501   int i,buflen,actsize;
502   char buf [MSGBUFSIZE]; 
503   char frombuf [MSGBUFSIZE];
504   char savec;
505   int msgTop;
506   int needFrom;
507   
508   time_t now;
509
510   /* This keeps the retrieved message count for display purposes */
511   static int msgnum = 0;  
512
513   /* set up for status message if outlevel allows it */
514   if (outlevel > O_SILENT && outlevel < O_VERBOSE) {
515     fprintf(stderr,"reading message %d",++msgnum);
516     /* won't do the '...' if retrieved messages are being sent to stdout */
517     if (mboxfd == 1)  /* we're writing to stdout */
518       fputs(".\n",stderr);
519     else
520       ;
521   }
522   else
523     ;
524
525
526   /* read the specified message content length from the server */
527   actsize = 0;
528   msgTop = !0;
529   while (msgsize > 0) {
530     buflen = msgsize <= MSGBUFSIZE ? msgsize : MSGBUFSIZE;
531     /* read a bufferful */ 
532     if (SockRead(socket, buf, buflen) == 0) {
533
534       /* Check for Unix 'From' header, and add bogus one if it's not
535          present -- only if not using an MDA.
536          XXX -- should probably parse real From: header and use its
537                 address field instead of bogus 'POPmail' string.
538       */
539       if (!topipe && msgTop) {
540         msgTop = 0;
541         if (strlen(buf) >= strlen("From ")) {
542           savec = *(buf + 5);
543           *(buf + 5) = 0;
544           needFrom = strcmp(buf,"From ") != 0;
545           *(buf + 5) = savec;
546         }
547         else
548           needFrom = 1;
549         if (needFrom) {
550           now = time(NULL);
551           sprintf(frombuf,"From POPmail %s",ctime(&now));
552           if (write(mboxfd,frombuf,strlen(frombuf)) < 0) {
553             perror("POP2_stateXFER: write");
554             return(-1);
555           }
556         }
557       }
558
559       /* write to folder, stripping CR chars in the process */
560       for (i = 0;  i < buflen;  i++)
561         if (*(buf + i) != '\r')
562           if (write(mboxfd,buf + i,1) < 0) {
563             perror("POP2_stateXFER: write");
564             return(-1);
565           }
566           else
567             ;  /* it was written */
568         else
569           ;  /* ignore CR character */
570     }
571     else
572       return(-1);   /* socket problem */
573
574     /* write another . for every bufferful received */
575     if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1) 
576       fputc('.',stderr);
577     else
578       ;
579     msgsize -= buflen;
580     actsize += buflen;
581   }
582
583   if (!topipe) {
584     /* The server may not write the extra newline required by the Unix
585        mail folder format, so we write one here just in case */
586     if (write(mboxfd,"\n",1) < 1) {
587       perror("POP2_stateXFER: write");
588       return(-1);
589     }
590   }
591   else {
592      /* the mailer might require some sort of termination string, send
593         it if it is defined */
594 #ifdef BINMAIL_TERM
595     if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
596       perror("POP2_stateXFER: write");
597       return(-1);
598     }
599 #endif
600   }
601
602   /* finish up display output */
603   if (outlevel == O_VERBOSE)
604     fprintf(stderr,"(%d characters of message content)\n",actsize);
605   else if (outlevel > O_SILENT && mboxfd != 0)
606     fputc('\n',stderr);
607   else
608     ;
609
610   return(actsize);
611 }