#endif
#include <netdb.h>
#ifdef HAVE_PKG_hesiod
+#ifdef __cplusplus
+extern "C" {
+#endif
#include <hesiod.h>
+#ifdef __cplusplus
+}
+#endif
#endif
#include <langinfo.h>
#include "getaddrinfo.h"
#include "tunable.h"
+#include "sdump.h"
+
/* throw types for runtime errors */
#define THROW_TIMEOUT 1 /* server timed out */
-#define THROW_SIGPIPE 2 /* SIGPIPE on stream socket */
/* magic values for the message length array */
#define MSGLEN_UNKNOWN 0 /* length unknown (0 is impossible) */
flag peek_capable; /* can we peek for better error recovery? */
int mailserver_socket_temp = -1; /* socket to free if connect timeout */
+struct addrinfo *ai0, *ai1; /* clean these up after signal */
+
static volatile int timeoutcount = 0; /* count consecutive timeouts */
static volatile int idletimeout = 0; /* timeout occured in idle stage? */
-static jmp_buf restart;
+static sigjmp_buf restart;
int is_idletimeout(void)
/* last timeout occured in idle stage? */
(void)signal;
if(stage != STAGE_IDLE) {
timeoutcount++;
- longjmp(restart, THROW_TIMEOUT);
+ /* XXX FIXME: this siglongjmp must die - it's not safe to be
+ * called from a function handler and breaks, for instance,
+ * getaddrinfo() */
+ siglongjmp(restart, THROW_TIMEOUT);
} else
idletimeout = 1;
}
-static RETSIGTYPE sigpipe_handler (int signal)
-/* handle SIGPIPE signal indicating a broken stream socket */
-{
- (void)signal;
- longjmp(restart, THROW_SIGPIPE);
-}
-
#define CLEANUP_TIMEOUT 60 /* maximum timeout during cleanup */
static int cleanupSockClose (int fd)
if (retval) {
#ifdef HEIMDAL
if (err_ret && err_ret->e_text) {
- report(stderr, GT_("krb5_sendauth: %s [server says '%*s'] \n"),
- error_message(retval),
- err_ret->e_text);
+ char *t = err_ret->e_text;
+ char *tt = sdump(t, strlen(t));
+ report(stderr, GT_("krb5_sendauth: %s [server says '%s']\n"),
+ error_message(retval), tt);
+ free(tt);
#else
if (err_ret && err_ret->text.length) {
- report(stderr, GT_("krb5_sendauth: %s [server says '%*s'] \n"),
- error_message(retval),
- err_ret->text.length,
- err_ret->text.data);
+ char *tt = sdump(err_ret->text.data, err_ret->text.length);
+ report(stderr, GT_("krb5_sendauth: %s [server says '%s']\n"),
+ error_message(retval), tt);
+ free(tt);
#endif
krb5_free_error(context, err_ret);
} else
size = atoi(current->id);
if (ctl->limitflush)
stuff_warning(NULL, ctl,
- GT_(" %d msg %d octets long deleted by fetchmail."),
+ ngettext(" %d message %d octets long deleted by fetchmail.",
+ " %d messages %d octets long deleted by fetchmail.", nbr),
nbr, size);
else
stuff_warning(NULL, ctl,
- GT_(" %d msg %d octets long skipped by fetchmail."),
+ ngettext(" %d message %d octets long skipped by fetchmail.",
+ " %d messages %d octets long skipped by fetchmail.", nbr),
nbr, size);
}
current->val.status.num++;
}
}
+static int eat_trailer(int sock, struct query *ctl)
+{
+ /* we only need this LF if we're printing ticker dots
+ * AND we are dumping protocol traces. */
+ if (outlevel >= O_VERBOSE && want_progress()) fputc('\n', stdout);
+ return (ctl->server.base_protocol->trail)(sock, ctl, tag);
+}
+
static int fetch_messages(int mailserver_socket, struct query *ctl,
int count, int **msgsizes, int maxfetch,
int *fetches, int *dispatches, int *deletions)
/* check if the message is old
* Note: the size of the message may not be known here */
- if (ctl->fetchall || force_retrieval)
- ;
- else if (ctl->server.base_protocol->is_old && (ctl->server.base_protocol->is_old)(mailserver_socket,ctl,num))
- msgcode = MSGLEN_OLD;
+ if (ctl->fetchall || force_retrieval) {
+ /* empty */
+ } else {
+ if (ctl->server.base_protocol->is_old && (ctl->server.base_protocol->is_old)(mailserver_socket,ctl,num)) {
+ msgcode = MSGLEN_OLD;
+ }
+ }
if (msgcode == MSGLEN_OLD)
{
/* To avoid flooding the syslog when using --keep,
}
else
{
+ /* XXX FIXME: make this one variable, wholesize and
+ separatefetchbody query the same variable just with
+ inverted logic */
flag wholesize = !ctl->server.base_protocol->fetch_body;
flag separatefetchbody = (ctl->server.base_protocol->fetch_body) ? TRUE : FALSE;
if (len > 0)
report_build(stdout, wholesize ? GT_(" (%d octets)")
: GT_(" (%d header octets)"), len);
- if (outlevel >= O_VERBOSE)
- report_complete(stdout, "\n");
+ if (want_progress()) {
+ /* flush and add a blank to append ticker dots */
+ report_flush(stdout);
+ putchar(' ');
+ }
}
/*
/* pass the suppress_readbody flag only if the underlying
* protocol does not fetch the body separately */
separatefetchbody ? 0 : &suppress_readbody);
+
if (err == PS_RETAINED)
suppress_forward = suppress_delete = retained = TRUE;
else if (err == PS_TRANSIENT)
suppress_delete = suppress_forward = TRUE;
else if (err == PS_REFUSED)
suppress_forward = TRUE;
- else if (err == PS_TRUNCATED)
- suppress_readbody = TRUE;
else if (err)
return(err);
/* tell server we got it OK and resynchronize */
if (separatefetchbody && ctl->server.base_protocol->trail)
{
- if (outlevel >= O_VERBOSE && !is_a_file(1) && !run.use_syslog)
- {
- fputc('\n', stdout);
- fflush(stdout);
- }
-
- if ((err = (ctl->server.base_protocol->trail)(mailserver_socket, ctl, tag)))
- return(err);
+ err = eat_trailer(mailserver_socket, ctl);
+ if (err) return(err);
}
/* do not read the body which is not being forwarded only if
*/
if (len == -1)
len = msgsize - msgblk.msglen;
- if (outlevel > O_SILENT && !wholesize)
- report_build(stdout,
- GT_(" (%d body octets)"), len);
+ if (!wholesize) {
+ if (outlevel > O_SILENT)
+ report_build(stdout,
+ GT_(" (%d body octets)"), len);
+ if (want_progress()) {
+ report_flush(stdout);
+ putchar(' ');
+ }
+ }
}
/* process the body now */
ctl,
!suppress_forward,
len);
+
if (err == PS_TRANSIENT)
suppress_delete = suppress_forward = TRUE;
else if (err)
return(err);
/* tell server we got it OK and resynchronize */
- if (ctl->server.base_protocol->trail)
- {
- if (outlevel >= O_VERBOSE && !is_a_file(1) && !run.use_syslog)
- {
- fputc('\n', stdout);
- fflush(stdout);
- }
-
- err = (ctl->server.base_protocol->trail)(mailserver_socket, ctl, tag);
- if (err != 0)
- return(err);
+ if (ctl->server.base_protocol->trail) {
+ err = eat_trailer(mailserver_socket, ctl);
+ if (err) return(err);
}
}
flagthemail:
/*
- * 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.
+ * At this point in flow of control,
+ * either we've bombed on a protocol error
+ * or had delivery refused by the SMTP server
+ * 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.
*/
+ /* in softbounce mode, suppress deletion and marking as seen */
+ if (suppress_forward)
+ suppress_delete = suppress_delete || run.softbounce;
+
/* maybe we delete this message now? */
if (retained)
{
int tmperr;
int deletions = 0, js;
const char *msg;
- SIGHANDLERTYPE pipesave;
SIGHANDLERTYPE alrmsave;
ctl->server.base_protocol = proto;
alrmsave = set_signal_handler(SIGALRM, timeout_handler);
mytimeout = ctl->server.timeout;
- /* set up the broken-pipe timeout */
- pipesave = set_signal_handler(SIGPIPE, sigpipe_handler);
-
- if ((js = setjmp(restart)))
+ if ((js = sigsetjmp(restart,1)))
{
/* exception caught */
-#ifdef HAVE_SIGPROCMASK
- /*
- * Don't rely on setjmp() to restore the blocked-signal mask.
- * It does this under BSD but is required not to under POSIX.
- *
- * If your Unix doesn't have sigprocmask, better hope it has
- * BSD-like behavior. Otherwise you may see fetchmail get
- * permanently wedged after a second timeout on a bad read,
- * because alarm signals were blocked after the first.
- */
sigset_t allsigs;
sigfillset(&allsigs);
sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
-#endif /* HAVE_SIGPROCMASK */
-
- if (js == THROW_SIGPIPE)
- {
- set_signal_handler(SIGPIPE, SIG_IGN);
- report(stdout,
- GT_("SIGPIPE thrown from an MDA or a stream socket error\n"));
- wait(0);
+
+ if (ai0) {
+ fm_freeaddrinfo(ai0); ai0 = NULL;
+ }
+
+ if (ai1) {
+ fm_freeaddrinfo(ai1); ai1 = NULL;
}
- else if (js == THROW_TIMEOUT)
+
+ if (js == THROW_TIMEOUT)
{
if (phase == OPEN_WAIT)
report(stdout,
}
else
{
- /* setjmp returned zero -> normal operation */
+ /* sigsetjmp returned zero -> normal operation */
char buf[MSGBUFSIZE+1], *realhost;
int count, newm, bytes;
int fetches, dispatches, oldphase;
/* execute pre-initialization command, if any */
if (ctl->preconnect && (err = system(ctl->preconnect)))
{
- report(stderr,
- GT_("pre-connection command failed with status %d\n"), err);
+ if (WIFSIGNALED(err))
+ report(stderr,
+ GT_("pre-connection command terminated with signal %d\n"), WTERMSIG(err));
+ else
+ report(stderr,
+ GT_("pre-connection command failed with status %d\n"), WEXITSTATUS(err));
err = PS_SYNTAX;
goto closeUp;
}
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_CANONNAME;
+#ifdef AI_ADDRCONFIG
+ hints.ai_flags |= AI_ADDRCONFIG;
+#endif
- error = getaddrinfo(ctl->server.queryname, NULL, &hints, &res);
+ error = fm_getaddrinfo(ctl->server.queryname, NULL, &hints, &res);
if (error)
{
report(stderr,
- GT_("couldn't find canonical DNS name of %s (%s)\n"),
- ctl->server.pollname, ctl->server.queryname);
+ GT_("couldn't find canonical DNS name of %s (%s): %s\n"),
+ ctl->server.pollname, ctl->server.queryname,
+ gai_strerror(error));
err = PS_DNS;
set_timeout(0);
phase = oldphase;
ctl->server.trueaddr = (struct sockaddr *)xmalloc(res->ai_addrlen);
ctl->server.trueaddr_len = res->ai_addrlen;
memcpy(ctl->server.trueaddr, res->ai_addr, res->ai_addrlen);
- freeaddrinfo(res);
+ fm_freeaddrinfo(res);
}
}
}
(void)sleep(1);
if ((mailserver_socket = SockOpen(realhost,
ctl->server.service ? ctl->server.service : ( ctl->use_ssl ? ctl->server.base_protocol->sslservice : ctl->server.base_protocol->service ),
- ctl->server.plugin)) == -1)
+ ctl->server.plugin, &ai0)) == -1)
{
char errbuf[BUFSIZ];
int err_no = errno;
strlcpy(errbuf, strerror(err_no), sizeof(errbuf));
report_complete(stderr, ": %s\n", errbuf);
-#ifdef __UNUSED__
- /*
- * Don't use this. It was an attempt to address Debian bug
- * #47143 (Notify user by mail when pop server nonexistent).
- * Trouble is, that doesn't work; you trip over the case
- * where your SLIP or PPP link is down...
- */
- /* warn the system administrator */
- if (open_warning_by_mail(ctl) == 0)
- {
- stuff_warning(iana_charset, ctl,
- GT_("Subject: Fetchmail unreachable-server warning."));
- stuff_warning(NULL, ctl, "");
- stuff_warning(NULL, ctl, GT_("Fetchmail could not reach the mail server %s:"),
- ctl->server.pollname);
- stuff_warning(NULL, ctl, errbuf, ctl->server.pollname);
- close_warning_by_mail(ctl, (struct msgblk *)NULL);
- }
-#endif
}
err = PS_SOCKET;
set_timeout(0);
set_timeout(mytimeout);
/* perform initial SSL handshake on open connection */
- /* Note: We pass the realhost name over for certificate
- verification. We may want to make this configurable */
- if (ctl->use_ssl && SSLOpen(mailserver_socket,ctl->sslcert,ctl->sslkey,ctl->sslproto,ctl->sslcertck,
- ctl->sslcertpath,ctl->sslfingerprint,realhost,ctl->server.pollname) == -1)
+ if (ctl->use_ssl &&
+ SSLOpen(mailserver_socket, ctl->sslcert, ctl->sslkey,
+ ctl->sslproto, ctl->sslcertck,
+ ctl->sslcertfile, ctl->sslcertpath,
+ ctl->sslfingerprint, ctl->sslcommonname ?
+ ctl->sslcommonname : realhost, ctl->server.pollname,
+ &ctl->remotename) == -1)
{
report(stderr, GT_("SSL connection failed.\n"));
err = PS_SOCKET;
stage = STAGE_GETAUTH;
if (ctl->server.base_protocol->getauth)
{
+ set_timeout(mytimeout);
err = (ctl->server.base_protocol->getauth)(mailserver_socket, ctl, buf);
+ set_timeout(0);
if (err != 0)
{
ctl->server.truename,
(ctl->wehaveauthed ? GT_(" (previously authorized)") : "")
);
+ if (ctl->server.authenticate == A_ANY && !ctl->wehaveauthed) {
+ report(stderr, GT_("For help, see http://www.fetchmail.info/fetchmail-FAQ.html#R15\n"));
+ }
/*
* If we're running in background, try to mail the
/* end-of-mailbox processing before we repoll or switch to another one */
if (ctl->server.base_protocol->end_mailbox_poll)
{
- err = (ctl->server.base_protocol->end_mailbox_poll)(mailserver_socket, ctl);
- if (err)
+ tmperr = (ctl->server.base_protocol->end_mailbox_poll)(mailserver_socket, ctl);
+ if (tmperr) {
+ err = tmperr;
goto cleanUp;
+ }
}
/* Return now if we have reached the fetchlimit */
if (maxfetch && maxfetch <= fetches)
smtp_close(ctl, 0);
if (mailserver_socket != -1) {
cleanupSockClose(mailserver_socket);
- mailserver_socket = -1;
}
/* If there was a connect timeout, the socket should be closed.
* mailserver_socket_temp contains the socket to close.
/* execute wrapup command, if any */
if (ctl->postconnect && (tmperr = system(ctl->postconnect)))
{
- report(stderr, GT_("post-connection command failed with status %d\n"), tmperr);
+ if (WIFSIGNALED(tmperr))
+ report(stderr, GT_("post-connection command terminated with signal %d\n"), WTERMSIG(tmperr));
+ else
+ report(stderr, GT_("post-connection command failed with status %d\n"), WEXITSTATUS(tmperr));
if (err == PS_SUCCESS)
err = PS_SYNTAX;
}
set_timeout(0); /* cancel any pending alarm */
set_signal_handler(SIGALRM, alrmsave);
- set_signal_handler(SIGPIPE, pipesave);
return(err);
}