1 /* Copyright 1993-95 by Carl Harris, Jr.
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
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>
19 /***********************************************************************
22 programmer: Carl Harris, ceharris@mal.com
23 description: POP2 client code.
26 Revision 1.1 1996/06/24 19:00:51 esr
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.
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.
44 Revision 1.4 1995/08/09 01:32:53 ceharris
45 Version 3.0 beta 2 release.
47 - .poprc functionality
49 - multiple servers on the command line.
51 - Passwords showing up in ps output.
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.
59 ***********************************************************************/
64 #if defined(STDC_HEADERS)
67 #if defined(HAVE_UNISTD_H)
75 #include "popclient.h"
78 /* TCP port number for POP2 as defined by RFC 937 */
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);
94 /*********************************************************************
96 description: retrieve messages from the specified mail server
97 using Post Office Protocol 2.
100 servername name of the server to which we'll connect.
101 options fully-specified options (i.e. parsed, defaults invoked,
104 return value: exit code from the set of PS_.* constants defined in
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 *********************************************************************/
113 int doPOP2 (servername,options)
115 struct optrec *options;
119 int number,msgsize,actsize;
120 int status = PS_UNDEFINED;
122 /* check for unsupported options */
123 if (options->limit) {
124 fprintf(stderr,"Option --limit is not supported in POP2\n");
127 else if (options->flush) {
128 fprintf(stderr,"Option --flush is not supported in POP2\n");
131 else if (options->fetchall) {
132 fprintf(stderr,"Option --all is not supported in POP2\n");
138 /* open the socket to the POP server */
139 if ((socket = Socket(servername,POP2_PORT)) < 0) {
140 perror("doPOP2: socket");
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)
149 /* wait for the POP2 greeting */
150 if (POP2_stateGREET(socket) != 0) {
155 /* log the user onto the server */
156 POP2_sendHELO(options->userid,options->password,socket);
157 if ((number = POP2_stateNMBR(socket)) < 0) {
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) {
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);
177 /* fall into a retrieve/acknowledge loop */
180 POP2_sendcmd("READ",socket);
181 msgsize = POP2_stateSIZE(socket);
182 while (msgsize > 0) {
185 if (options->foldertype == OF_SYSMBOX)
186 if ((mboxfd = openmailpipe(options)) < 0) {
191 POP2_sendcmd("RETR",socket);
192 actsize = POP2_stateXFER(msgsize,socket,mboxfd,
193 options->foldertype == OF_SYSMBOX);
194 if (actsize == msgsize)
196 POP2_sendcmd("ACKS",socket);
198 POP2_sendcmd("ACKD",socket);
199 else if (actsize >= 0)
200 POP2_sendcmd("NACK",socket);
207 if (options->foldertype == OF_SYSMBOX)
208 if (closemailpipe(mboxfd) < 0) {
213 msgsize = POP2_stateSIZE(socket);
216 status = msgsize == 0 ? PS_SUCCESS : PS_PROTOCOL;
223 if (options->foldertype != OF_SYSMBOX)
224 closeuserfolder(mboxfd);
231 /*********************************************************************
232 function: POP2_sendcmd
233 description: send a command string (with no arguments) a server.
235 cmd command string to send.
236 socket socket to which the server is connected.
240 globals: reads outlevel.
241 *********************************************************************/
243 int POP2_sendcmd (cmd,socket)
247 SockPuts(socket,cmd);
249 if (outlevel == O_VERBOSE)
250 fprintf(stderr,"> %s\n",cmd);
256 /*********************************************************************
257 function: POP2_sendHELO
258 description: send the HELO command to the server.
260 userid user's mailserver id.
261 password user's mailserver password.
262 socket socket to which the server is connected.
266 globals: read outlevel.
267 *********************************************************************/
269 int POP2_sendHELO (userid,password,socket)
270 char *userid, *password;
273 SockPrintf(socket,"HELO %s %s\r\n",userid,password);
276 if (outlevel == O_VERBOSE)
277 fprintf(stderr,"> HELO %s password\n",userid);
283 /*********************************************************************
284 function: POP2_sendFOLD
285 description: send the FOLD command to the server.
287 folder name of the folder to open on the server.
288 socket socket to which the server is connected.
292 globals: reads outlevel.
293 *********************************************************************/
295 int POP2_sendFOLD (folder,socket)
299 SockPrintf(socket,"FOLD %s\r\n",folder);
301 if (outlevel == O_VERBOSE)
302 fprintf(stderr,"> FOLD %s\n",folder);
308 /*********************************************************************
310 description: send the QUIT command to the server and close
314 socket socket to which the server is connected.
318 globals: reads outlevel.
319 *********************************************************************/
321 int POP2_quit (socket)
324 SockPuts(socket,"QUIT");
327 if (outlevel == O_VERBOSE)
328 fprintf(stderr,"> QUIT\n");
334 /*********************************************************************
335 function: POP2_stateGREET
336 description: process the GREET state as described in RFC 937.
338 socket ...to which server is connected.
340 return value: zero if server's handling of the GREET state was
341 correct, else non-zero (may indicate difficulty
344 globals: reads outlevel.
345 *********************************************************************/
347 int POP2_stateGREET (socket)
350 char buf [POPBUFSIZE];
352 /* read the greeting from the server */
353 if (SockGets(socket, buf, sizeof(buf)) == 0) {
355 /* echo the server's greeting to the user */
356 if (outlevel > O_SILENT)
357 fprintf(stderr,"%s\n",buf);
360 /* is the greeting in the correct format? */
367 /* an error at the socket */
368 if (outlevel > O_SILENT)
369 perror("error reading socket\n");
377 /*********************************************************************
378 function: POP2_stateNMBR
379 description: process the NMBR state as described in RFC 937.
381 socket ...to which the server is connected.
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
388 globals: reads outlevel.
389 *********************************************************************/
391 int POP2_stateNMBR (socket)
395 char buf [POPBUFSIZE];
397 /* read the NMBR (#ccc) message from the server */
398 if (SockGets(socket, buf, sizeof(buf)) == 0) {
400 /* is the message in the proper format? */
402 number = atoi(buf + 1);
403 if (outlevel == O_VERBOSE)
404 fprintf(stderr,"%s\n",buf);
410 if (outlevel > O_SILENT)
411 fprintf(stderr,"%s\n",buf);
419 if (outlevel == O_VERBOSE)
420 perror("socket read error\n");
428 /*********************************************************************
429 function: POP2_stateSIZE
430 description: process the SIZE state as described in RFC 937.
432 socket ...to which the server is connected.
434 return value: zero if the expected SIZE state action occured, else
435 non-zero (usually indicates a protocol violation).
437 globals: reads outlevel.
438 *********************************************************************/
440 int POP2_stateSIZE (socket)
444 char buf [POPBUFSIZE];
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? */
450 msgsize = atoi(buf + 1);
451 if (outlevel == O_VERBOSE)
452 fprintf(stderr,"%s\n",buf);
458 if (outlevel > O_SILENT)
459 fprintf(stderr,"%s\n",buf);
466 if (outlevel == O_VERBOSE)
467 perror("socket read error\n");
476 /*********************************************************************
477 function: POP2_stateXFER
478 description: process the XFER state as described in RFC 937.
480 msgsize content length of the message as reported in the
482 socket ... to which the server is connected.
483 mboxfd open file descriptor to which the retrieved message will
485 topipe true if we're writing to a the /bin/mail pipe.
488 >= 0 actual length of the message received.
489 < 0 socket I/O problem.
492 globals: reads outlevel.
493 *********************************************************************/
495 int POP2_stateXFER (msgsize,socket,mboxfd,topipe)
501 int i,buflen,actsize;
502 char buf [MSGBUFSIZE];
503 char frombuf [MSGBUFSIZE];
510 /* This keeps the retrieved message count for display purposes */
511 static int msgnum = 0;
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 */
526 /* read the specified message content length from the server */
529 while (msgsize > 0) {
530 buflen = msgsize <= MSGBUFSIZE ? msgsize : MSGBUFSIZE;
531 /* read a bufferful */
532 if (SockRead(socket, buf, buflen) == 0) {
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.
539 if (!topipe && msgTop) {
541 if (strlen(buf) >= strlen("From ")) {
544 needFrom = strcmp(buf,"From ") != 0;
551 sprintf(frombuf,"From POPmail %s",ctime(&now));
552 if (write(mboxfd,frombuf,strlen(frombuf)) < 0) {
553 perror("POP2_stateXFER: write");
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");
567 ; /* it was written */
569 ; /* ignore CR character */
572 return(-1); /* socket problem */
574 /* write another . for every bufferful received */
575 if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
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");
592 /* the mailer might require some sort of termination string, send
593 it if it is defined */
595 if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
596 perror("POP2_stateXFER: write");
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)