diff options
-rw-r--r-- | odmr.c | 222 |
1 files changed, 222 insertions, 0 deletions
@@ -0,0 +1,222 @@ +/* + * odmr.c -- ODMR protocol methods (see RFC 2645) + * + * For license terms, see the file COPYING in this directory. + */ + +#include "config.h" +#ifdef ODMR_ENABLE +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#ifdef HAVE_NET_SOCKET_H /* BeOS needs this */ +#include <net/socket.h> +#endif +#include <sys/types.h> +#include <sys/time.h> +#include <netdb.h> +#include <errno.h> +#include <unistd.h> +#include "i18n.h" +#include "fetchmail.h" +#include "smtp.h" +#include "socket.h" + +static int odmr_ok (int sock, char *argbuf) +/* parse command response */ +{ + int ok; + + ok = SMTP_ok(sock); + if (ok == SM_UNRECOVERABLE) + return(PS_PROTOCOL); + else + return(ok); +} + +static int odmr_getrange(int sock, struct query *ctl, const char *id, + int *countp, int *newp, int *bytes) +/* send ODMR and then run a reverse SMTP session */ +{ + int ok, opts, smtp_sock; + char buf [MSGBUFSIZE+1]; + struct idlist *qnp; /* pointer to Q names */ + + if ((ok = SMTP_ehlo(sock, fetchmailhost, &opts))) + { + report(stderr, _("%s's SMTP listener does not support ESMTP\n"), + ctl->server.pollname); + return(ok); + } + else if (!(opts & ESMTP_ATRN)) + { + report(stderr, _("%s's SMTP listener does not support ATRN\n"), + ctl->server.pollname); + return(PS_PROTOCOL); + } + + /* make sure we don't enter the fetch loop */ + *bytes = *countp = *newp = -1; + + /* authenticate via CRAM-MD5 */ + ok = do_cram_md5(sock, ctl); + if (ok) + return(ok); + + /* + * By default, the hostlist has a single entry, the fetchmail host's + * canonical DNS name. + */ + buf[0] = '\0'; + for (qnp = ctl->smtphunt; qnp; qnp = qnp->next) + if (strlen(buf) + strlen(qnp->id) + 1 >= sizeof(buf)) + break; + else + { + strcat(buf, qnp->id); + strcat(buf, ","); + } + buf[strlen(buf) - 1] = '\0'; /* nuke final comma */ + + /* ship the domain list and get turnaround */ + gen_send(sock, "ATRN %s", buf); + if ((ok = gen_recv(sock, buf, sizeof(buf)))) + return(ok); + + /* this switch includes all response codes described in RFC2645 */ + switch(atoi(buf)) + { + case 250: /* OK, turnaround is about to happe */ + if (outlevel >= O_SILENT) + report(stdout, _("Turnaround now...\n")); + break; + + case 450: /* ATRN request refused */ + if (outlevel >= O_SILENT) + report(stdout, _("ATRN request refused.\n")); + return(PS_PROTOCOL); + + case 451: /* Unable to process ATRN request now */ + report(stderr, _("Unable to process ATRN request now\n")); + return(PS_EXCLUDE); + + case 453: /* You have no mail */ + report(stderr, _("You have no mail.\n")); + return(PS_NOMAIL); + + case 502: /* Command not implemented */ + report(stderr, _("Command not implemented\n")); + return(PS_PROTOCOL); + + case 530: /* Authentication required */ + report(stderr, _("Authentication required.\n")); + return(PS_AUTHFAIL); + + default: + report(stderr, _("Unknown ODMR error %d\n"), atoi(buf)); + return(PS_PROTOCOL); + } + + /* + * OK, if we got here it's time to become a pipe between the ODMR + * remote server (sending) and the local SMTP daemon (receiving). + * We're npt going to try to be a protocol machine; instead, we'll + * use select(2) to watch the read sides of both sockets and just + * throw their data at each other. + */ + smtp_sock = SockOpen(ctl->smtphost, SMTP_PORT, NULL, NULL); + if (smtp_sock) + return(PS_SOCKET); + else + { + int maxfd = (sock > smtp_sock) ? sock : smtp_sock; + + for (;;) + { + fd_set readfds; + struct timeval timeout; + char buf[MSGBUFSIZE]; + + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + FD_SET(smtp_sock, &readfds); + + timeout.tv_sec = ctl->server.timeout; + timeout.tv_usec = 0; + + if (select(maxfd+1, &readfds, NULL, NULL, &timeout) == -1) + return(PS_PROTOCOL); /* timeout */ + + if (FD_ISSET(sock, &readfds)) + { + int n = SockRead(sock, buf, sizeof(buf)); + SockWrite(smtp_sock, buf, n); + } + if (FD_ISSET(smtp_sock, &readfds)) + { + int n = SockRead(smtp_sock, buf, sizeof(buf)); + SockWrite(sock, buf, n); + } + } + } + + return(0); +} + +const static struct method odmr = +{ + "ODMR", /* ODMR protocol */ +#if INET6_ENABLE + "odmr", /* standard SMTP port */ + "odmrs", /* ssl SMTP port */ +#else /* INET6_ENABLE */ + 366, /* standard SMTP port */ + 2366, /* ssl SMTP port (BOGUS! RANDOM VALUE) */ +#endif /* INET6_ENABLE */ + FALSE, /* this is not a tagged protocol */ + FALSE, /* this does not use a message delimiter */ + odmr_ok, /* parse command response */ + NULL, /* no password canonicalization */ + NULL, /* no need to get authentication */ + odmr_getrange, /* initialize message sending */ + NULL, /* we cannot get a list of sizes */ + NULL, /* how do we tell a message is old? */ + NULL, /* no way to fetch headers */ + NULL, /* no way to fetch body */ + NULL, /* no message trailer */ + NULL, /* how to delete a message */ + NULL, /* log out, we're done */ + FALSE, /* no, we can't re-poll */ +}; + +int doODMR (struct query *ctl) +/* retrieve messages using ODMR */ +{ + int status; + + if (ctl->keep) { + fprintf(stderr, _("Option --keep is not supported with ODMR\n")); + return(PS_SYNTAX); + } + if (ctl->flush) { + fprintf(stderr, _("Option --flush is not supported with ODMR\n")); + return(PS_SYNTAX); + } + if (ctl->mailboxes->id) { + fprintf(stderr, _("Option --remote is not supported with ODMR\n")); + return(PS_SYNTAX); + } + if (check_only) { + fprintf(stderr, _("Option --check is not supported with ODMR\n")); + return(PS_SYNTAX); + } + peek_capable = FALSE; + + status = do_protocol(ctl, &odmr); + if (status == PS_NOMAIL) + status = PS_SUCCESS; + return(status); +} +#endif /* ODMR_ENABLE */ + +/* odmr.c ends here */ |