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