* Fix a bonehead coding error in pop3_delete() that was masked by the
Intel register architecture. *blush* Thanks to Jay Anderson.
+* Restore --mda, seems some people either can't run a port 25 listener
+ due to bizarre dynamic-SLIP problems, or won't for security reasons.
+
fetchmail-1.5 (Thu Oct 3 04:35:15 EDT 1996):
* Naturally, my decision to announce 1.4 on comp.os.linux.announce
mboxfd open file descriptor to which the retrieved message will
be written.
len length of text
- popuser name of the POP user
- pophost name of the POP host
- output output mode
+ delimited does the protocol use a message delimiter?
+ queryctl host control block
return value: zero if success else PS_* return code.
calls: SockGets.
globals: reads outlevel.
*********************************************************************/
-static int gen_readmsg (socket,mboxfd,len,delimited,popuser,pophost,rewrite)
+static int gen_readmsg (socket, mboxfd, len, delimited, queryctl)
int socket;
int mboxfd;
long len;
int delimited;
-char *popuser;
-char *pophost;
-int rewrite;
+struct hostrec *queryctl;
{
char buf [MSGBUFSIZE+1];
char fromBuf[MSGBUFSIZE+1];
static int msgnum = 0;
/* set up for status message if outlevel allows it */
- if (outlevel > O_SILENT && outlevel < O_VERBOSE) {
+ if (outlevel > O_SILENT && outlevel < O_VERBOSE)
fprintf(stderr,"reading message %d",++msgnum);
- /* won't do the '...' if retrieved messages are being sent to stdout */
- if (mboxfd == 1)
- fputs("\n",stderr);
- }
/* read the message content from the server */
inheaders = 1;
if (inheaders)
{
- if (rewrite)
- reply_hack(bufp, pophost);
+ if (!queryctl->norewrite)
+ reply_hack(bufp, queryctl->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.
+ * and reply_hack will be able to see past it.
* We'll undo this before writing the header.
*/
if (isspace(bufp[0]))
{
char *cp;
- if (SMTP_from(mboxfd, nxtaddr(fromhdr)) != SM_OK)
- return(PS_SMTP);
+ if (!queryctl->mda[0])
+ {
+ if (SMTP_from(mboxfd, nxtaddr(fromhdr)) != SM_OK)
+ return(PS_SMTP);
#ifdef SMTP_RESEND
- /*
- * 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.
- */
- if (tohdr && (cp = nxtaddr(tohdr)) != (char *)NULL)
- do {
- if (SMTP_rcpt(mboxfd, cp) == SM_UNRECOVERABLE)
- return(PS_SMTP);
- } while
- (cp = nxtaddr(NULL));
- if (cchdr && (cp = nxtaddr(cchdr)) != (char *)NULL)
- do {
- if (SMTP_rcpt(mboxfd, cp) == SM_UNRECOVERABLE)
- return(PS_SMTP);
- } while
- (cp = nxtaddr(NULL));
- if (bcchdr && (cp = nxtaddr(bcchdr)) != (char *)NULL)
- do {
- if (SMTP_rcpt(mboxfd, cp) == SM_UNRECOVERABLE)
- return(PS_SMTP);
- } while
- (cp = nxtaddr(NULL));
+ /*
+ * 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.
+ */
+ if (tohdr && (cp = nxtaddr(tohdr)) != (char *)NULL)
+ do {
+ if (SMTP_rcpt(mboxfd, cp) == SM_UNRECOVERABLE)
+ return(PS_SMTP);
+ } while
+ (cp = nxtaddr(NULL));
+ if (cchdr && (cp = nxtaddr(cchdr)) != (char *)NULL)
+ do {
+ if (SMTP_rcpt(mboxfd, cp) == SM_UNRECOVERABLE)
+ return(PS_SMTP);
+ } while
+ (cp = nxtaddr(NULL));
+ if (bcchdr && (cp = nxtaddr(bcchdr)) != (char *)NULL)
+ do {
+ if (SMTP_rcpt(mboxfd, cp) == SM_UNRECOVERABLE)
+ return(PS_SMTP);
+ } while
+ (cp = nxtaddr(NULL));
#else
- /*
- * Since we're really only fetching mail for one user
- * per host query, we can be simpler
- */
- if (SMTP_rcpt(mboxfd, popuser) == SM_UNRECOVERABLE)
- return(PS_SMTP);
+ /*
+ * Since we're really only fetching mail for one user
+ * per host query, we can be simpler
+ */
+ if (SMTP_rcpt(mboxfd, queryctl->localname) == SM_UNRECOVERABLE)
+ return(PS_SMTP);
#endif /* SMTP_RESEND */
- SMTP_data(mboxfd);
- if (outlevel == O_VERBOSE)
- fputs("SMTP> ", stderr);
+ SMTP_data(mboxfd);
+ if (outlevel == O_VERBOSE)
+ fputs("SMTP> ", stderr);
+ }
/* change continuation markers back to regular newlines */
for (cp = headers; cp < headers + oldlen; cp++)
skipwrite:;
+ /* write the message size dots */
sizeticker += strlen(bufp);
while (sizeticker >= SIZETICKER)
{
- if (outlevel > O_SILENT && outlevel < O_VERBOSE && mboxfd != 1)
+ if (outlevel > O_SILENT && outlevel < O_VERBOSE)
fputc('.',stderr);
sizeticker -= SIZETICKER;
}
lines++;
}
+
+ /* finish up display output */
if (outlevel == O_VERBOSE)
- fputc('\n', stderr);
+ fprintf(stderr,"\n(%d lines of message content)\n",lines);
+ else if (outlevel > O_SILENT)
+ fputs("\n", stderr);
/* write message terminator */
if (SMTP_eom(mboxfd) != SM_OK)
return(PS_SMTP);
-
- /* 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);
return(0);
}
if (count > 0)
{
- ok = PS_SMTP;
- if ((mboxfd = Socket(queryctl->smtphost, SMTP_PORT)) < 0)
- goto cleanUp;
-
- /* eat the greeting message */
- if (SMTP_ok(mboxfd, NULL) != SM_OK) {
- close(mboxfd);
- mboxfd = -1;
- goto cleanUp;
- }
+ 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)
+ {
+ ok = PS_SMTP;
+ close(mboxfd);
+ mboxfd = -1;
+ goto cleanUp;
+ }
- /* make it look like mail is coming from the server */
- if (SMTP_helo(mboxfd,queryctl->servername) != SM_OK) {
- close(mboxfd);
- mboxfd = -1;
- goto cleanUp;
- }
-
/* read, forward, and delete messages */
for (num = 1; num <= count; num++)
{
"fetching message %d (%d bytes)\n",
num, len);
+ /* open the delivery pipe now if we're using an MDA */
+ if (queryctl->mda[0])
+ if ((mboxfd = openmailpipe(queryctl)) < 0)
+ goto cleanUp;
+
/* read the message and ship it to the output sink */
- ok = gen_readmsg(socket,
- mboxfd,
- len,
+ ok = gen_readmsg(socket, mboxfd,
+ len,
protocol->delimited,
- queryctl->localname,
- queryctl->servername,
- !queryctl->norewrite);
+ queryctl);
+
+ /* close the delivery pipe, we'll reopen before next message */
+ if (queryctl->mda[0])
+ if ((ok = closemailpipe(mboxfd)) != 0)
+ goto cleanUp;
/* tell the server we got it OK and resynchronize */
if (protocol->trail)
#ifdef HAVE_PROTOTYPES
/* prototypes for internal functions */
-int showoptions (struct hostrec *queryctl);
-int showversioninfo (void);
-int dump_options (struct hostrec *queryctl);
-int query_host(struct hostrec *queryctl);
+static int showversioninfo (void);
+static int dump_options (struct hostrec *queryctl);
+static int query_host(struct hostrec *queryctl);
#endif
/* controls the detail level of status/progress messages written to stderr */
prc_mergeoptions(servername, &cmd_opts, &def_opts, hostp);
strcpy(hostp->servername, servername);
+
hostp->next = hostlist;
hostlist = hostp;
+
+ if (hostp->mda[0])
+ {
+ int argi;
+ char *argp;
+
+ /* expand the %s escape if any before parsing */
+ sprintf(hostp->mdabuf, hostp->mda, hostp->localname);
+
+ /* now punch nulls into the delimiting whitespace in the args */
+ for (argp = hostp->mdabuf, argi = 1; *argp != '\0'; argi++)
+ {
+ hostp->mda_argv[argi] = argp;
+ while (!(*argp == '\0' || isspace(*argp)))
+ argp++;
+ if (*argp != '\0')
+ *(argp++) = '\0';
+ }
+
+ hostp->mda_argv[argi] = (char *)NULL;
+
+ hostp->mda_argv[0] = hostp->mda_argv[1];
+ if ((argp = strrchr(hostp->mda_argv[1], '/')) != (char *)NULL)
+ hostp->mda_argv[1] = argp + 1 ;
+ }
}
/* set up to do lock protocol */
globals: none.
*********************************************************************/
-char *showproto(proto)
+static char *showproto(proto)
int proto;
{
switch (proto)
*/
static const int autoprobe[] = {P_IMAP, P_POP3, P_POP2};
-int query_host(queryctl)
+static int query_host(queryctl)
/* perform fetch transaction with single host */
struct hostrec *queryctl;
{
/*********************************************************************
function: showversioninfo
- description: display program release and compiler info
+ description: display program release
arguments: none.
return value: none.
calls: none.
globals: none.
*********************************************************************/
-int showversioninfo()
+static int showversioninfo()
{
printf("This is fetchmail release %s\n",RELEASE_ID);
}
int dump_params (queryctl)
struct hostrec *queryctl;
{
- char *cp;
-
if (queryctl->skip || outlevel == O_VERBOSE)
printf(" This host will%s be queried when no host is specified.\n",
queryctl->skip ? " not" : "");
printf(" Rewrite of server-local addresses is %sabled (--norewrite %s)\n",
queryctl->norewrite ? "dis" : "en",
queryctl->norewrite ? "on" : "off");
- printf(" Messages will be SMTP-forwarded to '%s'\n", queryctl->smtphost);
+ if (queryctl->mda[0])
+ {
+ char **cp;
+
+ printf(" Messages will be delivered with %s, args:",
+ queryctl->mda_argv[0]);
+ for (cp = queryctl->mda_argv+1; *cp; cp++)
+ printf(" %s", *cp);
+ putchar('\n');
+ }
+ else
+ printf(" Messages will be SMTP-forwarded to '%s'\n", queryctl->smtphost);
+}
+
+/*********************************************************************
+ function: openmailpipe
+ description: open a one-way pipe to the mail delivery agent.
+ arguments:
+ queryctl fully-determined options (i.e. parsed, defaults invoked,
+ etc).
+
+ return value: open file descriptor for the pipe or -1.
+ calls: none.
+ globals: reads mda_argv.
+ *********************************************************************/
+
+int openmailpipe (queryctl)
+struct hostrec *queryctl;
+{
+ int pipefd [2];
+ int childpid;
+
+ if (pipe(pipefd) < 0) {
+ perror("fetchmail: openmailpipe: pipe");
+ return(-1);
+ }
+ if ((childpid = fork()) < 0) {
+ perror("fetchmail: openmailpipe: fork");
+ return(-1);
+ }
+ else if (childpid == 0) {
+
+ /* in child process space */
+ close(pipefd[1]); /* close the 'write' end of the pipe */
+ close(0); /* get rid of inherited stdin */
+ if (dup(pipefd[0]) != 0) {
+ fputs("fetchmail: openmailpipe: dup() failed\n",stderr);
+ exit(1);
+ }
+
+ execv(queryctl->mda_argv[0], queryctl->mda_argv + 1);
+
+ /* if we got here, an error occurred */
+ perror("fetchmail: openmailpipe: exec");
+ _exit(PS_SYNTAX);
+
+ }
+
+ /* in the parent process space */
+ close(pipefd[0]); /* close the 'read' end of the pipe */
+ return(pipefd[1]);
}
+/*********************************************************************
+ function: closemailpipe
+ description: close pipe to the mail delivery agent.
+ arguments:
+ queryctl fully-determined options record
+ fd pipe descriptor.
+
+ return value: 0 if success, else -1.
+ calls: none.
+ globals: none.
+ *********************************************************************/
+
+int closemailpipe (fd)
+int fd;
+{
+ int err;
+ int childpid;
+
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr, "about to close pipe %d\n", fd);
+ err = close(fd);
+#if defined(STDC_HEADERS)
+ childpid = wait(NULL);
+#else
+ childpid = wait((int *) 0);
+#endif
+ if (err)
+ perror("fetchmail: closemailpipe: close");
+
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr, "closed pipe %d\n", fd);
+
+ return(err);
+}
#define PASSWORDLEN MAX_PASSWORD_LENGTH
#define FOLDERLEN 256 /* max folder name length */
#define DIGESTLEN 33 /* length of MD5 digest */
+#define MDALEN 256 /* length of delivery agent command */
#define IDLEN 128 /* length of UIDL message ID */
/* exit code values */
char password [PASSWORDLEN+1];
char remotefolder [FOLDERLEN];
char smtphost[HOSTLEN+1];
+ char mda [MDALEN+1];
int protocol;
int port;
+ /* MDA arguments */
+ char *mda_argv[32];
+ char mdabuf[MDALEN+1];
+
/* control flags */
int keep;
int fetchall;
int setdefaults (struct hostrec *);
char *getnextserver (int argc, char **, int *);
char *MD5Digest (char *);
+int openmailpipe (struct hostrec *);
void append_server_names(int *, char **, int);
int daemonize(const char *, void (*)(int));
IMAP4 (as specified by RFC1730). It can use (but does not require)
the RPOP and LAST facilities removed from later POP3 versions.
.PP
-As each message is retrieved \fIfetchmail\fR delivers it via SMTP to
+As each message is retrieved \fIfetchmail\fR normally delivers it via SMTP to
port 25 on the machine it is running on (localhost), just as though it
were being passed in over a normal TCP/IP link. The mail will then be
delivered locally via your system's MDA (Mail Delivery Agent, usually
.B \-S host, --smtphost host
Specify an host to forward mail to (other than localhost).
.TP
+.B \-m, \--mda
+You can force mail to be passed to an MDA directly (rather than
+forwarded to port 25) with the -mda or -m
+option (this can be useful if you don't want to run sendmail as an
+SMTP listener for security or other reasons).
+Some possible MDAs are "/usr/sbin/sendmail -oem %s",
+"/usr/lib/sendmail -oem %s",
+"/usr/formail", and "/usr/bin/deliver %s" (if the MDA command contains
+%s, that escape will be expanded into your username on the client
+machine).
+.TP
.B \-F, --flush
POP3/IMAP only. Delete old (previously retrieved) messages from the mailserver
before retrieving new messages.
password (or pass)
remotefolder (or remote)
smtphost (or smtp)
+ mda
keep
flush
fetchall
proto pop3
user jsmith
pass "u can't krak this"
+ mda "/bin/mail %s"
.fi
Finally, you may have an initial server description headed by the keyword
#define LA_REMOTEFILE 13
#define LA_PORT 14
#define LA_SMTPHOST 15
-#define LA_LOGFILE 16
-#define LA_QUIT 17
-#define LA_NOREWRITE 18
-#define LA_HELP 19
-#define LA_YYDEBUG 20
+#define LA_MDA 16
+#define LA_LOGFILE 17
+#define LA_QUIT 18
+#define LA_NOREWRITE 19
+#define LA_HELP 20
+#define LA_YYDEBUG 21
-static char *shortoptions = "PVaKkvS:sFd:f:u:r:L:qN?";
+static char *shortoptions = "PVaKkvS:m:sFd:f:u:r:L:qN?";
static struct option longoptions[] = {
{"version", no_argument, (int *) 0, LA_VERSION },
{"all", no_argument, (int *) 0, LA_ALL },
{"remote", required_argument, (int *) 0, LA_REMOTEFILE },
{"port", required_argument, (int *) 0, LA_PORT },
{"smtphost", required_argument, (int *) 0, LA_SMTPHOST },
+ {"mda", required_argument, (int *) 0, LA_MDA },
{"logfile", required_argument, (int *) 0, LA_LOGFILE },
{"quit", no_argument, (int *) 0, LA_QUIT },
{"norewrite", no_argument, (int *) 0, LA_NOREWRITE },
case LA_REMOTEFILE:
strncpy(queryctl->remotefolder,optarg,sizeof(queryctl->remotefolder)-1);
break;
+ case 'm':
+ case LA_MDA:
+ strncpy(queryctl->mda,optarg,sizeof(queryctl->mda));
+ break;
case 'P':
case LA_PORT:
queryctl->port = atoi(optarg);
pass(word)? { return KW_PASSWORD; }
remote(folder)? { return KW_REMOTEFOLDER; }
smtp(host)? { return KW_SMTPHOST; }
+mda { return KW_MDA; }
keep { yylval.flag = FLAG_TRUE; return KW_KEEP; }
flush { yylval.flag = FLAG_TRUE; return KW_FLUSH; }
fetchall { yylval.flag = FLAG_TRUE; return KW_FETCHALL; }
}
%token KW_SERVER KW_PROTOCOL KW_USERNAME KW_PASSWORD
-%token KW_REMOTEFOLDER KW_SMTPHOST KW_DEFAULTS
+%token KW_REMOTEFOLDER KW_SMTPHOST KW_MDA KW_DEFAULTS
%token <proto> KW_PROTO
%token <sval> PARAM_STRING
%token <flag> KW_KEEP KW_FLUSH KW_FETCHALL KW_REWRITE KW_PORT KW_SKIP
| KW_PASSWORD PARAM_STRING {prc_setpassword($2);}
| KW_REMOTEFOLDER PARAM_STRING {prc_setremote($2);}
| KW_SMTPHOST PARAM_STRING {prc_setsmtphost($2);}
+ | KW_MDA PARAM_STRING {prc_setmda($2);}
| KW_KEEP {prc_setkeep($1==FLAG_TRUE);}
| KW_FLUSH {prc_setflush($1==FLAG_TRUE);}
| KW_FETCHALL {prc_setfetchall($1==FLAG_TRUE);}
# remotefolder (or remote)
# localfolder (or local)
# smtphost (or smtp)
+# mda
# keep
# flush
# fetchall