1 /* Copyright 1993-95 by Carl Harris, Jr. Copyright 1996 by Eric S. Raymond
3 * For license terms, see the file COPYING in this directory.
6 /***********************************************************************
9 programmer: Carl Harris, ceharris@mal.com
10 Hacks and bug fixes by esr.
11 description: POP2 client code.
13 ***********************************************************************/
18 #if defined(STDC_HEADERS)
21 #if defined(HAVE_UNISTD_H)
29 #include "popclient.h"
32 /* TCP port number for POP2 as defined by RFC 937 */
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);
48 /*********************************************************************
50 description: retrieve messages from the specified mail server
51 using Post Office Protocol 2.
54 options fully-specified options (i.e. parsed, defaults invoked,
57 return value: exit code from the set of PS_.* constants defined in
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 *********************************************************************/
67 struct hostrec *options;
71 int number,msgsize,actsize;
72 int status = PS_UNDEFINED;
74 /* check for unsupported options */
76 fprintf(stderr,"Option --limit is not supported in POP2\n");
79 else if (options->flush) {
80 fprintf(stderr,"Option --flush is not supported in POP2\n");
83 else if (options->fetchall) {
84 fprintf(stderr,"Option --all is not supported in POP2\n");
90 /* open the socket to the POP server */
91 if ((socket = Socket(options->servername,POP2_PORT)) < 0) {
92 perror("doPOP2: socket");
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)
101 /* wait for the POP2 greeting */
102 if (POP2_stateGREET(socket) != 0) {
104 status = PS_PROTOCOL;
108 /* log the user onto the server */
109 POP2_sendHELO(options->remotename,options->password,socket);
110 if ((number = POP2_stateNMBR(socket)) < 0) {
112 status = PS_AUTHFAIL;
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) {
121 status = PS_PROTOCOL;
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);
132 /* fall into a retrieve/acknowledge loop */
135 POP2_sendcmd("READ",socket);
136 msgsize = POP2_stateSIZE(socket);
137 while (msgsize > 0) {
140 if (options->output == TO_MDA)
141 if ((mboxfd = openmailpipe(options)) < 0) {
146 POP2_sendcmd("RETR",socket);
147 actsize = POP2_stateXFER(msgsize,socket,mboxfd,
148 options->output == TO_MDA);
149 if (actsize == msgsize)
151 POP2_sendcmd("ACKS",socket);
153 POP2_sendcmd("ACKD",socket);
154 else if (actsize >= 0)
155 POP2_sendcmd("NACK",socket);
163 if (options->output == TO_MDA)
164 if (closemailpipe(mboxfd) < 0) {
170 msgsize = POP2_stateSIZE(socket);
173 status = msgsize == 0 ? PS_SUCCESS : PS_PROTOCOL;
181 if (options->output == TO_FOLDER)
182 closeuserfolder(mboxfd);
189 /*********************************************************************
190 function: POP2_sendcmd
191 description: send a command string (with no arguments) a server.
193 cmd command string to send.
194 socket socket to which the server is connected.
198 globals: reads outlevel.
199 *********************************************************************/
201 int POP2_sendcmd (cmd,socket)
205 SockPuts(socket,cmd);
207 if (outlevel == O_VERBOSE)
208 fprintf(stderr,"> %s\n",cmd);
214 /*********************************************************************
215 function: POP2_sendHELO
216 description: send the HELO command to the server.
218 userid user's mailserver id.
219 password user's mailserver password.
220 socket socket to which the server is connected.
224 globals: read outlevel.
225 *********************************************************************/
227 int POP2_sendHELO (userid,password,socket)
228 char *userid, *password;
231 SockPrintf(socket,"HELO %s %s\r\n",userid,password);
234 if (outlevel == O_VERBOSE)
235 fprintf(stderr,"> HELO %s password\n",userid);
241 /*********************************************************************
242 function: POP2_sendFOLD
243 description: send the FOLD command to the server.
245 folder name of the folder to open on the server.
246 socket socket to which the server is connected.
250 globals: reads outlevel.
251 *********************************************************************/
253 int POP2_sendFOLD (folder,socket)
257 SockPrintf(socket,"FOLD %s\r\n",folder);
259 if (outlevel == O_VERBOSE)
260 fprintf(stderr,"> FOLD %s\n",folder);
266 /*********************************************************************
268 description: send the QUIT command to the server and close
272 socket socket to which the server is connected.
276 globals: reads outlevel.
277 *********************************************************************/
279 int POP2_quit (socket)
282 SockPuts(socket,"QUIT");
285 if (outlevel == O_VERBOSE)
286 fprintf(stderr,"> QUIT\n");
292 /*********************************************************************
293 function: POP2_stateGREET
294 description: process the GREET state as described in RFC 937.
296 socket ...to which server is connected.
298 return value: zero if server's handling of the GREET state was
299 correct, else non-zero (may indicate difficulty
302 globals: reads outlevel.
303 *********************************************************************/
305 int POP2_stateGREET (socket)
308 char buf [POPBUFSIZE];
310 /* read the greeting from the server */
311 if (SockGets(socket, buf, sizeof(buf)) == 0) {
313 /* echo the server's greeting to the user */
314 if (outlevel > O_SILENT)
315 fprintf(stderr,"%s\n",buf);
318 /* is the greeting in the correct format? */
325 /* an error at the socket */
326 if (outlevel > O_SILENT)
327 perror("error reading socket\n");
335 /*********************************************************************
336 function: POP2_stateNMBR
337 description: process the NMBR state as described in RFC 937.
339 socket ...to which the server is connected.
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
346 globals: reads outlevel.
347 *********************************************************************/
349 int POP2_stateNMBR (socket)
353 char buf [POPBUFSIZE];
355 /* read the NMBR (#ccc) message from the server */
356 if (SockGets(socket, buf, sizeof(buf)) == 0) {
358 /* is the message in the proper format? */
360 number = atoi(buf + 1);
361 if (outlevel == O_VERBOSE)
362 fprintf(stderr,"%s\n",buf);
368 if (outlevel > O_SILENT)
369 fprintf(stderr,"%s\n",buf);
377 if (outlevel == O_VERBOSE)
378 perror("socket read error\n");
386 /*********************************************************************
387 function: POP2_stateSIZE
388 description: process the SIZE state as described in RFC 937.
390 socket ...to which the server is connected.
392 return value: zero if the expected SIZE state action occured, else
393 non-zero (usually indicates a protocol violation).
395 globals: reads outlevel.
396 *********************************************************************/
398 int POP2_stateSIZE (socket)
402 char buf [POPBUFSIZE];
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? */
408 msgsize = atoi(buf + 1);
409 if (outlevel == O_VERBOSE)
410 fprintf(stderr,"%s\n",buf);
416 if (outlevel > O_SILENT)
417 fprintf(stderr,"%s\n",buf);
424 if (outlevel == O_VERBOSE)
425 perror("socket read error\n");
434 /*********************************************************************
435 function: POP2_stateXFER
436 description: process the XFER state as described in RFC 937.
438 msgsize content length of the message as reported in the
440 socket ... to which the server is connected.
441 mboxfd open file descriptor to which the retrieved message will
443 topipe true if we're writing to a the /bin/mail pipe.
446 >= 0 actual length of the message received.
447 < 0 socket I/O problem.
450 globals: reads outlevel.
451 *********************************************************************/
453 int POP2_stateXFER (msgsize,socket,mboxfd,topipe)
459 int i,buflen,actsize;
460 char buf [MSGBUFSIZE];
461 char frombuf [MSGBUFSIZE];
468 /* This keeps the retrieved message count for display purposes */
469 static int msgnum = 0;
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 */
484 /* read the specified message content length from the server */
487 while (msgsize > 0) {
488 buflen = msgsize <= MSGBUFSIZE ? msgsize : MSGBUFSIZE;
489 /* read a bufferful */
490 if (SockRead(socket, buf, buflen) == 0) {
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.
497 if (!topipe && msgTop) {
499 if (strlen(buf) >= strlen("From ")) {
502 needFrom = strcmp(buf,"From ") != 0;
509 sprintf(frombuf,"From POPmail %s",ctime(&now));
510 if (write(mboxfd,frombuf,strlen(frombuf)) < 0) {
511 perror("POP2_stateXFER: write");
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");
525 ; /* it was written */
527 ; /* ignore CR character */
530 return(-1); /* socket problem */
532 /* write another . for every bufferful received */
533 if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
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");
550 /* the mailer might require some sort of termination string, send
551 it if it is defined */
553 if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
554 perror("POP2_stateXFER: write");
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)