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: POP3 client code.
13 ***********************************************************************/
18 #if defined(STDC_HEADERS)
21 #if defined(HAVE_UNISTD_H)
30 #include "popclient.h"
34 #ifdef HAVE_PROTOTYPES
35 /* prototypes for internal functions */
36 int POP3_OK (char *buf, int socket);
37 int POP3_auth (struct hostrec *options, int socket);
38 int POP3_sendQUIT (int socket);
39 int POP3_sendSTAT (int *msgcount, int socket);
40 int POP3_sendRETR (int msgnum, int socket);
41 int POP3_sendDELE (int msgnum, int socket);
42 int POP3_sendLAST (int *last, int socket);
43 int POP3_readmsg (int socket, int mboxfd, char *host, int topipe, int rewrite);
44 int POP3_BuildDigest (char *buf, struct hostrec *options);
48 /*********************************************************************
50 description: retrieve messages from the specified mail server
51 using Post Office Protocol 3.
54 queryctl fully-specified options (i.e. parsed, defaults invoked,
57 return value: exit code from the set of PS_.* constants defined in
60 globals: reads outlevel.
61 *********************************************************************/
64 struct hostrec *queryctl;
68 char buf [POPBUFSIZE];
70 int first,number,count;
73 /* open/lock the folder if we're using a mailbox */
74 if (queryctl->output == TO_FOLDER)
75 if ((mboxfd = openuserfolder(queryctl)) < 0)
78 /* open the socket and get the greeting */
79 if ((socket = Socket(queryctl->servername,POP3_PORT)) < 0) {
80 perror("doPOP3: socket");
85 ok = POP3_OK(buf,socket);
88 POP3_sendQUIT(socket);
93 /* print the greeting */
94 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
95 fprintf(stderr,"%s\n",buf);
99 #if defined(HAVE_APOP_SUPPORT)
100 /* build MD5 digest from greeting timestamp + password */
101 if (queryctl->whichpop == P_APOP)
102 if (POP3_BuildDigest(buf,queryctl) != 0) {
108 ; /* not using APOP protocol this time */
111 /* try to get authorized */
112 ok = POP3_auth(queryctl,socket);
118 /* find out how many messages are waiting */
119 ok = POP3_sendSTAT(&count,socket);
124 /* Ask for number of last message retrieved */
125 if (queryctl->fetchall)
128 ok = POP3_sendLAST(&first, socket);
135 /* show them how many messages we'll be downloading */
136 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
138 fprintf(stderr,"%d messages in folder, %d new messages.\n",
139 count, count - first + 1);
141 fprintf(stderr,"%d new messages in folder.\n", count);
146 for (number = (queryctl->flush || queryctl->fetchall)? 1 : first;
150 /* open the mail pipe if we're using an MDA */
151 if (queryctl->output == TO_MDA
152 && (queryctl->fetchall || number >= first)) {
153 ok = (mboxfd = openmailpipe(queryctl)) < 0 ? -1 : 0;
158 if (queryctl->flush && number < first && !queryctl->fetchall)
159 ok = 0; /* no command to send here, will delete message below */
161 ok = POP3_sendTOP(number,linelimit,socket);
163 ok = POP3_sendRETR(number,socket);
167 if (number >= first || queryctl->fetchall)
168 ok = POP3_readmsg(socket,mboxfd,
169 queryctl->servername,
170 queryctl->output == TO_MDA,
177 if ((number < first && queryctl->flush) || !queryctl->keep) {
178 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
179 fprintf(stderr,"flushing message %d\n", number);
182 ok = POP3_sendDELE(number,socket);
187 ; /* message is kept */
189 /* close the mail pipe if we're using the system mailbox */
190 if (queryctl->output == TO_MDA
191 && (queryctl->fetchall || number >= first)) {
192 ok = closemailpipe(mboxfd);
198 ok = POP3_sendQUIT(socket);
205 ok = POP3_sendQUIT(socket);
213 if (ok != 0 && ok != PS_SOCKET)
214 POP3_sendQUIT(socket);
217 if (queryctl->output == TO_FOLDER)
218 if (closeuserfolder(mboxfd) < 0 && ok == 0)
221 if (ok == PS_IOERR || ok == PS_SOCKET)
222 perror("doPOP3: cleanUp");
229 /*********************************************************************
231 description: get the server's response to a command, and return
232 the extra arguments sent with the response.
234 argbuf buffer to receive the argument string.
235 socket socket to which the server is connected.
237 return value: zero if okay, else return code.
239 globals: reads outlevel.
240 *********************************************************************/
242 int POP3_OK (argbuf,socket)
247 char buf [POPBUFSIZE];
250 if (SockGets(socket, buf, sizeof(buf)) == 0) {
251 if (outlevel == O_VERBOSE)
252 fprintf(stderr,"%s\n",buf);
255 if (*bufp == '+' || *bufp == '-')
260 while (isalpha(*bufp))
264 if (strcmp(buf,"+OK") == 0)
266 else if (strcmp(buf,"-ERR") == 0)
282 /*********************************************************************
284 description: send the USER and PASS commands to the server, and
285 get the server's response.
287 queryctl merged options record.
288 socket socket to which the server is connected.
290 return value: zero if success, else status code.
291 calls: SockPrintf, POP3_OK.
292 globals: read outlevel.
293 *********************************************************************/
295 int POP3_auth (queryctl,socket)
296 struct hostrec *queryctl;
299 char buf [POPBUFSIZE];
301 switch (queryctl->protocol) {
303 SockPrintf(socket,"USER %s\r\n",queryctl->remotename);
304 if (outlevel == O_VERBOSE)
305 fprintf(stderr,"> USER %s\n",queryctl->remotename);
306 if (POP3_OK(buf,socket) != 0)
309 SockPrintf(socket,"PASS %s\r\n",queryctl->password);
310 if (outlevel == O_VERBOSE)
311 fprintf(stderr,"> PASS password\n");
312 if (POP3_OK(buf,socket) != 0)
317 #if defined(HAVE_APOP_SUPPORT)
319 SockPrintf(socket,"APOP %s %s\r\n",
320 queryctl->remotename, queryctl->digest);
321 if (outlevel == O_VERBOSE)
322 fprintf(stderr,"> APOP %s %s\n",queryctl->remotename, queryctl->digest);
323 if (POP3_OK(buf,socket) != 0)
326 #endif /* HAVE_APOP_SUPPORT */
328 #if defined(HAVE_RPOP_SUPPORT)
330 SockPrintf(socket, "RPOP %s\r\n", queryctl->remotename);
331 if (POP3_OK(buf,socket) != 0)
333 if (outlevel == O_VERBOSE)
334 fprintf(stderr,"> RPOP %s %s\n",queryctl->remotename);
336 #endif /* HAVE_RPOP_SUPPORT */
339 fprintf(stderr,"Undefined protocol request in POP3_auth\n");
348 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
349 fprintf(stderr,"%s\n",buf);
359 /*********************************************************************
360 function: POP3_sendQUIT
361 description: send the QUIT command to the server and close
365 socket socket to which the server is connected.
368 calls: SockPuts, POP3_OK.
369 globals: reads outlevel.
370 *********************************************************************/
372 int POP3_sendQUIT (socket)
376 char buf [POPBUFSIZE];
378 SockPuts(socket,"QUIT");
380 if (outlevel == O_VERBOSE)
381 fprintf(stderr,"> QUIT\n");
385 ok = POP3_OK(buf,socket);
386 if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
387 fprintf(stderr,"%s\n",buf);
394 /*********************************************************************
395 function: POP3_sendSTAT
396 description: send the STAT command to the POP3 server to find
397 out how many messages are waiting.
399 count pointer to an integer to receive the message count.
400 socket socket to which the POP3 server is connected.
402 return value: return code from POP3_OK.
403 calls: POP3_OK, SockPrintf
404 globals: reads outlevel.
405 *********************************************************************/
407 int POP3_sendSTAT (msgcount,socket)
412 char buf [POPBUFSIZE];
415 SockPrintf(socket,"STAT\r\n");
416 if (outlevel == O_VERBOSE)
417 fprintf(stderr,"> STAT\n");
419 ok = POP3_OK(buf,socket);
421 sscanf(buf,"%d %d",msgcount,&totalsize);
422 else if (outlevel > O_SILENT && outlevel < O_VERBOSE)
423 fprintf(stderr,"%s\n",buf);
431 /*********************************************************************
432 function: POP3_sendRETR
433 description: send the RETR command to the POP3 server.
435 msgnum message ID number
436 socket socket to which the POP3 server is connected.
438 return value: return code from POP3_OK.
439 calls: POP3_OK, SockPrintf
440 globals: reads outlevel.
441 *********************************************************************/
443 int POP3_sendRETR (msgnum,socket)
448 char buf [POPBUFSIZE];
450 SockPrintf(socket,"RETR %d\r\n",msgnum);
451 if (outlevel == O_VERBOSE)
452 fprintf(stderr,"> RETR %d\n",msgnum);
454 ok = POP3_OK(buf,socket);
455 if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
456 fprintf(stderr,"%s\n",buf);
462 /*********************************************************************
463 function: POP3_sendTOP
464 description: send the TOP command to the POP3 server.
466 msgnum message ID number
467 limit maximum number of message body lines to retrieve.
468 socket socket to which the POP3 server is connected.
470 return value: return code from POP3_OK.
471 calls: POP3_OK, SockPrintf
472 globals: reads outlevel.
473 *********************************************************************/
475 int POP3_sendTOP (msgnum,limit,socket)
480 char buf [POPBUFSIZE];
482 SockPrintf(socket,"TOP %d %d\r\n",msgnum,limit);
483 if (outlevel == O_VERBOSE)
484 fprintf(stderr,"> TOP %d %d\n",msgnum,limit);
486 ok = POP3_OK(buf,socket);
487 if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
488 fprintf(stderr,"option --limit failed; server says '%s'\n",buf);
496 /*********************************************************************
497 function: POP3_sendDELE
498 description: send the DELE command to the POP3 server.
500 msgnum message ID number
501 socket socket to which the POP3 server is connected.
503 return value: return code from POP3_OK.
504 calls: POP3_OK, SockPrintF.
505 globals: reads outlevel.
506 *********************************************************************/
508 int POP3_sendDELE (msgnum,socket)
513 char buf [POPBUFSIZE];
515 SockPrintf(socket,"DELE %d\r\n",msgnum);
516 if (outlevel == O_VERBOSE)
517 fprintf(stderr,"> DELE %d\n",msgnum);
519 ok = POP3_OK(buf,socket);
520 if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
521 fprintf(stderr,"%s\n",buf);
528 /*********************************************************************
529 function: POP3_readmsg
530 description: Read the message content as described in RFC 1225.
532 socket ... to which the server is connected.
533 mboxfd open file descriptor to which the retrieved message will
535 pophost name of the POP host
536 topipe true if we're writing to the system mailbox pipe.
538 return value: zero if success else PS_* return code.
540 globals: reads outlevel.
541 *********************************************************************/
543 int POP3_readmsg (socket,mboxfd,pophost,topipe,rewrite)
550 char buf [MSGBUFSIZE];
553 char fromBuf[MSGBUFSIZE];
556 int lines,sizeticker;
558 /* This keeps the retrieved message count for display purposes */
559 static int msgnum = 0;
561 /* set up for status message if outlevel allows it */
562 if (outlevel > O_SILENT && outlevel < O_VERBOSE) {
563 fprintf(stderr,"reading message %d",++msgnum);
564 /* won't do the '...' if retrieved messages are being sent to stdout */
573 /* read the message content from the server */
576 sizeticker = MSGBUFSIZE;
578 if (SockGets(socket,buf,sizeof(buf)) < 0)
581 if (buf[0] == '\r' || buf[0] == '\n')
586 break; /* end of message */
590 /* Check for Unix 'From' header, and add a bogus one if it's not
591 present -- only if not using an MDA.
592 XXX -- should probably parse real From: header and use its
593 address field instead of bogus 'POPmail' string.
595 if (!topipe && lines == 0) {
596 if (strlen(bufp) >= strlen("From ")) {
599 needFrom = strcmp(bufp,"From ") != 0;
606 sprintf(fromBuf,"From POPmail %s",ctime(&now));
607 if (write(mboxfd,fromBuf,strlen(fromBuf)) < 0) {
608 perror("POP3_readmsg: write");
615 * Edit some headers so that replies will work properly.
617 if (inheaders && rewrite)
618 reply_hack(bufp, pophost);
620 /* write this line to the file */
621 if (write(mboxfd,bufp,strlen(bufp)) < 0) {
622 perror("POP3_readmsg: write");
626 sizeticker -= strlen(bufp);
627 if (sizeticker <= 0) {
628 if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
630 sizeticker = MSGBUFSIZE;
636 /* The server may not write the extra newline required by the Unix
637 mail folder format, so we write one here just in case */
638 if (write(mboxfd,"\n",1) < 0) {
639 perror("POP3_readmsg: write");
644 /* The mail delivery agent may require a terminator. Write it if
645 it has been defined */
647 if (write(mboxfd,BINMAIL_TERM,strlen(BINMAIL_TERM)) < 0) {
648 perror("POP3_readmsg: write");
654 /* finish up display output */
655 if (outlevel == O_VERBOSE)
656 fprintf(stderr,"(%d lines of message content)\n",lines);
657 else if (outlevel > O_SILENT && mboxfd != 1)
667 /******************************************************************
668 function: POP3_sendLAST
669 description: send the LAST command to the server, which should
670 return the number of the last message number retrieved
673 last integer buffer to receive last message#
675 ret. value: non-zero on success, else zero.
676 globals: SockPrintf, POP3_OK.
677 calls: reads outlevel.
678 *****************************************************************/
680 int POP3_sendLAST (last, socket)
685 char buf [POPBUFSIZE];
687 SockPrintf(socket,"LAST\r\n");
688 if (outlevel == O_VERBOSE)
689 fprintf(stderr,"> LAST\n");
691 ok = POP3_OK(buf,socket);
692 if (ok == 0 && sscanf(buf,"%d",last) == 0)
695 if (ok != 0 && outlevel > O_SILENT)
696 fprintf(stderr,"Server says '%s' to LAST command.\n",buf);
702 /******************************************************************
703 function: POP3_BuildDigest
704 description: Construct the MD5 digest for the current session,
705 using the user-specified password, and the time
706 stamp in the POP3 greeting.
709 queryctl merged options record.
711 ret. value: zero on success, nonzero if no timestamp found in
715 *****************************************************************/
717 #if defined(HAVE_APOP_SUPPORT)
718 POP3_BuildDigest (buf,queryctl)
720 struct hostrec *queryctl;
725 /* find start of timestamp */
726 for (start = buf; *start != 0 && *start != '<'; start++)
729 fprintf(stderr,"Required APOP timestamp not found in greeting\n");
733 /* find end of timestamp */
734 for (end = start; *end != 0 && *end != '>'; end++)
736 if (*end == 0 || (end - start - 1) == 1) {
737 fprintf(stderr,"Timestamp syntax error in greeting\n");
741 /* copy timestamp and password into digestion buffer */
742 msg = (char *) malloc((end-start-1) + strlen(queryctl->password) + 1);
745 strcat(msg,queryctl->password);
747 strcpy(queryctl->digest, MD5Digest(msg));
751 #endif /* HAVE_APOP_SUPPORT */