1 /* Copyright 1996 by Eric S. Raymond
3 * For license terms, see the file COPYING in this directory.
6 /***********************************************************************
9 programmer: Eric S. Raymond
10 description: Generic driver for mail fetch method protocols
12 ***********************************************************************/
22 #include "fetchmail.h"
25 #define SMTP_PORT 25 /* standard SMTP service port */
27 static struct method *protocol;
29 static int alarmed; /* A flag to indicate that SIGALRM happened */
30 int timeout = CLIENT_TIMEOUT;
34 #define GENSYM (sprintf(tag, "a%04d", ++tagnum), tag)
36 /*********************************************************************
38 description: hack message headers so replies will work properly
41 after where to put the hacked header
43 host name of the pop header
47 *********************************************************************/
49 static void reply_hack(buf, host)
50 /* hack local mail IDs -- code by Eric S. Raymond 20 Jun 1996 */
56 char mycopy[POPBUFSIZE+1];
58 if (strncmp("From: ", buf, 6)
59 && strncmp("To: ", buf, 4)
60 && strncmp("Reply-", buf, 6)
61 && strncmp("Cc: ", buf, 4)
62 && strncmp("Bcc: ", buf, 5)) {
67 for (from = mycopy; *from; from++)
71 case 0: /* before header colon */
76 case 1: /* we've seen the colon, we're looking for addresses */
79 else if (*from == '(')
81 else if (*from == '<' || isalnum(*from))
85 case 2: /* we're in a quoted human name, copy and ignore */
90 case 3: /* we're in a parenthesized human name, copy and ignore */
95 case 4: /* the real work gets done here */
97 * We're in something that might be an address part,
98 * either a bare unquoted/unparenthesized text or text
99 * enclosed in <> as per RFC822.
101 /* if the address part contains an @, don't mess with it */
105 /* If the address token is not properly terminated, ignore it. */
106 else if (*from == ' ' || *from == '\t')
110 * On proper termination with no @, insert hostname.
111 * Case '>' catches <>-enclosed mail IDs. Case ',' catches
112 * comma-separated bare IDs. Cases \r and \n catch the case
113 * of a single ID alone on the line.
115 else if (strchr(">,\r\n", *from))
123 /* everything else, including alphanumerics, just passes through */
126 case 5: /* we're in a remote mail ID, no need to append hostname */
127 if (*from == '>' || *from == ',' || isspace(*from))
132 /* all characters from the old buffer get copied to the new one */
138 /*********************************************************************
140 description: Parse addresses in succession out of a specified RFC822
141 header. Note 1: RFC822 escaping with \ is *not* handled.
142 Note 2: it is important that this routine not stop on \r,
143 since we use \r as a marker for RFC822 continuations below.
145 hdr header line to be parsed, NUL to continue in previous hdr
147 return value: next address, or NUL if there is no next address
149 *********************************************************************/
151 static char *nxtaddr(hdr)
154 static char *hp, *tp, address[POPBUFSIZE+1];
167 case 0: /* before header colon */
177 case 1: /* we've seen the colon, now grab the address */
178 if ((*hp == '\n') || (*hp == ',')) /* end of address list */
183 else if (*hp == '"') /* quoted string */
188 else if (*hp == '(') /* address comment -- ignore */
190 else if (*hp == '<') /* begin <address> */
195 else if (isspace(*hp)) /* ignore space */
197 else /* just take it */
204 case 2: /* we're in a quoted string, copy verbatim */
216 case 3: /* we're in a parenthesized comment, ignore */
223 case 4: /* possible <>-enclosed address */
224 if (*hp == '>') /* end of address */
230 else if (*hp == '<') /* nested <> */
232 else if (*hp == '"') /* quoted address */
237 else /* just copy address */
241 case 5: /* we're in a quoted address, copy verbatim */
242 if (*hp == '\n') /* mismatched quotes */
244 if (*hp != '"') /* just copy it if it isn't a quote */
246 else if (*hp == '"') /* end of quoted string */
258 /*********************************************************************
259 function: gen_readmsg
260 description: Read the message content
262 as described in RFC 1225.
264 socket ... to which the server is connected.
265 mboxfd open file descriptor to which the retrieved message will
268 delimited does the protocol use a message delimiter?
269 queryctl host control block
271 return value: zero if success else PS_* return code.
273 globals: reads outlevel.
274 *********************************************************************/
276 static int gen_readmsg (socket, mboxfd, len, delimited, queryctl)
281 struct hostrec *queryctl;
283 char buf [MSGBUFSIZE+1];
284 char fromBuf[MSGBUFSIZE+1];
285 char *bufp, *headers, *unixfrom, *fromhdr, *tohdr, *cchdr, *bcchdr;
288 int lines,sizeticker;
290 /* This keeps the retrieved message count for display purposes */
291 static int msgnum = 0;
293 /* set up for status message if outlevel allows it */
294 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
295 fprintf(stderr,"reading message %d",++msgnum);
297 /* read the message content from the server */
299 headers = unixfrom = fromhdr = tohdr = cchdr = bcchdr = NULL;
302 while (delimited || len > 0) {
303 if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
307 if (buf[0] == '\0' || buf[0] == '\r' || buf[0] == '\n')
311 if (delimited && *bufp == 0)
312 break; /* end of message */
318 if (!queryctl->norewrite)
319 reply_hack(bufp, queryctl->servername);
323 oldlen = strlen(bufp);
324 headers = malloc(oldlen + 1);
327 (void) strcpy(headers, bufp);
335 * We deal with RFC822 continuation lines here.
336 * Replace previous '\n' with '\r' so nxtaddr
337 * and reply_hack will be able to see past it.
338 * (We know this safe because SocketGets stripped
339 * out all carriage returns in the read loop above
340 * and we haven't reintroduced any since then.)
341 * We'll undo this before writing the header.
343 if (isspace(bufp[0]))
344 headers[oldlen-1] = '\r';
346 newlen = oldlen + strlen(bufp);
347 headers = realloc(headers, newlen + 1);
350 strcpy(headers + oldlen, bufp);
351 bufp = headers + oldlen;
355 if (!strncmp(bufp,"From ",5))
357 else if (!strncasecmp("From: ", bufp, 6))
359 else if (!strncasecmp("To: ", bufp, 4))
361 else if (!strncasecmp("Cc: ", bufp, 4))
363 else if (!strncasecmp("Bcc: ", bufp, 5))
372 if (!queryctl->mda[0])
374 if (SMTP_from(mboxfd, nxtaddr(fromhdr)) != SM_OK)
378 * This is what we'd do if fetchmail were a real MDA
379 * a la sendmail -- crack all the destination headers
380 * and send to every address we can reach via SMTP.
382 if (tohdr && (cp = nxtaddr(tohdr)) != (char *)NULL)
384 if (SMTP_rcpt(mboxfd, cp) == SM_UNRECOVERABLE)
387 (cp = nxtaddr(NULL));
388 if (cchdr && (cp = nxtaddr(cchdr)) != (char *)NULL)
390 if (SMTP_rcpt(mboxfd, cp) == SM_UNRECOVERABLE)
393 (cp = nxtaddr(NULL));
394 if (bcchdr && (cp = nxtaddr(bcchdr)) != (char *)NULL)
396 if (SMTP_rcpt(mboxfd, cp) == SM_UNRECOVERABLE)
399 (cp = nxtaddr(NULL));
402 * Since we're really only fetching mail for one user
403 * per host query, we can be simpler
405 if (SMTP_rcpt(mboxfd, queryctl->localname) == SM_UNRECOVERABLE)
407 #endif /* SMTP_RESEND */
409 if (outlevel == O_VERBOSE)
410 fputs("SMTP> ", stderr);
413 /* change continuation markers back to regular newlines */
414 for (cp = headers; cp < headers + oldlen; cp++)
419 * Strictly speaking, we ought to replace all LFs
420 * with CR-LF before shipping to an SMTP listener.
421 * Since most SMTP listeners built since the mid-1980s
422 * (and *all* Unix listeners) accept lone LF as equivalent
423 * to CR-LF, we'll skip this particular contortion.
425 if (write(mboxfd,headers,oldlen) < 0)
429 perror("gen_readmsg: writing RFC822 headers");
432 else if (outlevel == O_VERBOSE)
438 /* SMTP byte-stuffing */
439 if (*bufp == '.' && queryctl->mda[0] == 0)
440 write(mboxfd, ".", 1);
442 /* write this line to the file -- comment about CR-LF vs. LF applies */
443 if (write(mboxfd,bufp,strlen(bufp)) < 0)
445 perror("gen_readmsg: writing message text");
448 else if (outlevel == O_VERBOSE)
453 /* write the message size dots */
454 sizeticker += strlen(bufp);
455 while (sizeticker >= SIZETICKER)
457 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
459 sizeticker -= SIZETICKER;
461 /* reset timeout so we don't choke on very long messages */
468 /* finish up display output */
469 if (outlevel == O_VERBOSE)
470 fprintf(stderr,"\n(%d lines of message content)\n",lines);
471 else if (outlevel > O_SILENT)
476 /* write message terminator */
477 if (!queryctl->mda[0])
478 if (SMTP_eom(mboxfd) != SM_OK)
483 /*********************************************************************
484 function: do_protocol
485 description: retrieve messages from the specified mail server
486 using a given set of methods
489 queryctl fully-specified options (i.e. parsed, defaults invoked,
491 proto protocol method pointer
493 return value: exit code from the set of PS_.* constants defined in
496 globals: reads outlevel.
497 *********************************************************************/
499 int do_protocol(queryctl, proto)
500 struct hostrec *queryctl;
501 struct method *proto;
505 char buf [POPBUFSIZE+1], host[HOSTLEN+1];
509 #ifdef HAVE_RRESVPORT_H
511 #endif /* HAVE_RRESVPORT_H */
512 int num, count, deletions = 0;
515 sigsave = signal(SIGALRM, alarm_handler);
517 /* lacking methods, there are some options that may fail */
520 /* check for unsupported options */
521 if (queryctl->flush) {
523 "Option --flush is not supported with %s\n",
526 signal(SIGALRM, sigsave);
529 else if (queryctl->fetchall) {
531 "Option --all is not supported with %s\n",
534 signal(SIGALRM, sigsave);
542 /* open a socket to the mail server */
543 if ((socket = Socket(queryctl->servername,
544 queryctl->port ? queryctl->port : protocol->port))<0
547 perror("fetchmail, connecting to host");
552 /* accept greeting message from mail server */
553 ok = (protocol->parse_response)(socket, buf);
554 if (alarmed || ok != 0)
557 /* try to get authorized to fetch mail */
558 ok = (protocol->getauth)(socket, queryctl, buf);
559 if (alarmed || ok == PS_ERROR)
561 if (alarmed || ok != 0)
564 /* compute count, and get UID list if possible */
565 if ((protocol->getrange)(socket, queryctl, &count) != 0 || alarmed)
568 /* show user how many messages we downloaded */
569 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
571 fprintf(stderr, "No mail from %s\n", queryctl->servername);
574 "%d message%s from %s.\n",
575 count, count > 1 ? "s" : "",
576 queryctl->servername);
580 if (queryctl->mda[0] == '\0')
581 if ((mboxfd = Socket(queryctl->smtphost, SMTP_PORT)) < 0
582 || SMTP_ok(mboxfd, NULL) != SM_OK
583 || SMTP_helo(mboxfd, queryctl->servername) != SM_OK
592 /* read, forward, and delete messages */
593 for (num = 1; num <= count; num++)
597 || !(protocol->is_old)(socket, queryctl, num);
599 /* we may want to reject this message if it's old */
600 if (treat_as_new || queryctl->fetchall)
602 /* request a message */
603 (protocol->fetch)(socket, num, &len);
604 if (outlevel == O_VERBOSE)
605 if (protocol->delimited)
607 "fetching message %d (delimited)\n",
611 "fetching message %d (%d bytes)\n",
614 /* open the delivery pipe now if we're using an MDA */
615 if (queryctl->mda[0])
616 if ((mboxfd = openmailpipe(queryctl)) < 0)
619 /* read the message and ship it to the output sink */
620 ok = gen_readmsg(socket, mboxfd,
625 /* close the delivery pipe, we'll reopen before next message */
626 if (queryctl->mda[0])
627 if ((ok = closemailpipe(mboxfd)) != 0 || alarmed)
630 /* tell the server we got it OK and resynchronize */
632 (protocol->trail)(socket, queryctl, num);
633 if (alarmed || ok != 0)
638 * At this point in flow of control, either we've bombed
639 * on a protocol error or had delivery refused by the SMTP
640 * server (unlikely -- I've never seen it) or we've seen
641 * `accepted for delivery' and the message is shipped.
642 * It's safe to delete the message on the server now.
645 /* maybe we delete this message now? */
648 && (treat_as_new || queryctl->flush))
651 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
652 fprintf(stderr,"flushing message %d\n", num);
653 ok = (protocol->delete)(socket, queryctl, num);
654 if (alarmed || ok != 0)
659 /* remove all messages flagged for deletion */
660 if (protocol->expunge_cmd && deletions > 0)
662 ok = gen_transact(socket, protocol->expunge_cmd);
663 if (alarmed || ok != 0)
667 ok = gen_transact(socket, protocol->exit_cmd);
668 if (alarmed || ok == 0)
674 ok = gen_transact(socket, protocol->exit_cmd);
682 if (ok != 0 && ok != PS_SOCKET)
684 gen_transact(socket, protocol->exit_cmd);
688 #ifdef HAVE_RRESVPORT_H
690 close(privport); /* no big deal if this fails */
691 #endif /* HAVE_RRESVPORT_H */
700 signal(SIGALRM, sigsave);
704 /*********************************************************************
706 description: Assemble command in print style and send to the server
709 socket socket to which the server is connected.
710 fmt printf-style format
714 globals: reads outlevel.
715 *********************************************************************/
717 void gen_send(socket, fmt, va_alist)
722 char buf [POPBUFSIZE+1];
725 if (protocol->tagged)
726 (void) sprintf(buf, "%s ", GENSYM);
731 vsprintf(buf + strlen(buf), fmt, ap);
734 SockPuts(socket, buf);
736 if (outlevel == O_VERBOSE)
737 fprintf(stderr,"> %s\n", buf);
740 /*********************************************************************
741 function: gen_transact
742 description: Assemble command in print style and send to the server.
743 then accept a protocol-dependent response.
746 socket socket to which the server is connected.
747 fmt printf-style format
751 globals: reads outlevel.
752 *********************************************************************/
754 int gen_transact(socket, fmt, va_alist)
760 char buf [POPBUFSIZE+1];
763 if (protocol->tagged)
764 (void) sprintf(buf, "%s ", GENSYM);
769 vsprintf(buf + strlen(buf), fmt, ap);
772 SockPuts(socket, buf);
773 if (outlevel == O_VERBOSE)
774 fprintf(stderr,"> %s\n", buf);
776 /* we presume this does its own response echoing */
777 ok = (protocol->parse_response)(socket, buf);
783 /*********************************************************************
784 function: alarm_handler
785 description: In real life process can get stuck waiting for
786 something. This deadlock is avoided here by this
790 signal hopefully SIGALRM
794 globals: sets alarmed to 1
795 *********************************************************************/
797 alarm_handler (int signal)
800 fprintf(stderr,"fetchmail: timeout after %d seconds.\n", timeout);