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 queryctl 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 *queryctl;
71 int number,msgsize,actsize;
72 int status = PS_UNDEFINED;
74 /* check for unsupported options */
76 fprintf(stderr,"Option --limit is not supported with POP2\n");
79 else if (queryctl->flush) {
80 fprintf(stderr,"Option --flush is not supported with POP2\n");
83 else if (queryctl->fetchall) {
84 fprintf(stderr,"Option --all is not supported with POP2\n");
87 else if (queryctl->smtphost[0]) {
88 fprintf(stderr,"Option --smtphost is not supported with POP2\n");
94 /* open the socket to the POP server */
95 if ((socket = Socket(queryctl->servername,
96 queryctl->port ? queryctl->port : POP2_PORT)) < 0)
98 perror("doPOP2: socket");
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)
107 /* wait for the POP2 greeting */
108 if (POP2_stateGREET(socket) != 0) {
110 status = PS_PROTOCOL;
114 /* log the user onto the server */
115 POP2_sendHELO(queryctl->remotename,queryctl->password,socket);
116 if ((number = POP2_stateNMBR(socket)) < 0) {
118 status = PS_AUTHFAIL;
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) {
127 status = PS_PROTOCOL;
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);
138 /* fall into a retrieve/acknowledge loop */
141 POP2_sendcmd("READ",socket);
142 msgsize = POP2_stateSIZE(socket);
143 while (msgsize > 0) {
146 if (queryctl->output == TO_MDA)
147 if ((mboxfd = openmailpipe(queryctl)) < 0) {
152 POP2_sendcmd("RETR",socket);
153 actsize = POP2_stateXFER(msgsize,socket,mboxfd,
154 queryctl->output == TO_MDA);
155 if (actsize == msgsize)
157 POP2_sendcmd("ACKS",socket);
159 POP2_sendcmd("ACKD",socket);
160 else if (actsize >= 0)
161 POP2_sendcmd("NACK",socket);
169 if (queryctl->output == TO_MDA)
170 if (closemailpipe(mboxfd) < 0) {
176 msgsize = POP2_stateSIZE(socket);
179 status = msgsize == 0 ? PS_SUCCESS : PS_PROTOCOL;
187 if (queryctl->output == TO_FOLDER)
188 closeuserfolder(mboxfd);
195 /*********************************************************************
196 function: POP2_sendcmd
197 description: send a command string (with no arguments) a server.
199 cmd command string to send.
200 socket socket to which the server is connected.
204 globals: reads outlevel.
205 *********************************************************************/
207 int POP2_sendcmd (cmd,socket)
211 SockPuts(socket,cmd);
213 if (outlevel == O_VERBOSE)
214 fprintf(stderr,"> %s\n",cmd);
220 /*********************************************************************
221 function: POP2_sendHELO
222 description: send the HELO command to the server.
224 userid user's mailserver id.
225 password user's mailserver password.
226 socket socket to which the server is connected.
230 globals: read outlevel.
231 *********************************************************************/
233 int POP2_sendHELO (userid,password,socket)
234 char *userid, *password;
237 SockPrintf(socket,"HELO %s %s\r\n",userid,password);
240 if (outlevel == O_VERBOSE)
241 fprintf(stderr,"> HELO %s password\n",userid);
247 /*********************************************************************
248 function: POP2_sendFOLD
249 description: send the FOLD command to the server.
251 folder name of the folder to open on the server.
252 socket socket to which the server is connected.
256 globals: reads outlevel.
257 *********************************************************************/
259 int POP2_sendFOLD (folder,socket)
263 SockPrintf(socket,"FOLD %s\r\n",folder);
265 if (outlevel == O_VERBOSE)
266 fprintf(stderr,"> FOLD %s\n",folder);
272 /*********************************************************************
274 description: send the QUIT command to the server and close
278 socket socket to which the server is connected.
282 globals: reads outlevel.
283 *********************************************************************/
285 int POP2_quit (socket)
288 SockPuts(socket,"QUIT");
291 if (outlevel == O_VERBOSE)
292 fprintf(stderr,"> QUIT\n");
298 /*********************************************************************
299 function: POP2_stateGREET
300 description: process the GREET state as described in RFC 937.
302 socket ...to which server is connected.
304 return value: zero if server's handling of the GREET state was
305 correct, else non-zero (may indicate difficulty
308 globals: reads outlevel.
309 *********************************************************************/
311 int POP2_stateGREET (socket)
314 char buf [POPBUFSIZE];
316 /* read the greeting from the server */
317 if (SockGets(socket, buf, sizeof(buf)) >= 0) {
319 /* echo the server's greeting to the user */
320 if (outlevel > O_SILENT)
321 fprintf(stderr,"POP2 greeting: %s\n",buf);
324 /* is the greeting in the correct format? */
331 /* an error at the socket */
332 if (outlevel > O_SILENT)
333 perror("error reading socket\n");
341 /*********************************************************************
342 function: POP2_stateNMBR
343 description: process the NMBR state as described in RFC 937.
345 socket ...to which the server is connected.
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
352 globals: reads outlevel.
353 *********************************************************************/
355 int POP2_stateNMBR (socket)
359 char buf [POPBUFSIZE];
361 /* read the NMBR (#ccc) message from the server */
362 if (SockGets(socket, buf, sizeof(buf)) >= 0) {
364 /* is the message in the proper format? */
366 number = atoi(buf + 1);
367 if (outlevel == O_VERBOSE)
368 fprintf(stderr,"%s\n",buf);
374 if (outlevel > O_SILENT)
375 fprintf(stderr,"%s\n",buf);
383 if (outlevel == O_VERBOSE)
384 perror("socket read error\n");
392 /*********************************************************************
393 function: POP2_stateSIZE
394 description: process the SIZE state as described in RFC 937.
396 socket ...to which the server is connected.
398 return value: zero if the expected SIZE state action occured, else
399 non-zero (usually indicates a protocol violation).
401 globals: reads outlevel.
402 *********************************************************************/
404 int POP2_stateSIZE (socket)
408 char buf [POPBUFSIZE];
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? */
414 msgsize = atoi(buf + 1);
415 if (outlevel == O_VERBOSE)
416 fprintf(stderr,"%s\n",buf);
422 if (outlevel > O_SILENT)
423 fprintf(stderr,"%s\n",buf);
430 if (outlevel == O_VERBOSE)
431 perror("socket read error\n");
440 /*********************************************************************
441 function: POP2_stateXFER
442 description: process the XFER state as described in RFC 937.
444 msgsize content length of the message as reported in the
446 socket ... to which the server is connected.
447 mboxfd open file descriptor to which the retrieved message will
449 topipe true if we're writing to a the /bin/mail pipe.
452 >= 0 actual length of the message received.
453 < 0 socket I/O problem.
456 globals: reads outlevel.
457 *********************************************************************/
459 int POP2_stateXFER (msgsize,socket,mboxfd,topipe)
465 int i,buflen,actsize;
466 char buf [MSGBUFSIZE];
467 char frombuf [MSGBUFSIZE];
474 /* This keeps the retrieved message count for display purposes */
475 static int msgnum = 0;
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 */
490 /* read the specified message content length from the server */
493 while (msgsize > 0) {
494 buflen = msgsize <= MSGBUFSIZE ? msgsize : MSGBUFSIZE;
495 /* read a bufferful */
496 if (SockRead(socket, buf, buflen) == 0) {
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.
503 if (!topipe && msgTop) {
505 if (strlen(buf) >= strlen("From ")) {
508 needFrom = strcmp(buf,"From ") != 0;
515 sprintf(frombuf,"From POPmail %s",ctime(&now));
516 if (write(mboxfd,frombuf,strlen(frombuf)) < 0) {
517 perror("POP2_stateXFER: write");
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");
531 ; /* it was written */
533 ; /* ignore CR character */
536 return(-1); /* socket problem */
538 /* write another . for every bufferful received */
539 if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
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");
556 /* the mailer might require some sort of termination string, send
557 it if it is defined */
559 if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
560 perror("POP2_stateXFER: write");
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)