]> Pileus Git - ~andy/fetchmail/blob - pop2.c
optrec -> hostrec.
[~andy/fetchmail] / pop2.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:       pop2.c
8   project:      popclient
9   programmer:   Carl Harris, ceharris@mal.com
10                 Hacks and bug fixes by esr.
11   description:  POP2 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  <errno.h>
27
28 #include  "socket.h"
29 #include  "popclient.h"
30
31
32 /* TCP port number for POP2 as defined by RFC 937 */
33 #define   POP2_PORT     109
34
35 #if HAVE_PROTOTYPES
36 /* prototypes for internal functions */
37 int POP2_sendcmd (char *cmd, int socket);
38 int POP2_sendHELO (char *userid, char *password, int socket);
39 int POP2_sendFOLD (char *folder, int socket);
40 int POP2_quit (int socket);
41 int POP2_stateGREET (int socket);
42 int POP2_stateNMBR (int socket);
43 int POP2_stateSIZE (int socket);
44 int POP2_stateXFER (int msgsize, int socket, int mboxfd, int topipe);
45 #endif
46
47
48 /*********************************************************************
49   function:      doPOP2
50   description:   retrieve messages from the specified mail server
51                  using Post Office Protocol 2.
52
53   arguments:     
54     options      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:         POP2_stateGREET, POP2_stateNMBR, POP2_stateSIZE,
60                  POP2_stateXFER, POP2_sendcmd, POP2_sendHELO,
61                  POP2_sendFOLD, POP2_quit, Socket, openuserfolder,
62                  closeuserfolder, openmailpipe, closemailpipe.
63   globals:       reads outlevel.
64  *********************************************************************/
65
66 int doPOP2 (options)
67 struct hostrec *options;
68 {
69   int mboxfd;
70   int socket;
71   int number,msgsize,actsize;
72   int status = PS_UNDEFINED;
73
74   /* check for unsupported options */
75   if (linelimit) {
76     fprintf(stderr,"Option --limit is not supported in POP2\n");
77     return(PS_SYNTAX);
78   }
79   else if (options->flush) {
80     fprintf(stderr,"Option --flush is not supported in POP2\n");
81     return(PS_SYNTAX);
82   }
83   else if (options->fetchall) {
84     fprintf(stderr,"Option --all is not supported in POP2\n");
85     return(PS_SYNTAX);
86   }
87   else
88     ;
89
90   /* open the socket to the POP server */
91   if ((socket = Socket(options->servername,POP2_PORT)) < 0) {
92     perror("doPOP2: socket");
93     return(PS_SOCKET);
94   }
95     
96   /* open/lock the folder if it is a user folder or stdout */
97   if (options->output == TO_FOLDER)
98     if ((mboxfd = openuserfolder(options)) < 0) 
99       return(PS_IOERR);
100  
101   /* wait for the POP2 greeting */
102   if (POP2_stateGREET(socket) != 0) {
103     POP2_quit(socket);
104     status = PS_PROTOCOL;
105     goto closeUp;
106   }
107
108   /* log the user onto the server */
109   POP2_sendHELO(options->remotename,options->password,socket);
110   if ((number = POP2_stateNMBR(socket)) < 0) {
111     POP2_quit(socket);
112     status = PS_AUTHFAIL;
113     goto closeUp;
114   }
115
116   /* set the remote folder if selected */
117   if (*options->remotefolder != 0) {
118     POP2_sendFOLD(options->remotefolder,socket);
119     if ((number = POP2_stateNMBR(socket)) < 0) {
120       POP2_quit(socket);
121       status = PS_PROTOCOL;
122       goto closeUp;
123     }
124   }
125
126   /* tell 'em how many messages are waiting */
127   if (outlevel > O_SILENT && outlevel < O_VERBOSE)
128     fprintf(stderr,"%d messages in folder %s\n",number,options->remotefolder);
129   else
130     ;
131
132   /* fall into a retrieve/acknowledge loop */
133   if (number > 0) { 
134
135     POP2_sendcmd("READ",socket);
136     msgsize = POP2_stateSIZE(socket);
137     while (msgsize > 0) {
138
139       /* open the pipe */
140       if (options->output == TO_MDA)
141         if ((mboxfd = openmailpipe(options)) < 0) {   
142           POP2_quit(socket);
143           return(PS_IOERR);
144         }
145
146       POP2_sendcmd("RETR",socket);
147       actsize = POP2_stateXFER(msgsize,socket,mboxfd,
148                                options->output == TO_MDA);
149       if (actsize == msgsize) 
150         if (options->keep)
151           POP2_sendcmd("ACKS",socket);
152         else
153           POP2_sendcmd("ACKD",socket);
154       else if (actsize >= 0) 
155         POP2_sendcmd("NACK",socket);
156       else {
157         POP2_quit(socket);
158         status = PS_SOCKET;
159         goto closeUp; 
160       }
161
162       /* close the pipe */
163       if (options->output == TO_MDA)
164         if (closemailpipe(mboxfd) < 0) {
165           POP2_quit(socket);
166           status = PS_IOERR;
167           goto closeUp;
168         }
169     
170       msgsize = POP2_stateSIZE(socket);
171     }
172     POP2_quit(socket);
173     status = msgsize == 0 ? PS_SUCCESS : PS_PROTOCOL;
174   }
175   else {
176     POP2_quit(socket);
177     status = PS_NOMAIL;
178   }
179
180 closeUp:
181   if (options->output == TO_FOLDER)
182     closeuserfolder(mboxfd);
183
184   return(status);
185 }
186
187
188
189 /*********************************************************************
190   function:      POP2_sendcmd
191   description:   send a command string (with no arguments) a server.
192   arguments:     
193     cmd          command string to send.
194     socket       socket to which the server is connected.
195
196   return value:  none.
197   calls:         SockPuts.
198   globals:       reads outlevel.
199  *********************************************************************/
200
201 int POP2_sendcmd (cmd,socket) 
202 char *cmd;
203 int socket;
204 {
205   SockPuts(socket,cmd);
206
207   if (outlevel == O_VERBOSE)
208     fprintf(stderr,"> %s\n",cmd);
209   else
210     ;
211 }
212
213
214 /*********************************************************************
215   function:      POP2_sendHELO
216   description:   send the HELO command to the server.
217   arguments:     
218     userid       user's mailserver id.
219     password     user's mailserver password.
220     socket       socket to which the server is connected.
221
222   return value:  none.
223   calls:         SockPrintf.
224   globals:       read outlevel.
225  *********************************************************************/
226
227 int POP2_sendHELO (userid,password,socket) 
228 char *userid, *password;
229 int socket;
230 {
231   SockPrintf(socket,"HELO %s %s\r\n",userid,password);
232     
233
234   if (outlevel == O_VERBOSE)
235     fprintf(stderr,"> HELO %s password\n",userid);
236   else
237     ;
238 }
239
240
241 /*********************************************************************
242   function:      POP2_sendFOLD
243   description:   send the FOLD command to the server.
244   arguments:     
245     folder       name of the folder to open on the server.
246     socket       socket to which the server is connected.  
247
248   return value:  none.
249   calls:         SockPrintf.
250   globals:       reads outlevel.
251  *********************************************************************/
252
253 int POP2_sendFOLD (folder,socket)
254 char *folder;
255 int socket;
256 {
257   SockPrintf(socket,"FOLD %s\r\n",folder);
258
259   if (outlevel == O_VERBOSE)
260     fprintf(stderr,"> FOLD %s\n",folder);
261   else
262     ;
263 }
264
265
266 /*********************************************************************
267   function:      POP2_quit
268   description:   send the QUIT command to the server and close 
269                  the socket.
270
271   arguments:     
272     socket       socket to which the server is connected.
273
274   return value:  none.
275   calls:         SockPuts.
276   globals:       reads outlevel.
277  *********************************************************************/
278
279 int POP2_quit (socket)
280 int socket;
281 {
282   SockPuts(socket,"QUIT");
283   close(socket);
284
285   if (outlevel == O_VERBOSE)
286     fprintf(stderr,"> QUIT\n");
287   else
288     ;
289 }
290
291
292 /*********************************************************************
293   function:      POP2_stateGREET
294   description:   process the GREET state as described in RFC 937.
295   arguments:     
296     socket       ...to which server is connected.
297
298   return value:  zero if server's handling of the GREET state was 
299                  correct, else non-zero (may indicate difficulty
300                  at the socket).
301   calls:         SockGets.
302   globals:       reads outlevel.
303  *********************************************************************/
304
305 int POP2_stateGREET (socket)
306 int socket;
307 {
308   char buf [POPBUFSIZE];
309  
310   /* read the greeting from the server */
311   if (SockGets(socket, buf, sizeof(buf)) == 0) {
312
313     /* echo the server's greeting to the user */
314     if (outlevel > O_SILENT)
315       fprintf(stderr,"%s\n",buf);
316     else
317       ;
318     /* is the greeting in the correct format? */
319     if (*buf == '+')
320       return(0);
321     else
322       return(-1);
323   }
324   else {
325     /* an error at the socket */ 
326     if (outlevel > O_SILENT)
327       perror("error reading socket\n");
328     else
329       ;
330     return(-1);
331   }
332 }
333
334
335 /*********************************************************************
336   function:      POP2_stateNMBR
337   description:   process the NMBR state as described in RFC 937.
338   arguments:     
339     socket       ...to which the server is connected.
340
341   return value:  zero if the expected NMBR state action occured, else
342                  non-zero.  Following HELO, a non-zero return value 
343                  usually here means the user authorization at the server
344                  failed.
345   calls:         SockGets.
346   globals:       reads outlevel.
347  *********************************************************************/
348
349 int POP2_stateNMBR (socket)
350 int socket;
351 {
352   int number;
353   char buf [POPBUFSIZE];
354
355   /* read the NMBR (#ccc) message from the server */
356   if (SockGets(socket, buf, sizeof(buf)) == 0) {
357
358     /* is the message in the proper format? */
359     if (*buf == '#') {
360       number = atoi(buf + 1);
361       if (outlevel == O_VERBOSE)
362         fprintf(stderr,"%s\n",buf);
363       else
364         ;
365     }
366     else {
367       number = -1;
368       if (outlevel > O_SILENT) 
369         fprintf(stderr,"%s\n",buf);
370       else
371         ;
372     }
373   }
374   else {
375     /* socket problem */
376     number = -1;
377     if (outlevel == O_VERBOSE) 
378       perror("socket read error\n");
379     else
380       ;
381   }
382   return(number);
383 }
384
385
386 /*********************************************************************
387   function:      POP2_stateSIZE
388   description:   process the SIZE state as described in RFC 937.
389   arguments:     
390     socket       ...to which the server is connected.
391
392   return value:  zero if the expected SIZE state action occured, else
393                  non-zero (usually indicates a protocol violation).
394   calls:         SockGets.
395   globals:       reads outlevel.
396  *********************************************************************/
397
398 int POP2_stateSIZE (socket)
399 int socket;
400 {
401   int msgsize;
402   char buf [POPBUFSIZE];
403
404   /* read the SIZE message (=ccc) from the server */
405   if (SockGets(socket, buf, sizeof(buf)) == 0) 
406     /* is the message in the correct format? */
407     if (*buf == '=') {
408       msgsize = atoi(buf + 1);
409       if (outlevel == O_VERBOSE)
410         fprintf(stderr,"%s\n",buf);
411       else
412         ;
413     }
414     else {
415       msgsize = -1;
416       if (outlevel > O_SILENT) 
417         fprintf(stderr,"%s\n",buf);
418       else
419         ;
420     }
421   else {
422     /* socket problem */
423     msgsize = -1;
424     if (outlevel == O_VERBOSE) 
425       perror("socket read error\n");
426     else
427       ;
428   }
429
430   return(msgsize);
431 }
432
433
434 /*********************************************************************
435   function:      POP2_stateXFER
436   description:   process the XFER state as described in RFC 937.
437   arguments:     
438     msgsize      content length of the message as reported in the 
439                  SIZE state.
440     socket       ... to which the server is connected.
441     mboxfd       open file descriptor to which the retrieved message will
442                  be written.  
443     topipe       true if we're writing to a the /bin/mail pipe.
444
445   return value:  
446     >= 0         actual length of the message received. 
447     < 0          socket I/O problem.
448
449   calls:         SockRead.
450   globals:       reads outlevel. 
451  *********************************************************************/
452
453 int POP2_stateXFER (msgsize,socket,mboxfd,topipe)
454 int msgsize;
455 int socket;
456 int mboxfd;
457 int topipe;
458 {
459   int i,buflen,actsize;
460   char buf [MSGBUFSIZE]; 
461   char frombuf [MSGBUFSIZE];
462   char savec;
463   int msgTop;
464   int needFrom;
465   
466   time_t now;
467
468   /* This keeps the retrieved message count for display purposes */
469   static int msgnum = 0;  
470
471   /* set up for status message if outlevel allows it */
472   if (outlevel > O_SILENT && outlevel < O_VERBOSE) {
473     fprintf(stderr,"reading message %d",++msgnum);
474     /* won't do the '...' if retrieved messages are being sent to stdout */
475     if (mboxfd == 1)  /* we're writing to stdout */
476       fputs(".\n",stderr);
477     else
478       ;
479   }
480   else
481     ;
482
483
484   /* read the specified message content length from the server */
485   actsize = 0;
486   msgTop = !0;
487   while (msgsize > 0) {
488     buflen = msgsize <= MSGBUFSIZE ? msgsize : MSGBUFSIZE;
489     /* read a bufferful */ 
490     if (SockRead(socket, buf, buflen) == 0) {
491
492       /* Check for Unix 'From' header, and add bogus one if it's not
493          present -- only if not using an MDA.
494          XXX -- should probably parse real From: header and use its
495                 address field instead of bogus 'POPmail' string.
496       */
497       if (!topipe && msgTop) {
498         msgTop = 0;
499         if (strlen(buf) >= strlen("From ")) {
500           savec = *(buf + 5);
501           *(buf + 5) = 0;
502           needFrom = strcmp(buf,"From ") != 0;
503           *(buf + 5) = savec;
504         }
505         else
506           needFrom = 1;
507         if (needFrom) {
508           now = time(NULL);
509           sprintf(frombuf,"From POPmail %s",ctime(&now));
510           if (write(mboxfd,frombuf,strlen(frombuf)) < 0) {
511             perror("POP2_stateXFER: write");
512             return(-1);
513           }
514         }
515       }
516
517       /* write to folder, stripping CR chars in the process */
518       for (i = 0;  i < buflen;  i++)
519         if (*(buf + i) != '\r')
520           if (write(mboxfd,buf + i,1) < 0) {
521             perror("POP2_stateXFER: write");
522             return(-1);
523           }
524           else
525             ;  /* it was written */
526         else
527           ;  /* ignore CR character */
528     }
529     else
530       return(-1);   /* socket problem */
531
532     /* write another . for every bufferful received */
533     if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1) 
534       fputc('.',stderr);
535     else
536       ;
537     msgsize -= buflen;
538     actsize += buflen;
539   }
540
541   if (!topipe) {
542     /* The server may not write the extra newline required by the Unix
543        mail folder format, so we write one here just in case */
544     if (write(mboxfd,"\n",1) < 1) {
545       perror("POP2_stateXFER: write");
546       return(-1);
547     }
548   }
549   else {
550      /* the mailer might require some sort of termination string, send
551         it if it is defined */
552 #ifdef BINMAIL_TERM
553     if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
554       perror("POP2_stateXFER: write");
555       return(-1);
556     }
557 #endif
558   }
559
560   /* finish up display output */
561   if (outlevel == O_VERBOSE)
562     fprintf(stderr,"(%d characters of message content)\n",actsize);
563   else if (outlevel > O_SILENT && mboxfd != 0)
564     fputc('\n',stderr);
565   else
566     ;
567
568   return(actsize);
569 }