/***********************************************************************
module: driver.c
- project: popclient
+ project: fetchmail
programmer: Eric S. Raymond
description: Generic driver for mail fetch method protocols
***********************************************************************/
#include <config.h>
-#include <varargs.h>
-
#include <stdio.h>
-#if defined(STDC_HEADERS)
-#include <string.h>
-#endif
-#if defined(HAVE_UNISTD_H)
-#include <unistd.h>
-#endif
-
-#include <sys/time.h>
-#include <ctype.h>
-#include <errno.h>
#include <malloc.h>
+#include <varargs.h>
+#include <sys/time.h>
#include "socket.h"
-#include "popclient.h"
+#include "fetchmail.h"
#include "smtp.h"
static struct method *protocol;
proto protocol method pointer
return value: exit code from the set of PS_.* constants defined in
- popclient.h
+ fetchmail.h
calls:
globals: reads outlevel.
*********************************************************************/
{
int ok, len;
int mboxfd;
- char buf [POPBUFSIZE], host[HOSTLEN];
+ char buf [POPBUFSIZE+1], host[HOSTLEN+1];
int socket;
int first,number,count;
goto closeUp;
}
- /* print the greeting */
- if (outlevel > O_SILENT && outlevel < O_VERBOSE)
- fprintf(stderr,"%s greeting: %s\n", protocol->name, buf);
-
/* try to get authorized to fetch mail */
ok = (protocol->getauth)(socket, queryctl, buf);
if (ok == PS_ERROR)
/* show them how many messages we'll be downloading */
if (outlevel > O_SILENT && outlevel < O_VERBOSE)
- if (first > 1)
- fprintf(stderr,"%d messages in folder, %d new messages.\n",
- count, count - first + 1);
+ if (count == 0)
+ fprintf(stderr, "No mail from %s\n", queryctl->servername);
+ else if (first > 1)
+ fprintf(stderr,
+ "%d message%s from %s, %d new messages.\n",
+ count, count > 1 ? "s" : "",
+ queryctl->servername, count - first + 1);
else
- fprintf(stderr,"%d %smessages in folder.\n", count, ok ? "" : "new ");
+ fprintf(stderr,
+ "%d %smessage%s from %s.\n",
+ count, ok ? "" : "new ",
+ count > 1 ? "s" : "",
+ queryctl->servername);
if (count > 0) {
for (number = queryctl->flush ? 1 : first; number<=count; number++) {
}
/* maybe we delete this message now? */
- if ((number < first && queryctl->flush) || !queryctl->keep) {
- if (outlevel > O_SILENT && outlevel < O_VERBOSE)
- fprintf(stderr,"flushing message %d\n", number);
- else
- ;
- ok = gen_transact(socket, protocol->delete_cmd, number);
- if (ok != 0)
- goto cleanUp;
+ if (protocol->delete_cmd)
+ {
+ if ((number < first && queryctl->flush) || !queryctl->keep) {
+ if (outlevel > O_SILENT && outlevel < O_VERBOSE)
+ fprintf(stderr,"flushing message %d\n", number);
+ else
+ ;
+ ok = gen_transact(socket, protocol->delete_cmd, number);
+ if (ok != 0)
+ goto cleanUp;
+ }
}
/* close the mail pipe, we'll reopen before next message */
if (closeuserfolder(mboxfd) < 0 && ok == 0)
ok = PS_IOERR;
}
- else if (queryctl->output == TO_SMTP && mboxfd > 0)
+ else if (queryctl->output == TO_SMTP && mboxfd > 0) {
+ SMTP_quit(mboxfd);
close(mboxfd);
+ }
if (ok == PS_IOERR || ok == PS_SOCKET)
perror("do_protocol: cleanUp");
const char *fmt;
va_dcl {
- char buf [POPBUFSIZE];
+ char buf [POPBUFSIZE+1];
va_list ap;
if (protocol->tagged)
va_dcl {
int ok;
- char buf [POPBUFSIZE];
+ char buf [POPBUFSIZE+1];
va_list ap;
if (protocol->tagged)
fprintf(stderr,"> %s\n", buf);
ok = (protocol->parse_response)(buf,socket);
- if (ok != 0 && outlevel > O_SILENT && outlevel < O_VERBOSE)
+ if (ok != 0 && outlevel > O_SILENT && outlevel <= O_VERBOSE)
fprintf(stderr,"%s\n",buf);
return(ok);
{
const char *from;
int state = 0;
- char mycopy[POPBUFSIZE];
+ char mycopy[POPBUFSIZE+1];
if (strncmp("From: ", buf, 6)
&& strncmp("To: ", buf, 4)
/*********************************************************************
function: nxtaddr
description: Parse addresses in succession out of a specified RFC822
- header. Note: RFC822 escaping with \ is *not* handled.
-
+ header. Note 1: RFC822 escaping with \ is *not* handled.
+ Note 2: it is important that this routine not stop on \r,
+ since we use \r as a marker for RFC822 continuations below.
arguments:
hdr header line to be parsed, NUL to continue in previous hdr
static char *nxtaddr(hdr)
char *hdr;
{
- static char *hp, *tp, address[POPBUFSIZE];
+ static char *hp, *tp, address[POPBUFSIZE+1];
static state;
if (hdr)
if (*hp == '\n')
return(NULL);
else if (*hp == ':')
+ {
state = 1;
+ tp = address;
+ }
break;
- case 1: /* we've seen the colon, we're looking for addresses */
- if (*hp == '\n')
- return(NULL);
- else if (*hp == '"')
- state = 2;
- else if (*hp == '(')
+ case 1: /* we've seen the colon, now grab the address */
+ if ((*hp == '\n') || (*hp == ',')) /* end of address list */
+ {
+ *tp++ = '\0';
+ return(address);
+ }
+ else if (*hp == '"') /* quoted string */
+ {
+ state = 2;
+ *tp++ = *hp;
+ }
+ else if (*hp == '(') /* address comment -- ignore */
state = 3;
- else if (*hp == '<')
+ else if (*hp == '<') /* begin <address> */
{
state = 4;
tp = address;
}
- else if (isalnum(*hp))
+ else if (isspace(*hp)) /* ignore space */
+ state = 1;
+ else /* just take it */
{
- state = 5;
- tp = address;
+ state = 1;
*tp++ = *hp;
}
break;
- case 2: /* we're in a quoted human name, copy and ignore */
+ case 2: /* we're in a quoted string, copy verbatim */
if (*hp == '\n')
return(NULL);
+ if (*hp != '"')
+ *tp++ = *hp;
else if (*hp == '"')
+ {
+ *tp++ = *hp;
state = 1;
+ }
break;
- case 3: /* we're in a parenthesized human name, copy and ignore */
+ case 3: /* we're in a parenthesized comment, ignore */
if (*hp == '\n')
return(NULL);
else if (*hp == ')')
break;
case 4: /* possible <>-enclosed address */
- if (*hp == '>')
+ if (*hp == '>') /* end of address */
{
*tp++ = '\0';
state = 1;
return(address);
}
- else
+ else if (*hp == '<') /* nested <> */
+ tp = address;
+ else if (*hp == '"') /* quoted address */
+ {
+ *tp++ = *hp;
+ state = 5;
+ }
+ else /* just copy address */
*tp++ = *hp;
break;
- case 5: /* address not <>-enclosed, terminate on any whitespace */
- if (isspace(*hp))
+ case 5: /* we're in a quoted address, copy verbatim */
+ if (*hp == '\n') /* mismatched quotes */
+ return(NULL);
+ if (*hp != '"') /* just copy it if it isn't a quote */
+ *tp++ = *hp;
+ else if (*hp == '"') /* end of quoted string */
{
- *tp++ = '\0';
- state = 1;
- return(address); /* prevents normal hp++ */
+ *tp++ = *hp;
+ state = 4;
}
- else
- *tp++ = *hp;
break;
}
}
int output;
int rewrite;
{
- char buf [MSGBUFSIZE];
- char fromBuf[MSGBUFSIZE];
+ char buf [MSGBUFSIZE+1];
+ char fromBuf[MSGBUFSIZE+1];
char *bufp, *headers, *unixfrom, *fromhdr, *tohdr, *cchdr, *bcchdr;
int n, oldlen;
int inheaders;
fprintf(stderr,"reading message %d",++msgnum);
/* won't do the '...' if retrieved messages are being sent to stdout */
if (mboxfd == 1)
- fputs(".\n",stderr);
+ fputs("\n",stderr);
}
/* read the message content from the server */
inheaders = 1;
headers = unixfrom = fromhdr = tohdr = cchdr = bcchdr = NULL;
lines = 0;
- sizeticker = MSGBUFSIZE;
+ sizeticker = 0;
while (delimited || len > 0) {
if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
return(PS_SOCKET);
if (delimited && *bufp == 0)
break; /* end of message */
}
- strcat(bufp,"\n");
+ strcat(bufp, output == TO_SMTP && !inheaders ? "\r\n" : "\n");
if (inheaders)
{
}
else
{
- int newlen = oldlen + strlen(bufp);
+ int newlen;
+ /*
+ * 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'll undo this before writing the header.
+ */
+ if (isspace(bufp[0]))
+ headers[oldlen-1] = '\r';
+
+ newlen = oldlen + strlen(bufp);
headers = realloc(headers, newlen + 1);
if (headers == NULL)
return(PS_IOERR);
return(PS_SMTP);
#ifdef SMTP_RESEND
/*
- * This is what we'd do if popclient were a real MDA
+ * This is what we'd do if fetchmail were a real MDA
* a la sendmail -- crack all the destination headers
* and send to every address we can reach via SMTP.
*/
return(PS_SMTP);
#endif /* SMTP_RESEND */
SMTP_data(mboxfd);
+ if (outlevel == O_VERBOSE)
+ fputs("SMTP> ", stderr);
break;
case TO_FOLDER:
break;
}
+ /* change continuation markers back to regular newlines */
+ for (cp = headers; cp < headers + oldlen; cp++)
+ if (*cp == '\r')
+ *cp = '\n';
+
if (write(mboxfd,headers,oldlen) < 0)
{
free(headers);
perror("gen_readmsg: writing RFC822 headers");
return(PS_IOERR);
}
+ else if (outlevel == O_VERBOSE)
+ fputs("#", stderr);
free(headers);
headers = NULL;
}
perror("gen_readmsg: writing message text");
return(PS_IOERR);
}
+ else if (outlevel == O_VERBOSE)
+ fputc('*', stderr);
skipwrite:;
- sizeticker -= strlen(bufp);
- if (sizeticker <= 0)
+ sizeticker += strlen(bufp);
+ while (sizeticker >= MSGBUFSIZE)
{
if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
fputc('.',stderr);
- sizeticker = MSGBUFSIZE;
+ sizeticker -= MSGBUFSIZE;
}
lines++;
}
+ if (outlevel == O_VERBOSE)
+ fputc('\n', stderr);
+
/* write message terminator, if any */
switch (output)
{
return(PS_IOERR);
}
#endif /* BINMAIL_TERM */
+ break;
}
/* finish up display output */
if (outlevel == O_VERBOSE)
fprintf(stderr,"(%d lines of message content)\n",lines);
else if (outlevel > O_SILENT && mboxfd != 1)
- fputs(".\n",stderr);
+ fputs("\n",stderr);
else
;
return(0);