#include <config.h>
#include <stdio.h>
+#include <setjmp.h>
+#include <ctype.h>
#if defined(STDC_HEADERS)
#include <stdlib.h>
#include <string.h>
#endif
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
#if defined(HAVE_STDARG_H)
#include <stdarg.h>
#else
#define SMTP_PORT 25 /* standard SMTP service port */
-static struct method *protocol;
-
-static int alarmed; /* a flag to indicate that SIGALRM happened */
-static int mytimeout; /* server-nonresponse timeout for current query */
-static char *srvname; /* current server name for timeout message */
+static const struct method *protocol;
+static jmp_buf restart;
char tag[TAGLEN];
static int tagnum;
#define GENSYM (sprintf(tag, "a%04d", ++tagnum), tag)
-static char *shroud;
+static char *shroud; /* string to shroud in debug output, if non-NULL */
+static int mytimeout; /* value of nonreponse timeout */
+
+static int strcrlf(dst, src, count)
+/* replace LFs with CR-LF; return length of string with replacements */
+char *dst; /* new string with CR-LFs */
+char *src; /* original string with LFs */
+int count; /* length of src */
+{
+ int len = count;
+
+ while (count--)
+ {
+ if (*src == '\n')
+ {
+ *dst++ = '\r';
+ len++;
+ }
+ *dst++ = *src++;
+ }
+ *dst = '\0';
+ return len;
+}
+
+static void vtalarm(timeleft)
+/* reset the nonresponse-timeout */
+int timeleft;
+{
+ struct itimerval ntimeout;
+
+ ntimeout.it_interval.tv_sec = ntimeout.it_interval.tv_sec = 0;
+ ntimeout.it_value.tv_sec = timeleft;
+ ntimeout.it_value.tv_usec = 0;
+ setitimer(ITIMER_VIRTUAL, &ntimeout, (struct itimerval *)NULL);
+}
+
+static void vtalarm_handler (int signal)
+/* handle server-timeout SIGVTALARM signal */
+{
+ longjmp(restart, 1);
+}
static void reply_hack(buf, host)
/* hack message headers so replies will work properly */
const char *host; /* server hostname */
{
const char *from;
- int state = 0, tokencount = 0;
+ int parendepth, state = 0, tokencount = 0;
char mycopy[POPBUFSIZE+1];
if (strncmp("From: ", buf, 6)
if (*from == '"')
state = 3;
else if (*from == '(')
+ {
+ parendepth = 1;
state = 4;
+ }
else if (*from == '<' || isalnum(*from))
state = 5;
else if (isspace(*from))
state = 2;
- else if (*from == ',')
- tokencount = 0;
break;
case 2: /* found a token boundary -- reset without copying */
break;
case 4: /* we're in a parenthesized human name, copy and ignore */
- if (*from == ')')
+ if (*from == '(')
+ ++parendepth;
+ else if (*from == ')')
+ --parendepth;
+ if (parendepth == 0)
state = 1;
break;
strcpy(buf, "@");
strcat(buf, host);
buf += strlen(buf);
+ tokencount = 0;
state = 1;
}
/* a single local name alone on the line */
- else if (*from == '\n' && tokencount == 0)
+ else if (*from == '\n' && tokencount == 1)
{
strcpy(buf, "@");
strcat(buf, host);
char *hdr; /* header line to be parsed, NUL to continue in previous hdr */
{
static char *hp, *tp, address[POPBUFSIZE+1];
- static state;
+ static int state;
+ int parendepth;
/*
* Note 1: RFC822 escaping with \ is *not* handled. Note 2: it is
*tp++ = *hp;
}
else if (*hp == '(') /* address comment -- ignore */
+ {
+ parendepth = 1;
state = 3;
+ }
else if (*hp == '<') /* begin <address> */
{
state = 4;
case 3: /* we're in a parenthesized comment, ignore */
if (*hp == '\n')
return(NULL);
+ else if (*hp == '(')
+ ++parendepth;
else if (*hp == ')')
+ --parendepth;
+ if (parendepth == 0)
state = 1;
break;
#ifdef HAVE_GETHOSTBYNAME
#define MX_RETRIES 3
-static int is_host_alias(name, queryctl)
+static int is_host_alias(name, ctl)
/* determine whether name is a DNS alias of the hostname */
const char *name;
-struct hostrec *queryctl;
+struct query *ctl;
{
struct hostent *he;
- int i, n;
+ int i;
/*
* The first two checks are optimizations that will catch a good
* name doesn't match either is it time to call the bind library.
* If this happens odds are good we're looking at an MX name.
*/
- if (strcmp(name, queryctl->servername) == 0)
+ if (strcmp(name, ctl->servername) == 0)
return(TRUE);
- else if (strcmp(name, queryctl->canonical_name) == 0)
+ else if (strcmp(name, ctl->canonical_name) == 0)
return(TRUE);
/*
* to respond quickly and reliably. Ergo if we get failure,
* the name isn't a mailserver alias.
*/
- else if ((he = gethostbyname(name)) && strcmp(queryctl->canonical_name, he->h_name) == 0)
+ else if ((he = gethostbyname(name)) && strcmp(ctl->canonical_name, he->h_name) == 0)
return(TRUE);
/*
*/
for (i = 0; i < MX_RETRIES; i++)
{
- struct mxentry mxresp[32];
- int j;
+ struct mxentry *mxrecords, *mxp;
- n = getmxrecords(name, sizeof(mxresp)/sizeof(struct mxentry), mxresp);
+ mxrecords = getmxrecords(name);
- if (n == -1)
+ if (mxrecords == (struct mxentry *)NULL)
if (h_errno == TRY_AGAIN)
{
sleep(1);
else
break;
- for (j = 0; j < n; j++)
- if (strcmp(name, mxresp[i].name) == 0)
+ for (mxp = mxrecords; mxp->name; mxp++)
+ if (strcmp(name, mxp->name) == 0)
return(TRUE);
}
return(FALSE);
}
-void find_server_names(hdr, queryctl, xmit_names)
+void find_server_names(hdr, ctl, xmit_names)
/* parse names out of a RFC822 header into an ID list */
const char *hdr; /* RFC822 header in question */
-struct hostrec *queryctl; /* list of permissible aliases */
+struct query *ctl; /* list of permissible aliases */
struct idlist **xmit_names; /* list of recipient names parsed out */
{
if (hdr == (char *)NULL)
if ((cp = nxtaddr(hdr)) != (char *)NULL)
do {
- char *atsign = strchr(cp, '@');
+ char *atsign;
- if (atsign)
- if (queryctl->norewrite)
+ if ((atsign = strchr(cp, '@')))
+ {
+ /*
+ * Address has an @. Check to see if the right-hand part
+ * is an alias or MX equivalent of the mailserver. If it's
+ * not, skip this name. If it is, we'll keep going and try
+ * to find a mapping to a client name.
+ */
+ if (!is_host_alias(atsign+1, ctl))
continue;
- else
- {
- if (!is_host_alias(atsign+1, queryctl))
- continue;
- atsign[0] = '\0';
- }
- lname = idpair_find(&queryctl->localnames, cp);
+ atsign[0] = '\0';
+ }
+
+ lname = idpair_find(&ctl->localnames, cp);
if (lname != (char *)NULL)
{
if (outlevel == O_VERBOSE)
}
#endif /* HAVE_GETHOSTBYNAME */
-static int gen_readmsg (socket, mboxfd, len, delimited, queryctl)
+static FILE *smtp_open(ctl)
+/* try to open a socket to the appropriate SMTP server for this query */
+struct query *ctl;
+{
+ ctl = ctl->leader; /* go to the SMTP leader for this query */
+
+ /* if no socket to this host is already set up, try to open one */
+ if (ctl->smtp_sockfp == (FILE *)NULL)
+ {
+ if ((ctl->smtp_sockfp = Socket(ctl->smtphost, SMTP_PORT)) == (FILE *)NULL)
+ return((FILE *)NULL);
+ else if (SMTP_ok(ctl->smtp_sockfp, NULL) != SM_OK
+ || SMTP_helo(ctl->smtp_sockfp, ctl->servername) != SM_OK)
+ {
+ fclose(ctl->smtp_sockfp);
+ ctl->smtp_sockfp = (FILE *)NULL;
+ }
+ }
+
+ return(ctl->smtp_sockfp);
+}
+
+static int gen_readmsg (sockfp, len, delimited, ctl)
/* read message content and ship to SMTP or MDA */
-int socket; /* to which the server is connected */
-int mboxfd; /* descriptor to which retrieved message will be written */
+FILE *sockfp; /* to which the server is connected */
long len; /* length of message */
int delimited; /* does the protocol use a message delimiter? */
-struct hostrec *queryctl; /* query control record */
-{
+struct query *ctl; /* query control record */
+{
char buf [MSGBUFSIZE+1];
- char fromBuf[MSGBUFSIZE+1];
- char *bufp, *headers, *unixfrom, *fromhdr, *tohdr, *cchdr, *bcchdr;
- int n, oldlen;
- int inheaders;
- int lines,sizeticker;
- /* This keeps the retrieved message count for display purposes */
- static int msgnum = 0;
+ char *bufp, *headers, *fromhdr, *tohdr, *cchdr, *bcchdr;
+ int n, oldlen, mboxfd;
+ int inheaders,lines,sizeticker;
+ FILE *sinkfp;
/* read the message content from the server */
inheaders = 1;
- headers = unixfrom = fromhdr = tohdr = cchdr = bcchdr = NULL;
+ headers = fromhdr = tohdr = cchdr = bcchdr = NULL;
lines = 0;
sizeticker = 0;
+ oldlen = 0;
while (delimited || len > 0)
{
- if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
+ if ((n = SockGets(buf,sizeof(buf),sockfp)) < 0)
return(PS_SOCKET);
+ vtalarm(ctl->timeout);
/* write the message size dots */
if (n > 0)
sizeticker += n;
while (sizeticker >= SIZETICKER)
{
- if (outlevel > O_SILENT && outlevel < O_VERBOSE)
+ if (outlevel > O_SILENT)
fputc('.',stderr);
sizeticker -= SIZETICKER;
}
if (inheaders)
{
- if (!queryctl->norewrite)
- reply_hack(bufp, queryctl->servername);
+ if (!ctl->norewrite)
+ reply_hack(bufp, ctl->servername);
if (!lines)
{
* We deal with RFC822 continuation lines here.
* Replace previous '\n' with '\r' so nxtaddr
* and reply_hack will be able to see past it.
- * (We know this safe because SocketGets stripped
+ * (We know this is safe because SocketGets stripped
* out all carriage returns in the read loop above
* and we haven't reintroduced any since then.)
* We'll undo this before writing the header.
oldlen = newlen;
}
- if (!strncmp(bufp,"From ",5))
- unixfrom = bufp;
- else if (!strncasecmp("From:", bufp, 5))
+ if (!strncasecmp("From:", bufp, 5))
fromhdr = bufp;
else if (!strncasecmp("To:", bufp, 3))
tohdr = bufp;
goto skipwrite;
}
- else if (headers)
+ else if (headers) /* OK, we're at end of headers now */
{
char *cp;
+ struct idlist *idp, *xmit_names;
- if (!queryctl->mda[0])
+ /* cons up a list of local recipients */
+ xmit_names = (struct idlist *)NULL;
+#ifdef HAVE_GETHOSTBYNAME
+ /* is this a multidrop box? */
+ if (MULTIDROP(ctl))
{
- if (SMTP_from(mboxfd, nxtaddr(fromhdr)) != SM_OK)
- return(PS_SMTP);
+ /* compute the local address list */
+ find_server_names(tohdr, ctl, &xmit_names);
+ find_server_names(cchdr, ctl, &xmit_names);
+ find_server_names(bcchdr, ctl, &xmit_names);
+ }
+ else /* it's a single-drop box, use first localname */
+#endif /* HAVE_GETHOSTBYNAME */
+ {
+ if (ctl->localnames)
+ save_uid(&xmit_names, -1, ctl->localnames->id);
+ }
-#ifdef HAVE_GETHOSTBYNAME
- /* is this a multidrop box? */
- if (queryctl->localnames != (struct idlist *)NULL
- && queryctl->localnames->next != (struct idlist *)NULL)
- {
- struct idlist *idp, *xmit_names;
+ /* if nothing supplied localnames, default appropriately */
+ if (!xmit_names)
+ if (getuid() == 0)
+ save_uid(&xmit_names, -1, ctl->remotename);
+ else
+ save_uid(&xmit_names, -1, user);
+
+ /* time to address the message */
+ if (ctl->mda[0]) /* we have a declared MDA */
+ {
+ int i, nlocals = 0;
+ char **sargv, **sp;
+
+ /*
+ * We go through this in order to be able to handle very
+ * long lists of users.
+ */
+ for (idp = xmit_names; idp; idp = idp->next)
+ nlocals++;
+ sp = sargv = (char **)alloca(sizeof(char **) * ctl->mda_argcount+nlocals+2);
+ for (i = 0; i < ctl->mda_argcount; i++)
+ *sp++ = ctl->mda_argv[i];
+ for (idp = xmit_names; idp; idp = idp->next)
+ *sp++ = idp->id;
+ *sp = (char *)NULL;
- /* compute the local address list */
- xmit_names = (struct idlist *)NULL;
- find_server_names(tohdr, queryctl, &xmit_names);
- find_server_names(cchdr, queryctl, &xmit_names);
- find_server_names(bcchdr, queryctl, &xmit_names);
+#ifdef HAVE_SETEUID
+ /*
+ * Arrange to run with user's permissions if we're root.
+ * This will initialize the ownership of any files the
+ * MDA creates properly. (The seteuid call is available
+ * under all BSDs and Linux)
+ */
+ seteuid(ctl->uid);
+#endif /* HAVE_SETEUID */
- /* if nothing supplied localnames, default appropriately */
- if (!xmit_names)
- save_uid(&xmit_names, -1, dfltuser);
+ mboxfd = openmailpipe(sargv);
- for (idp = xmit_names; idp; idp = idp->next)
- if (SMTP_rcpt(mboxfd, idp->id) != SM_OK)
- return(PS_SMTP);
+#ifdef HAVE_SETEUID
+ /* this will fail quietly if we didn't start as root */
+ seteuid(0);
+#endif /* HAVE_SETEUID */
+
+ if (mboxfd < 0)
+ {
+ fprintf(stderr, "fetchmail: MDA open failed\n");
+ return(PS_IOERR);
+ }
+ }
+ else
+ {
+ if (ctl->mda[0] == '\0' && ((sinkfp = smtp_open(ctl)) < 0))
+ {
free_uid_list(&xmit_names);
+ fprintf(stderr, "fetchmail: SMTP connect failed\n");
+ return(PS_SMTP);
}
- else /* it's a single-drop box, use first localname */
-#endif /* HAVE_GETHOSTBYNAME */
+
+ if (SMTP_from(sinkfp, nxtaddr(fromhdr)) != SM_OK)
{
- if (queryctl->localnames)
- cp = queryctl->localnames->id;
- else
- cp = dfltuser;
+ fprintf(stderr, "fetchmail: SMTP listener is confused\n");
+ return(PS_SMTP);
+ }
- if (SMTP_rcpt(mboxfd, cp) != SM_OK)
+ for (idp = xmit_names; idp; idp = idp->next)
+ if (SMTP_rcpt(sinkfp, idp->id) != SM_OK)
+ {
+ fprintf(stderr, "fetchmail: SMTP listener is upset\n");
return(PS_SMTP);
- }
+ }
- SMTP_data(mboxfd);
+ SMTP_data(sinkfp);
if (outlevel == O_VERBOSE)
fputs("SMTP> ", stderr);
}
+ free_uid_list(&xmit_names);
/* change continuation markers back to regular newlines */
- for (cp = headers; cp < headers + oldlen; cp++)
+ for (cp = headers; cp < headers + oldlen; cp++)
if (*cp == '\r')
*cp = '\n';
+ headers[oldlen++] = '\0';
/* replace all LFs with CR-LF before sending to the SMTP server */
- if (!queryctl->mda[0])
+ if (!ctl->mda[0])
{
char *newheaders = malloc(1 + oldlen * 2);
free(headers);
headers = newheaders;
}
- if (write(mboxfd,headers,oldlen) < 0)
+
+ /* write all the headers */
+ if (ctl->mda[0])
+ n = write(mboxfd,headers,oldlen);
+ else
+ n = SockWrite(headers, oldlen, sinkfp);
+
+ if (n < 0)
{
free(headers);
headers = NULL;
- perror("gen_readmsg: writing RFC822 headers");
+ perror("fetchmail: writing RFC822 headers");
return(PS_IOERR);
}
else if (outlevel == O_VERBOSE)
}
/* SMTP byte-stuffing */
- if (*bufp == '.' && queryctl->mda[0] == 0)
- write(mboxfd, ".", 1);
+ if (*bufp == '.' && ctl->mda[0] == 0)
+ SockWrite(".", 1, sinkfp);
- /* write this line to the file after replacing all LFs with CR-LF */
- if (!queryctl->mda[0])
+ /* replace all LFs with CR-LF in the line */
+ if (!ctl->mda[0])
{
char *newbufp = malloc(1 + strlen(bufp) * 2);
strcrlf(newbufp, bufp, strlen(bufp));
bufp = newbufp;
}
- n = write(mboxfd,bufp,strlen(bufp));
- if (!queryctl->mda[0])
+
+ /* ship out the text line */
+ if (ctl->mda[0])
+ n = write(mboxfd,bufp,strlen(bufp));
+ else
+ n = SockWrite(bufp, strlen(bufp), sinkfp);
+
+ if (!ctl->mda[0])
free(bufp);
if (n < 0)
{
- perror("gen_readmsg: writing message text");
+ perror("fetchmail: writing message text");
return(PS_IOERR);
}
else if (outlevel == O_VERBOSE)
lines++;
}
- if (alarmed)
- return (0);
- /* write message terminator */
- if (!queryctl->mda[0])
- if (SMTP_eom(mboxfd) != SM_OK)
+ if (ctl->mda[0])
+ {
+ /* close the delivery pipe, we'll reopen before next message */
+ if (closemailpipe(mboxfd))
+ return(PS_IOERR);
+ }
+ else
+ {
+ /* write message terminator */
+ if (SMTP_eom(sinkfp) != SM_OK)
+ {
+ fputs("fetchmail: SMTP listener refused delivery\n", stderr);
return(PS_SMTP);
+ }
+ }
+
return(0);
}
#ifdef KERBEROS_V4
int
-kerberos_auth (socket, servername)
+kerberos_auth (socket, canonical)
/* authenticate to the server host using Kerberos V4 */
int socket; /* socket to server host */
-char *servername; /* server name */
+char *canonical; /* server name */
{
char * host_primary;
KTEXT ticket;
Key_schedule schedule;
int rem;
- /* Get the primary name of the host. */
- {
- struct hostent * hp = (gethostbyname (servername));
- if (hp == 0)
- {
- fprintf (stderr, "fetchmail: server %s unknown: n", servername);
- return (PS_ERROR);
- }
- host_primary = ((char *) (malloc ((strlen (hp -> h_name)) + 1)));
- strcpy (host_primary, (hp -> h_name));
- }
-
ticket = ((KTEXT) (malloc (sizeof (KTEXT_ST))));
- rem
- = (krb_sendauth (0L, socket, ticket, "pop",
- host_primary,
- ((char *) (krb_realmofhost (host_primary))),
+ rem = (krb_sendauth (0L, socket, ticket, "pop",
+ canonical,
+ ((char *) (krb_realmofhost (canonical))),
((unsigned long) 0),
(&msg_data),
(&cred),
((struct sockaddr_in *) 0),
"KPOPV0.1"));
free (ticket);
- free (host_primary);
if (rem != KSUCCESS)
{
fprintf (stderr, "fetchmail: kerberos error %s\n", (krb_get_err_text (rem)));
}
#endif /* KERBEROS_V4 */
-int do_protocol(queryctl, proto)
+int do_protocol(ctl, proto)
/* retrieve messages from server using given protocol method table */
-struct hostrec *queryctl; /* parsed options with merged-in defaults */
-struct method *proto; /* protocol method table */
+struct query *ctl; /* parsed options with merged-in defaults */
+const struct method *proto; /* protocol method table */
{
- int ok, len;
- int mboxfd = -1;
- char buf [POPBUFSIZE+1], host[HOSTLEN+1];
- int socket;
+ int ok;
void (*sigsave)();
- int num, count, new, deletions = 0;
-
- srvname = queryctl->servername;
- alarmed = 0;
- sigsave = signal(SIGALRM, alarm_handler);
- alarm (mytimeout = queryctl->timeout);
#ifndef KERBEROS_V4
- if (queryctl->authenticate == A_KERBEROS)
+ if (ctl->authenticate == A_KERBEROS)
{
fputs("fetchmail: Kerberos support not linked.\n", stderr);
return(PS_ERROR);
if (!proto->is_old)
{
/* check for unsupported options */
- if (queryctl->flush) {
+ if (ctl->flush) {
fprintf(stderr,
"Option --flush is not supported with %s\n",
proto->name);
- alarm(0);
- signal(SIGALRM, sigsave);
return(PS_SYNTAX);
}
- else if (queryctl->fetchall) {
+ else if (ctl->fetchall) {
fprintf(stderr,
"Option --all is not supported with %s\n",
proto->name);
- alarm(0);
- signal(SIGALRM, sigsave);
return(PS_SYNTAX);
}
}
+ if (!proto->getsizes && ctl->limit)
+ {
+ fprintf(stderr,
+ "Option --limit is not supported with %s\n",
+ proto->name);
+ return(PS_SYNTAX);
+ }
+ protocol = proto;
tagnum = 0;
tag[0] = '\0'; /* nuke any tag hanging out from previous query */
- protocol = proto;
+ ok = 0;
- /* open a socket to the mail server */
- if ((socket = Socket(queryctl->servername,
- queryctl->port ? queryctl->port : protocol->port))<0
- || alarmed)
+ /* set up the server-nonresponse timeout */
+ sigsave = signal(SIGVTALRM, vtalarm_handler);
+ vtalarm(mytimeout = ctl->timeout);
+
+ if (setjmp(restart) == 1)
+ fprintf(stderr,
+ "fetchmail: timeout after %d seconds waiting for %s.\n",
+ ctl->timeout, ctl->servername);
+ else
{
- perror("fetchmail, connecting to host");
- ok = PS_SOCKET;
- goto closeUp;
- }
+ char buf [POPBUFSIZE+1];
+ int *msgsizes, len, num, count, new, deletions = 0;
+ FILE *sockfp;
+
+ /* open a socket to the mail server */
+ if ((sockfp = Socket(ctl->servername,
+ ctl->port ? ctl->port : protocol->port))<0)
+ {
+ perror("fetchmail, connecting to host");
+ ok = PS_SOCKET;
+ goto closeUp;
+ }
#ifdef KERBEROS_V4
- if (queryctl->authenticate == A_KERBEROS)
- {
- ok = (kerberos_auth (socket, queryctl->servername));
+ if (ctl->authenticate == A_KERBEROS)
+ {
+ ok = (kerberos_auth (fileno(sockfp), ctl->canonical_name));
+ vtalarm(ctl->timeout);
+ if (ok != 0)
+ goto cleanUp;
+ }
+#endif /* KERBEROS_V4 */
+
+ /* accept greeting message from mail server */
+ ok = (protocol->parse_response)(sockfp, buf);
+ vtalarm(ctl->timeout);
if (ok != 0)
goto cleanUp;
- }
-#endif /* KERBEROS_V4 */
- /* accept greeting message from mail server */
- ok = (protocol->parse_response)(socket, buf);
- if (alarmed || ok != 0)
- goto cleanUp;
-
- /* try to get authorized to fetch mail */
- shroud = queryctl->password;
- ok = (protocol->getauth)(socket, queryctl, buf);
- shroud = (char *)NULL;
- if (alarmed || ok == PS_ERROR)
- ok = PS_AUTHFAIL;
- if (alarmed || ok != 0)
- goto cleanUp;
-
- /* compute number of messages and number of new messages waiting */
- if ((protocol->getrange)(socket, queryctl, &count, &new) != 0 || alarmed)
- goto cleanUp;
-
- /* show user how many messages we downloaded */
- if (outlevel > O_SILENT && outlevel < O_VERBOSE)
- if (count == 0)
- fprintf(stderr, "No mail from %s@%s\n",
- queryctl->remotename,
- queryctl->servername);
- else
- {
- fprintf(stderr, "%d message%s", count, count > 1 ? "s" : "");
- if (new != -1 && (count - new) > 0)
- fprintf(stderr, " (%d seen)", count-new);
- fprintf(stderr,
- " from %s@%s.\n",
- queryctl->remotename,
- queryctl->servername);
- }
+ /* try to get authorized to fetch mail */
+ shroud = ctl->password;
+ ok = (protocol->getauth)(sockfp, ctl, buf);
+ vtalarm(ctl->timeout);
+ shroud = (char *)NULL;
+ if (ok == PS_ERROR)
+ ok = PS_AUTHFAIL;
+ if (ok != 0)
+ goto cleanUp;
- if (check_only)
- {
- if (new == -1 || queryctl->fetchall)
- new = count;
- ok = ((new > 0) ? PS_SUCCESS : PS_NOMAIL);
- goto closeUp;
- }
- else if (count > 0)
- {
- if (queryctl->mda[0] == '\0')
- if ((mboxfd = Socket(queryctl->smtphost, SMTP_PORT)) < 0
- || SMTP_ok(mboxfd, NULL) != SM_OK
- || SMTP_helo(mboxfd, queryctl->servername) != SM_OK
- || alarmed)
+ /* compute number of messages and number of new messages waiting */
+ if ((protocol->getrange)(sockfp, ctl, &count, &new) != 0)
+ goto cleanUp;
+ vtalarm(ctl->timeout);
+
+ /* show user how many messages we downloaded */
+ if (outlevel > O_SILENT)
+ if (count == 0)
+ fprintf(stderr, "No mail from %s@%s\n",
+ ctl->remotename,
+ ctl->servername);
+ else
{
- ok = PS_SMTP;
- close(mboxfd);
- mboxfd = -1;
- goto cleanUp;
+ fprintf(stderr, "%d message%s", count, count > 1 ? "s" : "");
+ if (new != -1 && (count - new) > 0)
+ fprintf(stderr, " (%d seen)", count-new);
+ fprintf(stderr,
+ " from %s@%s.\n",
+ ctl->remotename,
+ ctl->servername);
}
-
- /* read, forward, and delete messages */
- for (num = 1; num <= count; num++)
+
+ /* we may need to get sizes in order to check message limits */
+ msgsizes = (int *)NULL;
+ if (!ctl->fetchall && proto->getsizes && ctl->limit)
{
- int fetch_it = queryctl->fetchall ||
- !(protocol->is_old && (protocol->is_old)(socket,queryctl,num));
+ msgsizes = (int *)alloca(sizeof(int) * count);
- /* we may want to reject this message if it's old */
- if (!fetch_it)
- fprintf(stderr, "skipping message %d ", num);
- else
+ if ((ok = (proto->getsizes)(sockfp, count, msgsizes)) != 0)
+ return(PS_ERROR);
+ }
+
+ if (check_only)
+ {
+ if (new == -1 || ctl->fetchall)
+ new = count;
+ ok = ((new > 0) ? PS_SUCCESS : PS_NOMAIL);
+ goto closeUp;
+ }
+ else if (count > 0)
+ {
+ /* read, forward, and delete messages */
+ for (num = 1; num <= count; num++)
{
- /* request a message */
- (protocol->fetch)(socket, num, &len);
+ int toolarge = msgsizes && msgsizes[num-1]>ctl->limit;
+ int fetch_it = ctl->fetchall ||
+ (!(protocol->is_old && (protocol->is_old)(sockfp,ctl,num)) && !toolarge);
- if (outlevel > O_SILENT)
+ /* we may want to reject this message if it's old */
+ if (!fetch_it)
{
- fprintf(stderr, "reading message %d", num);
- if (len > 0)
- fprintf(stderr, " (%d bytes)", len);
- if (outlevel == O_VERBOSE)
- fputc('\n', stderr);
- else
- fputc(' ', stderr);
+ if (outlevel > O_SILENT)
+ {
+ fprintf(stderr, "skipping message %d", num);
+ if (toolarge)
+ fprintf(stderr, " (oversized, %d bytes)", msgsizes[num-1]);
+ }
}
-
- /* open the delivery pipe now if we're using an MDA */
- if (queryctl->mda[0])
+ else
{
-#ifdef HAVE_SETEUID
- /*
- * Arrange to run with user's permissions if we're root.
- * This will initialize the ownership of any files the
- * MDA creates properly. (The seteuid call is available
- * under all BSDs and Linux)
- */
- seteuid(queryctl->uid);
-#endif /* HAVE_SETEUID */
- mboxfd = openmailpipe(queryctl);
-#ifdef HAVE_SETEUID
- /* this will fail quietly if we didn't start as root */
- seteuid(0);
-#endif /* HAVE_SETEUID */
+ /* request a message */
+ (protocol->fetch)(sockfp, num, &len);
+ vtalarm(ctl->timeout);
+
+ if (outlevel > O_SILENT)
+ {
+ fprintf(stderr, "reading message %d", num);
+ if (len > 0)
+ fprintf(stderr, " (%d bytes)", len);
+ if (outlevel == O_VERBOSE)
+ fputc('\n', stderr);
+ else
+ fputc(' ', stderr);
+ }
- if (mboxfd < 0)
+ /* read the message and ship it to the output sink */
+ ok = gen_readmsg(sockfp,
+ len,
+ protocol->delimited,
+ ctl);
+ vtalarm(ctl->timeout);
+ if (ok != 0)
goto cleanUp;
+
+ /* tell the server we got it OK and resynchronize */
+ if (protocol->trail)
+ (protocol->trail)(sockfp, ctl, num);
}
- /* read the message and ship it to the output sink */
- ok = gen_readmsg(socket, mboxfd,
- len,
- protocol->delimited,
- queryctl);
+ /*
+ * At this point in flow of control, either we've bombed
+ * on a protocol error or had delivery refused by the SMTP
+ * server (unlikely -- I've never seen it) or we've seen
+ * `accepted for delivery' and the message is shipped.
+ * It's safe to mark the message seen and delete it on the
+ * server now.
+ */
- /* close the delivery pipe, we'll reopen before next message */
- if (queryctl->mda[0])
- if ((ok = closemailpipe(mboxfd)) != 0 || alarmed)
+ /* maybe we delete this message now? */
+ if (protocol->delete
+ && (fetch_it ? !ctl->keep : ctl->flush))
+ {
+ deletions++;
+ if (outlevel > O_SILENT)
+ fprintf(stderr, " flushed\n");
+ ok = (protocol->delete)(sockfp, ctl, num);
+ vtalarm(ctl->timeout);
+ if (ok != 0)
goto cleanUp;
-
- /* tell the server we got it OK and resynchronize */
- if (protocol->trail)
- (protocol->trail)(socket, queryctl, num);
- if (alarmed || ok != 0)
- goto cleanUp;
+ }
+ else if (outlevel > O_SILENT)
+ {
+ /* nuke it from the unseen-messages list */
+ delete_uid(&ctl->newsaved, num);
+ fprintf(stderr, " not flushed\n");
+ }
}
- /*
- * At this point in flow of control, either we've bombed
- * on a protocol error or had delivery refused by the SMTP
- * server (unlikely -- I've never seen it) or we've seen
- * `accepted for delivery' and the message is shipped.
- * It's safe to mark the message seen and delete it on the
- * server now.
- */
-
- /* maybe we delete this message now? */
- if (protocol->delete
- && (fetch_it ? !queryctl->keep : queryctl->flush))
+ /* remove all messages flagged for deletion */
+ if (protocol->expunge_cmd && deletions > 0)
{
- deletions++;
- if (outlevel > O_SILENT && outlevel < O_VERBOSE)
- fprintf(stderr, " flushed\n", num);
- ok = (protocol->delete)(socket, queryctl, num);
- if (alarmed || ok != 0)
+ ok = gen_transact(sockfp, protocol->expunge_cmd);
+ if (ok != 0)
goto cleanUp;
}
- else if (outlevel > O_SILENT && outlevel < O_VERBOSE)
- {
- /* nuke it from the unseen-messages list */
- delete_uid(&queryctl->newsaved, num);
- fprintf(stderr, " not flushed\n", num);
- }
+
+ ok = gen_transact(sockfp, protocol->exit_cmd);
+ if (ok == 0)
+ ok = PS_SUCCESS;
+ fclose(sockfp);
+ goto closeUp;
+ }
+ else {
+ ok = gen_transact(sockfp, protocol->exit_cmd);
+ if (ok == 0)
+ ok = PS_NOMAIL;
+ fclose(sockfp);
+ goto closeUp;
}
- /* remove all messages flagged for deletion */
- if (protocol->expunge_cmd && deletions > 0)
+ cleanUp:
+ if (ok != 0 && ok != PS_SOCKET)
{
- ok = gen_transact(socket, protocol->expunge_cmd);
- if (alarmed || ok != 0)
- goto cleanUp;
- }
-
- ok = gen_transact(socket, protocol->exit_cmd);
- if (alarmed || ok == 0)
- ok = PS_SUCCESS;
- close(socket);
- goto closeUp;
- }
- else {
- ok = gen_transact(socket, protocol->exit_cmd);
- if (ok == 0)
- ok = PS_NOMAIL;
- close(socket);
- goto closeUp;
+ gen_transact(sockfp, protocol->exit_cmd);
+ fclose(sockfp);
+ }
}
-cleanUp:
- if (ok != 0 && ok != PS_SOCKET)
- {
- gen_transact(socket, protocol->exit_cmd);
- close(socket);
- }
+ signal(SIGVTALRM, sigsave);
closeUp:
- if (mboxfd != -1)
- {
- if (!queryctl->mda[0])
- SMTP_quit(mboxfd);
- close(mboxfd);
- }
- alarm(0);
- signal(SIGALRM, sigsave);
return(ok);
}
#if defined(HAVE_STDARG_H)
-void gen_send(int socket, char *fmt, ... )
+void gen_send(FILE *sockfp, char *fmt, ... )
/* assemble command in printf(3) style and send to the server */
{
#else
-void gen_send(socket, fmt, va_alist)
+void gen_send(sockfp, fmt, va_alist)
/* assemble command in printf(3) style and send to the server */
-int socket; /* socket to which server is connected */
+FILE *sockfp; /* socket to which server is connected */
const char *fmt; /* printf-style format */
va_dcl {
#endif
vsprintf(buf + strlen(buf), fmt, ap);
va_end(ap);
- SockPuts(socket, buf);
+ strcat(buf, "\r\n");
+ SockWrite(buf, strlen(buf), sockfp);
if (outlevel == O_VERBOSE)
{
if (shroud && (cp = strstr(buf, shroud)))
memset(cp, '*', strlen(shroud));
- fprintf(stderr,"> %s\n", buf);
+ fprintf(stderr,"> %s", buf);
}
}
#if defined(HAVE_STDARG_H)
-int gen_transact(int socket, char *fmt, ... )
+int gen_transact(FILE *sockfp, char *fmt, ... )
/* assemble command in printf(3) style, send to server, accept a response */
{
#else
-int gen_transact(socket, fmt, va_alist)
+int gen_transact(sockfp, fmt, va_alist)
/* assemble command in printf(3) style, send to server, accept a response */
-int socket; /* socket to which server is connected */
+FILE *sockfp; /* socket to which server is connected */
const char *fmt; /* printf-style format */
va_dcl {
#endif
vsprintf(buf + strlen(buf), fmt, ap);
va_end(ap);
- SockPuts(socket, buf);
+ strcat(buf, "\r\n");
+ SockWrite(buf, strlen(buf), sockfp);
if (outlevel == O_VERBOSE)
{
char *cp;
if (shroud && (cp = strstr(buf, shroud)))
memset(cp, '*', strlen(shroud));
- fprintf(stderr,"> %s\n", buf);
+ fprintf(stderr,"> %s", buf);
}
/* we presume this does its own response echoing */
- ok = (protocol->parse_response)(socket, buf);
+ ok = (protocol->parse_response)(sockfp, buf);
+ vtalarm(mytimeout);
return(ok);
}
-int strcrlf(dst, src, count)
-/* replace LFs with CR-LF; return length of string with replacements */
-char *dst; /* new string with CR-LFs */
-char *src; /* original string with LFs */
-int count; /* length of src */
-{
- int len = count;
-
- while (count--)
- {
- if (*src == '\n')
- {
- *dst++ = '\r';
- len++;
- }
- *dst++ = *src++;
- }
- *dst = '\0';
- return len;
-}
-
-void
-alarm_handler (int signal)
-/* handle server-timeout signal */
-{
- alarmed = 1;
- fprintf(stderr,
- "fetchmail: timeout after %d seconds waiting for %s.\n",
- mytimeout, srvname);
-}
-
/* driver.c ends here */