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 ***********************************************************************/
24 #include <netinet/in.h> /* must be included before "socket.h".*/
26 #endif /* KERBEROS_V4 */
28 #include "fetchmail.h"
31 #define SMTP_PORT 25 /* standard SMTP service port */
33 static struct method *protocol;
35 static int alarmed; /* A flag to indicate that SIGALRM happened */
36 int timeout = CLIENT_TIMEOUT;
40 #define GENSYM (sprintf(tag, "a%04d", ++tagnum), tag)
42 /*********************************************************************
44 description: hack message headers so replies will work properly
47 after where to put the hacked header
49 host name of the pop header
53 *********************************************************************/
55 static void reply_hack(buf, host)
56 /* hack local mail IDs -- code by Eric S. Raymond 20 Jun 1996 */
62 char mycopy[POPBUFSIZE+1];
64 if (strncmp("From: ", buf, 6)
65 && strncmp("To: ", buf, 4)
66 && strncmp("Reply-", buf, 6)
67 && strncmp("Cc: ", buf, 4)
68 && strncmp("Bcc: ", buf, 5)) {
73 for (from = mycopy; *from; from++)
77 case 0: /* before header colon */
82 case 1: /* we've seen the colon, we're looking for addresses */
85 else if (*from == '(')
87 else if (*from == '<' || isalnum(*from))
91 case 2: /* we're in a quoted human name, copy and ignore */
96 case 3: /* we're in a parenthesized human name, copy and ignore */
101 case 4: /* the real work gets done here */
103 * We're in something that might be an address part,
104 * either a bare unquoted/unparenthesized text or text
105 * enclosed in <> as per RFC822.
107 /* if the address part contains an @, don't mess with it */
111 /* If the address token is not properly terminated, ignore it. */
112 else if (*from == ' ' || *from == '\t')
116 * On proper termination with no @, insert hostname.
117 * Case '>' catches <>-enclosed mail IDs. Case ',' catches
118 * comma-separated bare IDs. Cases \r and \n catch the case
119 * of a single ID alone on the line.
121 else if (strchr(">,\r\n", *from))
129 /* everything else, including alphanumerics, just passes through */
132 case 5: /* we're in a remote mail ID, no need to append hostname */
133 if (*from == '>' || *from == ',' || isspace(*from))
138 /* all characters from the old buffer get copied to the new one */
144 /*********************************************************************
146 description: Parse addresses in succession out of a specified RFC822
147 header. Note 1: RFC822 escaping with \ is *not* handled.
148 Note 2: it is important that this routine not stop on \r,
149 since we use \r as a marker for RFC822 continuations below.
151 hdr header line to be parsed, NUL to continue in previous hdr
153 return value: next address, or NUL if there is no next address
155 *********************************************************************/
157 static char *nxtaddr(hdr)
160 static char *hp, *tp, address[POPBUFSIZE+1];
173 case 0: /* before header colon */
183 case 1: /* we've seen the colon, now grab the address */
184 if ((*hp == '\n') || (*hp == ',')) /* end of address list */
189 else if (*hp == '"') /* quoted string */
194 else if (*hp == '(') /* address comment -- ignore */
196 else if (*hp == '<') /* begin <address> */
201 else if (isspace(*hp)) /* ignore space */
203 else /* just take it */
210 case 2: /* we're in a quoted string, copy verbatim */
222 case 3: /* we're in a parenthesized comment, ignore */
229 case 4: /* possible <>-enclosed address */
230 if (*hp == '>') /* end of address */
236 else if (*hp == '<') /* nested <> */
238 else if (*hp == '"') /* quoted address */
243 else /* just copy address */
247 case 5: /* we're in a quoted address, copy verbatim */
248 if (*hp == '\n') /* mismatched quotes */
250 if (*hp != '"') /* just copy it if it isn't a quote */
252 else if (*hp == '"') /* end of quoted string */
264 /*********************************************************************
265 function: gen_readmsg
266 description: Read the message content
268 as described in RFC 1225.
270 socket ... to which the server is connected.
271 mboxfd open file descriptor to which the retrieved message will
274 delimited does the protocol use a message delimiter?
275 queryctl host control block
277 return value: zero if success else PS_* return code.
279 globals: reads outlevel.
280 *********************************************************************/
282 static int gen_readmsg (socket, mboxfd, len, delimited, queryctl)
287 struct hostrec *queryctl;
289 char buf [MSGBUFSIZE+1];
290 char fromBuf[MSGBUFSIZE+1];
291 char *bufp, *headers, *unixfrom, *fromhdr, *tohdr, *cchdr, *bcchdr;
294 int lines,sizeticker;
296 /* This keeps the retrieved message count for display purposes */
297 static int msgnum = 0;
299 /* read the message content from the server */
301 headers = unixfrom = fromhdr = tohdr = cchdr = bcchdr = NULL;
304 while (delimited || len > 0) {
305 if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
309 if (buf[0] == '\0' || buf[0] == '\r' || buf[0] == '\n')
313 if (delimited && *bufp == 0)
314 break; /* end of message */
320 if (!queryctl->norewrite)
321 reply_hack(bufp, queryctl->servername);
325 oldlen = strlen(bufp);
326 headers = malloc(oldlen + 1);
329 (void) strcpy(headers, bufp);
337 * We deal with RFC822 continuation lines here.
338 * Replace previous '\n' with '\r' so nxtaddr
339 * and reply_hack will be able to see past it.
340 * (We know this safe because SocketGets stripped
341 * out all carriage returns in the read loop above
342 * and we haven't reintroduced any since then.)
343 * We'll undo this before writing the header.
345 if (isspace(bufp[0]))
346 headers[oldlen-1] = '\r';
348 newlen = oldlen + strlen(bufp);
349 headers = realloc(headers, newlen + 1);
352 strcpy(headers + oldlen, bufp);
353 bufp = headers + oldlen;
357 if (!strncmp(bufp,"From ",5))
359 else if (!strncasecmp("From: ", bufp, 6))
361 else if (!strncasecmp("To: ", bufp, 4))
363 else if (!strncasecmp("Cc: ", bufp, 4))
365 else if (!strncasecmp("Bcc: ", bufp, 5))
374 if (!queryctl->mda[0])
376 if (SMTP_from(mboxfd, nxtaddr(fromhdr)) != SM_OK)
380 * This is what we'd do if fetchmail were a real MDA
381 * a la sendmail -- crack all the destination headers
382 * and send to every address we can reach via SMTP.
384 if (tohdr && (cp = nxtaddr(tohdr)) != (char *)NULL)
386 if (SMTP_rcpt(mboxfd, cp) == SM_UNRECOVERABLE)
389 (cp = nxtaddr(NULL));
390 if (cchdr && (cp = nxtaddr(cchdr)) != (char *)NULL)
392 if (SMTP_rcpt(mboxfd, cp) == SM_UNRECOVERABLE)
395 (cp = nxtaddr(NULL));
396 if (bcchdr && (cp = nxtaddr(bcchdr)) != (char *)NULL)
398 if (SMTP_rcpt(mboxfd, cp) == SM_UNRECOVERABLE)
401 (cp = nxtaddr(NULL));
404 * Since we're really only fetching mail for one user
405 * per host query, we can be simpler
407 if (SMTP_rcpt(mboxfd, queryctl->localname) == SM_UNRECOVERABLE)
409 #endif /* SMTP_RESEND */
411 if (outlevel == O_VERBOSE)
412 fputs("SMTP> ", stderr);
415 /* change continuation markers back to regular newlines */
416 for (cp = headers; cp < headers + oldlen; cp++)
421 * Strictly speaking, we ought to replace all LFs
422 * with CR-LF before shipping to an SMTP listener.
423 * Since most SMTP listeners built since the mid-1980s
424 * (and *all* Unix listeners) accept lone LF as equivalent
425 * to CR-LF, we'll skip this particular contortion.
427 if (write(mboxfd,headers,oldlen) < 0)
431 perror("gen_readmsg: writing RFC822 headers");
434 else if (outlevel == O_VERBOSE)
440 /* SMTP byte-stuffing */
441 if (*bufp == '.' && queryctl->mda[0] == 0)
442 write(mboxfd, ".", 1);
444 /* write this line to the file -- comment about CR-LF vs. LF applies */
445 if (write(mboxfd,bufp,strlen(bufp)) < 0)
447 perror("gen_readmsg: writing message text");
450 else if (outlevel == O_VERBOSE)
455 /* write the message size dots */
456 sizeticker += strlen(bufp);
457 while (sizeticker >= SIZETICKER)
459 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
461 sizeticker -= SIZETICKER;
463 /* reset timeout so we don't choke on very long messages */
471 /* write message terminator */
472 if (!queryctl->mda[0])
473 if (SMTP_eom(mboxfd) != SM_OK)
479 /*********************************************************************
480 function: kerberos_auth
481 description: Authenticate to the server host using Kerberos V4
484 socket socket to server
485 servername server name
487 return value: exit code from the set of PS_.* constants defined in
490 globals: reads outlevel.
491 *********************************************************************/
494 kerberos_auth (socket, servername)
502 Key_schedule schedule;
505 /* Get the primary name of the host. */
507 struct hostent * hp = (gethostbyname (servername));
510 fprintf (stderr, "fetchmail: server %s unknown: n", servername);
513 host_primary = ((char *) (malloc ((strlen (hp -> h_name)) + 1)));
514 strcpy (host_primary, (hp -> h_name));
517 ticket = ((KTEXT) (malloc (sizeof (KTEXT_ST))));
519 = (krb_sendauth (0L, socket, ticket, "pop",
521 ((char *) (krb_realmofhost (host_primary))),
526 ((struct sockaddr_in *) 0),
527 ((struct sockaddr_in *) 0),
533 fprintf (stderr, "fetchmail: kerberos error %s\n", (krb_get_err_text (rem)));
538 #endif /* KERBEROS_V4 */
540 /*********************************************************************
541 function: do_protocol
542 description: retrieve messages from the specified mail server
543 using a given set of methods
546 queryctl fully-specified options (i.e. parsed, defaults invoked,
548 proto protocol method pointer
550 return value: exit code from the set of PS_.* constants defined in
553 globals: reads outlevel.
554 *********************************************************************/
556 int do_protocol(queryctl, proto)
557 struct hostrec *queryctl;
558 struct method *proto;
562 char buf [POPBUFSIZE+1], host[HOSTLEN+1];
565 int num, count, deletions = 0;
568 sigsave = signal(SIGALRM, alarm_handler);
572 if (queryctl->authenticate == A_KERBEROS)
574 fputs("fetchmail: Kerberos support not linked.\n", stderr);
577 #endif /* KERBEROS_V4 */
579 /* lacking methods, there are some options that may fail */
582 /* check for unsupported options */
583 if (queryctl->flush) {
585 "Option --flush is not supported with %s\n",
588 signal(SIGALRM, sigsave);
591 else if (queryctl->fetchall) {
593 "Option --all is not supported with %s\n",
596 signal(SIGALRM, sigsave);
604 /* open a socket to the mail server */
605 if ((socket = Socket(queryctl->servername,
606 queryctl->port ? queryctl->port : protocol->port))<0
609 perror("fetchmail, connecting to host");
615 if (queryctl->authentication == A_KERBEROS)
617 ok = (kerberos_auth (socket, queryctl->servername));
621 #endif /* KERBEROS_V4 */
623 /* accept greeting message from mail server */
624 ok = (protocol->parse_response)(socket, buf);
625 if (alarmed || ok != 0)
628 /* try to get authorized to fetch mail */
629 ok = (protocol->getauth)(socket, queryctl, buf);
630 if (alarmed || ok == PS_ERROR)
632 if (alarmed || ok != 0)
635 /* compute count, and get UID list if possible */
636 if ((protocol->getrange)(socket, queryctl, &count) != 0 || alarmed)
639 /* show user how many messages we downloaded */
640 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
642 fprintf(stderr, "No mail from %s\n", queryctl->servername);
645 "%d message%s from %s.\n",
646 count, count > 1 ? "s" : "",
647 queryctl->servername);
649 if ((count > 0) && (!check_only))
651 if (queryctl->mda[0] == '\0')
652 if ((mboxfd = Socket(queryctl->smtphost, SMTP_PORT)) < 0
653 || SMTP_ok(mboxfd, NULL) != SM_OK
654 || SMTP_helo(mboxfd, queryctl->servername) != SM_OK
663 /* read, forward, and delete messages */
664 for (num = 1; num <= count; num++)
668 || !(protocol->is_old)(socket, queryctl, num);
670 /* we may want to reject this message if it's old */
671 if (treat_as_new || queryctl->fetchall)
673 /* request a message */
674 (protocol->fetch)(socket, num, &len);
676 if (outlevel > O_SILENT)
678 fprintf(stderr, "reading message %d", num);
680 fprintf(stderr, " (%d bytes)", len);
681 if (outlevel == O_VERBOSE)
687 /* open the delivery pipe now if we're using an MDA */
688 if (queryctl->mda[0])
689 if ((mboxfd = openmailpipe(queryctl)) < 0)
692 /* read the message and ship it to the output sink */
693 ok = gen_readmsg(socket, mboxfd,
698 /* close the delivery pipe, we'll reopen before next message */
699 if (queryctl->mda[0])
700 if ((ok = closemailpipe(mboxfd)) != 0 || alarmed)
703 /* tell the server we got it OK and resynchronize */
705 (protocol->trail)(socket, queryctl, num);
706 if (alarmed || ok != 0)
711 * At this point in flow of control, either we've bombed
712 * on a protocol error or had delivery refused by the SMTP
713 * server (unlikely -- I've never seen it) or we've seen
714 * `accepted for delivery' and the message is shipped.
715 * It's safe to mark the message seen and delete it on the
719 /* maybe we delete this message now? */
722 && (treat_as_new || queryctl->flush))
725 if (outlevel > O_SILENT && outlevel < O_VERBOSE)
726 fprintf(stderr, " flushed\n", num);
727 ok = (protocol->delete)(socket, queryctl, num);
728 if (alarmed || ok != 0)
731 else if (outlevel > O_SILENT && outlevel < O_VERBOSE)
733 /* nuke it from the unseen-messages list */
734 delete_uid(&queryctl->newsaved, num);
735 fprintf(stderr, " not flushed\n", num);
739 /* remove all messages flagged for deletion */
740 if (protocol->expunge_cmd && deletions > 0)
742 ok = gen_transact(socket, protocol->expunge_cmd);
743 if (alarmed || ok != 0)
747 ok = gen_transact(socket, protocol->exit_cmd);
748 if (alarmed || ok == 0)
753 else if (check_only) {
754 ok = ((count > 0) ? PS_SUCCESS : PS_NOMAIL);
758 ok = gen_transact(socket, protocol->exit_cmd);
766 if (ok != 0 && ok != PS_SOCKET)
768 gen_transact(socket, protocol->exit_cmd);
779 signal(SIGALRM, sigsave);
783 /*********************************************************************
785 description: Assemble command in print style and send to the server
788 socket socket to which the server is connected.
789 fmt printf-style format
793 globals: reads outlevel.
794 *********************************************************************/
796 void gen_send(socket, fmt, va_alist)
801 char buf [POPBUFSIZE+1];
804 if (protocol->tagged)
805 (void) sprintf(buf, "%s ", GENSYM);
810 vsprintf(buf + strlen(buf), fmt, ap);
813 SockPuts(socket, buf);
815 if (outlevel == O_VERBOSE)
816 fprintf(stderr,"> %s\n", buf);
819 /*********************************************************************
820 function: gen_transact
821 description: Assemble command in print style and send to the server.
822 then accept a protocol-dependent response.
825 socket socket to which the server is connected.
826 fmt printf-style format
830 globals: reads outlevel.
831 *********************************************************************/
833 int gen_transact(socket, fmt, va_alist)
839 char buf [POPBUFSIZE+1];
842 if (protocol->tagged)
843 (void) sprintf(buf, "%s ", GENSYM);
848 vsprintf(buf + strlen(buf), fmt, ap);
851 SockPuts(socket, buf);
852 if (outlevel == O_VERBOSE)
853 fprintf(stderr,"> %s\n", buf);
855 /* we presume this does its own response echoing */
856 ok = (protocol->parse_response)(socket, buf);
862 /*********************************************************************
863 function: alarm_handler
864 description: In real life process can get stuck waiting for
865 something. This deadlock is avoided here by this
869 signal hopefully SIGALRM
873 globals: sets alarmed to 1
874 *********************************************************************/
876 alarm_handler (int signal)
879 fprintf(stderr,"fetchmail: timeout after %d seconds.\n", timeout);