From 8a6226720dbb281ad0b409b06e182621cc08b241 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Thu, 17 Jul 2003 00:55:18 +0000 Subject: First round ofmlong-delayed bug fixes. svn path=/trunk/; revision=3814 --- NEWS | 15 +- configure.in | 2 +- fetchmail-features.html | 8 +- fetchmail.c | 2 +- fetchmail.man | 42 ++--- fetchmail.py | 367 ++++++++++++++++++++++++++++++++++++-- fetchmailconf | 4 +- imap.c | 5 +- pop3.c | 5 +- sink.c | 13 +- todo.html | 6 +- torturetest.glade | 457 +++++++++++++++++++++++++++++++++++++++++------- torturetest.py | 65 +++++-- transact.c | 9 +- 14 files changed, 853 insertions(+), 147 deletions(-) diff --git a/NEWS b/NEWS index 20d09814..33d6d0b2 100644 --- a/NEWS +++ b/NEWS @@ -2,13 +2,22 @@ (The `lines' figures total .c, .h, .l, and .y files under version control.) +* German, Danish, Spanish, and Turkish translations updated. +* Brian Sammon's patch to deal with malformed message lines containiing NULs. +* Fai's patch to ignore all but the first Return-Path (some spams have + more than one of these). +* Benjamin Drieu's ptch to properly byte-stuff when talking to BSNTP. + Fixes Debian bug #184469. +* Benjamin Drieu's patch to enable auth=cram-md5. + Fixes Debian bug #185232. + fetchmail-6.2.2 (Fri Feb 28 21:34:26 EST 2003), 22345 lines: -* Sunil Shetye's patch to imprive behavior in empty messages. +* Sunil Shetye's patch to improve behavior on empty messages. * Conform to RFC2595; reissue capability probes after successful STARTTLS negotiation. * Sunil's patch to make handling of failed STARTTLS more graceful. -* Sunil's JF2 fix patch for .fetchmailrc security fix. +* Sunil's JF2 fix patch for .fetchmailrc security. * Christophe GIAUME finished the implementation of RFC2177 IDLE. * Jason Tishler's fix patch for Cygwin. @@ -32,7 +41,7 @@ fetchmail-6.2.0 (Fri Dec 13 00:10:07 EST 2002), 22235 lines: * Applied Steffen Esser's fix for a buffer-overflow bug in rfc822.c * Updated Danish, German, and Turkish translation files. -* Sunil Sheye's SMTP timeout patch. +* Sunil Shetye's SMTP timeout patch. There are 538 people on fetchmail-friends and 701 on fetchmail-announce. diff --git a/configure.in b/configure.in index d087e463..6b86ebd0 100644 --- a/configure.in +++ b/configure.in @@ -68,7 +68,7 @@ then fi dnl i18n -ALL_LINGUAS="ca cs da de es fr gl ja pl pt_BR tr sk" +ALL_LINGUAS="ca cs da de el es fr gl ja pl pt_BR tr sk" AM_GNU_GETTEXT dnl end i18n diff --git a/fetchmail-features.html b/fetchmail-features.html index 76baad07..cbb4663b 100644 --- a/fetchmail-features.html +++ b/fetchmail-features.html @@ -18,7 +18,7 @@ Back to Fetchmail Home Page To Site Map -$Date: 2003/02/28 10:58:25 $ +$Date: 2003/07/17 00:55:17 $ @@ -61,10 +61,10 @@ delivered-to headers looking exactly the same as the ones it adds himself, it bounces the message.
  • Added --smtpname to set username and domain portion of SMTP -"RCPT TO" command. >fetchmail@mail.julianhaight.com>.
  • +"RCPT TO" command. <fetchmail@mail.julianhaight.com>.
  • Added "from" server's IP address to inserted Received line ->fetchmail@mail.julianhaight.com<.
  • +<fetchmail@mail.julianhaight.com<.
  • Fetchmail now runs on BeOS, thanks to David Reid <david@jetnet.co.uk>.
  • @@ -280,7 +280,7 @@ be unique to fetchmail if I hadn't added it to fetchpop.) Back to Fetchmail Home Page To Site Map -$Date: 2003/02/28 10:58:25 $ +$Date: 2003/07/17 00:55:17 $ diff --git a/fetchmail.c b/fetchmail.c index e541aa57..8a0cdf0b 100644 --- a/fetchmail.c +++ b/fetchmail.c @@ -497,7 +497,7 @@ int main(int argc, char **argv) } else { - if (run.logfile && access(run.logfile, F_OK) == 0) + if (run.logfile && !nodetach && access(run.logfile, F_OK) == 0) { if (!freopen(run.logfile, "a", stdout)) report(stderr, GT_("could not open %s to append logs to \n"), run.logfile); diff --git a/fetchmail.man b/fetchmail.man index ce90f441..86a01bd5 100644 --- a/fetchmail.man +++ b/fetchmail.man @@ -401,23 +401,25 @@ this option. For the command-line option, the list values should be comma-separated. .TP .B \-m | \-\-mda -(Keyword: mda) -You can force mail to be passed to an MDA directly (rather than -forwarded to port 25) with the --mda or -m option. To avoid losing -mail, use this option only with MDAs like procmail or sendmail that -return a nonzero status on disk-full and other resource-exhaustion -errors; the nonzero status tells fetchmail that delivery failed and -prevents the message from being deleted off the server. If -\fIfetchmail\fR is running as root, it sets its userid to that of the -target user while delivering mail through an MDA. Some possible MDAs -are "/usr/sbin/sendmail -i -f %F %T", "/usr/bin/deliver" and -"/usr/bin/procmail -d %T" (but the latter is usually redundant as it's -what SMTP listeners normally forward to). Local delivery addresses -will be inserted into the MDA command wherever you place a %T; the -mail message's From address will be inserted where you place an %F. -Do \fInot\fR use an MDA invocation like "sendmail -i -t" that -dispatches on the contents of To/Cc/Bcc, it will create mail loops and -bring the just wrath of many postmasters down upon your head. +(Keyword: mda) You can force mail to be passed to an MDA directly +(rather than forwarded to port 25) with the --mda or -m option. To +avoid losing mail, use this option only with MDAs like procmail or +sendmail that return a nonzero status on disk-full and other +resource-exhaustion errors; the nonzero status tells fetchmail that +delivery failed and prevents the message from being deleted off the +server. If \fIfetchmail\fR is running as root, it sets its userid to +that of the target user while delivering mail through an MDA. Some +possible MDAs are "/usr/sbin/sendmail -i -f %F %T", "/usr/bin/deliver" +and "/usr/bin/procmail -d %T" (but the latter is usually redundant as +it's what SMTP listeners normally forward to). Local delivery +addresses will be inserted into the MDA command wherever you place a +%T; the mail message's From address will be inserted where you place +an %F. In both cases the addresses are enclosed in single quotes ('), +after removing any single quotes they may contain, before the MDA +command is passed to the shell. Do \fInot\fR use an MDA invocation +like "sendmail -i -t" that dispatches on the contents of To/Cc/Bcc, it +will create mail loops and bring the just wrath of many postmasters +down upon your head. .TP .B \-\-lmtp (Keyword: lmtp) @@ -1320,6 +1322,9 @@ T} interval \& T{ Only check this site every N poll cycles; N is a numeric argument. T} +tracepolls \& T{ +Add poll tracing information to the Received header +T} netsec \& T{ Pass in IPsec security option request. T} @@ -1476,9 +1481,6 @@ T} expunge -e T{ Perform an expunge on every #th message (IMAP and POP3 only) T} -tracepolls \& T{ -Add poll tracing information to the Received header -T} properties \& T{ String value is ignored by fetchmail (may be used by extension scripts) T} diff --git a/fetchmail.py b/fetchmail.py index ef3c5212..889f59ab 100755 --- a/fetchmail.py +++ b/fetchmail.py @@ -49,17 +49,6 @@ KPOP_PORT = 1109 SIMAP_PORT = 993 SPOP3_PORT = 995 -# authentication types -A_ANY = 0 # use the first method that works -A_PASSWORD = 1 # password authentication -A_NTLM = 2 # Microsoft NTLM protocol -A_CRAM_MD5 = 3 # CRAM-MD5 shrouding (RFC2195) -A_OTP = 4 # One-time password (RFC1508) -A_KERBEROS_V4 = 5 # authenticate w/ Kerberos V4 -A_KERBEROS_V5 = 6 # authenticate w/ Kerberos V5 -A_GSSAPI = 7 # authenticate with GSSAPI -A_SSH = 8 # authentication at session level - def DOTLINE(s): return (s[0] == '.' and (s[1]=='\r' or s[1]=='\n' or s[1]=='\0')) @@ -174,11 +163,10 @@ class hostdata: akalist = [] # server name first, then akas localdomains = [] # list of pass-through domains protocol = None # protocol type - service = None # IPv6 service name netsec = None # IPv6 security request - port = 0 # TCP/IP service port number + port = None # TCP/IP service port number (name in IPV6) interval = 0 # cycles to skip between polls - authenticate = 'A_PASSWD' # authentication mode to try + authenticate = 'password' # authentication mode to try timeout = 300 # inactivity timout in seconds envelope = None # envelope address list header envskip = 0 # skip to numbered envelope header @@ -210,6 +198,12 @@ class hostdata: lead_server = None # ptr to lead query for this server esmtp_options = [] # ESMTP option list + def is_mailbox_protocol(self): + # We need to distinguish between mailbox and mailbag protocols. + # Under a mailbox protocol we're pulling mail for a speecific user. + # Under a mailbag protocol we're fetching mail for an entire domain. + return self.protocol != proto_etrn + class query: "All the parameters of a fetchmail query." # mailserver connection controls @@ -283,11 +277,346 @@ class query: mimemsg = 0 # bitmask indicating MIME body-type digest = None - def mailbox_protocol(self): - # We need to distinguish between mailbox and mailbag protocols. - # Under a mailbox protocol we're pulling mail for a speecific user. - # Under a mailbag protocol we're fetching mail for an entire domain. - return self.server.protocol != proto_etrn + def dump(self): + print "Options for retrieving from %s@%s:" \ + % (self.remotename, self.server.pollname) + if self.server.via and self.server.server.is_mailbox_protocol(): + print " Mail will be retrieved via %s" % self.server.via + if self.server.interval: + print " Poll of this server will occur every %d intervals." \ + % self.server.interval; + if self.server.truename: + print " True name of server is %s." % self.server.truename + if self.server.skip || outlevel >= O_VERBOSE: + if self.server.skip: + print " Will not be queried when no host is specified." + else: + print " Will not be queried when no host is specified." + if self.server.authenticate not in ('KERBEROS', 'GSSAPI', 'SSH'): + if not self.password: + print " Password will be prompted for." + else if outlevel >= O_VERBOSE: + if self.server.protocol == proto_apop: + print " APOP secret = \"%s\"." % self.password + elif self.server.protocol == proto_rpop: + print " RPOP id = \"%s\"." % self.password + else + print " Password = \"%s\"." % self.password + + if self.server.protocol == proto_pop3 \ + and self.server.port == KPOP_PORT \ + and self.server.authenticate.startswith("Kerberos"): + sys.stdout.write(" Protocol is KPOP with %s authentication" \ + % self.server.authenticate) + else + sys.stdout.write(" Protocol is %s" % self.server.protocol.name) + if ipv6: + if self.server.port: + sys.stdout.write(" (using service %s)" % self.server.port) + if (self.server.netsec) + sys.stdout.write(" (using network security options %s)" % self.server.netsec) + else: + if self.server.port: + sys.stdout.write(" (using port %d)" % self.server.port) + else if outlevel >= O_VERBOSE: + sys.stdout.write(" (using default port)") + if self.server.uidl and self.server.is_mailbox.protocol()) + sys.stdout.write(" (forcing UIDL use)") + sys.stdout.write("\n") + print { + None : " All available authentication methods will be tried.", + 'password' : " Password authentication will be forced.", + 'NTLM' : " NTLM authentication will be forced.", + 'OTP' : " OTP authentication will be forced.", + 'CRAM-MD5' " CRAM-MD5 authentication will be forced.", + 'GSSAPI' : " GSSAPI authentication will be forced.", + 'Kerberos V4' : " Kerberos V4 authentication will be forced.", + 'Kerberos V5' : " Kerberos V5 authentication will be forced.", + 'ssh' : " End-to-end encryption will be assumed.", + }[self.server.authenticate] + + if self.server.principal: + print " Mail service principal is: %s" % self.server.principal + if self.use_ssl: + print " SSL encrypted sessions enabled." + if self.sslproto: + print " SSL protocol: %s." % self.sslproto; + if self.sslcertck: + print " SSL server certificate checking enabled." + if self.sslcertpath: + print " SSL trusted certificate directory: %s" % self.sslcertpath; + if self.sslfingerprint: + print " SSL key fingerprint (checked against the server key): %s" % self.sslfingerprint; + if self.server.timeout > 0: + print " Server nonresponse timeout is %d seconds" % self.server.timeout; + if self.server.is_mailbox_protocol(): + if not self.mailboxes.id: + print " Default mailbox selected." + else + print " Selected mailboxes are: ", ", ".join(self.mailboxes) + flagarray = ( + ('fetchall', + "%s messages will be retrieved (--all %s)." + "All", "Only new") + ('keep', + " Fetched messages %s be kept on the server (--keep %s)." + "will", "will not") + ('flush', + " Old messages %s be flushed before message retrieval (--flush %s).", + "will", "will not") + ('rewrite', + " Rewrite of server-local addresses is %s (norewrite %s).", + "enabled", "disabled") + ('stripcr', + " Carriage-return stripping is %s (stripcr %s).", + "enabled", "disabled") + ('forcecr', + " Carriage-return forcing is %s (forcecr %s).", + "enabled", "disabled") + ('pass8bits', + " Interpretation of Content-Transfer-Encoding is %s (pass8bits %s).", + "enabled", "disabled") + ('mimedecode', + " MIME decoding is %s (mimedecode %s).", + "enabled", "disabled") + ('idle', + " Idle after poll is %s (idle %s).", + "enabled", "disabled") + ('dropstatus', + " Nonempty Status lines will be %s (dropstatus %s)", + "discarded", "kept") + ('dropdelivered', + " Delivered-To lines will be %s (dropdelivered %s)", + "discarded", "kept") + ) + for (attr, template, on, off) in flagarray: + flag = getattr(self, att) + if flag: + onoff1 = on + onoff2 = "on" + else: + onoff1 = off + onoff2 = "off" + print template % (onoff1, onoff2) + if self.limit: + { + if NUM_NONZERO(self.limit): + print " Message size limit is %d octets (--limit %d)." % + self.limit, self.limit); + else if outlevel >= O_VERBOSE: + print " No message size limit (--limit 0)." + if run.poll_interval > 0: + print " Message size warning interval is %d seconds (--warnings %d)." % + self.warnings, self.warnings); + else if outlevel >= O_VERBOSE: + print " Size warnings on every poll (--warnings 0)." + } + if NUM_NONZERO(self.fetchlimit): + print " Received-message limit is %d (--fetchlimit %d)."), + self.fetchlimit, self.fetchlimit); + else if outlevel >= O_VERBOSE: + print " No received-message limit (--fetchlimit 0)." + if NUM_NONZERO(self.batchlimit): + print " SMTP message batch limit is %d." % self.batchlimit); + else if outlevel >= O_VERBOSE: + print " No SMTP message batch limit (--batchlimit 0)." + if MAILBOX_PROTOCOL(ctl): + { + if NUM_NONZERO(self.expunge): + print " Deletion interval between expunges forced to %d (--expunge %d)." % self.expunge, self.expunge); + else if outlevel >= O_VERBOSE: + print " No forced expunges (--expunge 0)." + } + } + else /* ODMR or ETRN */ + { + struct idlist *idp; + + print " Domains for which mail will be fetched are:" + for (idp = self.domainlist; idp; idp = idp.next: + { + printf(" %s", idp.id); + if not idp.val.status.mark: + print " (default)" + } + printf(""); + } + if self.bsmtp: + print " Messages will be appended to %s as BSMTP" % visbuf(self.bsmtp + else if self.mda and MAILBOX_PROTOCOL(ctl): + print " Messages will be delivered with \"%s\"." % visbuf(self.mda + else + { + struct idlist *idp; + + if self.smtphunt: + { + print " Messages will be %cMTP-forwarded to:" % + self.listener); + for (idp = self.smtphunt; idp; idp = idp.next: + { + printf(" %s", idp.id); + if not idp.val.status.mark: + print " (default)" + } + printf(""); + } + if self.smtpaddress: + print " Host part of MAIL FROM line will be %s"), + self.smtpaddress); + if self.smtpname: + print " Address to be put in RCPT TO lines shipped to SMTP will be %s"), + self.smtpname); + } + if MAILBOX_PROTOCOL(ctl): + { + if self.antispam != (struct idlist *)NULL: + { + struct idlist *idp; + + print " Recognized listener spam block responses are:" + for (idp = self.antispam; idp; idp = idp.next: + printf(" %d", idp.val.status.num); + printf(""); + } + else if outlevel >= O_VERBOSE: + print " Spam-blocking disabled" + } + if self.preconnect: + print " Server connection will be brought up with \"%s\"."), + visbuf(self.preconnect + else if outlevel >= O_VERBOSE: + print " No pre-connection command." + if self.postconnect: + print " Server connection will be taken down with \"%s\"."), + visbuf(self.postconnect + else if outlevel >= O_VERBOSE: + print " No post-connection command." + if MAILBOX_PROTOCOL(ctl)) { + if !self.localnames: + print " No localnames declared for this host." + else + { + struct idlist *idp; + int count = 0; + + for (idp = self.localnames; idp; idp = idp.next: + ++count; + + if count > 1 || self.wildcard: + print " Multi-drop mode: " + else + print " Single-drop mode: " + + print "%d local name(s) recognized." % count); + if outlevel >= O_VERBOSE: + { + for (idp = self.localnames; idp; idp = idp.next: + if idp.val.id2: + printf("\t%s . %s", idp.id, idp.val.id2); + else + printf("\t%s", idp.id); + if self.wildcard: + fputs("\t*", stdout); + } + + if count > 1 || self.wildcard: + { + print " DNS lookup for multidrop addresses is %s."), + self.server.dns ? GT_("enabled") : GT_("disabled" + if self.server.dns: + { + print " Server aliases will be compared with multidrop addresses by " + if self.server.checkalias: + print "IP address." + else + print "name." + } + if self.server.envelope == STRING_DISABLED: + print " Envelope-address routing is disabled" + else + { + print " Envelope header is assumed to be: %s"), + self.server.envelope ? self.server.envelope:GT_("Received" + if self.server.envskip > 1 || outlevel >= O_VERBOSE: + print " Number of envelope header to be parsed: %d"), + self.server.envskip); + if self.server.qvirtual: + print " Prefix %s will be removed from user id"), + self.server.qvirtual); + else if outlevel >= O_VERBOSE) + print " No prefix stripping" + } + + if self.server.akalist: + { + struct idlist *idp; + + print " Predeclared mailserver aliases:" + for (idp = self.server.akalist; idp; idp = idp.next: + printf(" %s", idp.id); + putchar(''); + } + if self.server.localdomains: + { + struct idlist *idp; + + print " Local domains:" + for (idp = self.server.localdomains; idp; idp = idp.next: + printf(" %s", idp.id); + putchar(''); + } + } + } + } +#if defined(linux) || defined(__FreeBSD__: + if self.server.interface: + print " Connection must be through interface %s." % self.server.interface); + else if outlevel >= O_VERBOSE: + print " No interface requirement specified." + if self.server.monitor: + print " Polling loop will monitor %s." % self.server.monitor); + else if outlevel >= O_VERBOSE: + print " No monitor interface specified." +#endif + + if self.server.plugin: + print " Server connections will be made via plugin %s (--plugin %s)." % self.server.plugin, self.server.plugin); + else if outlevel >= O_VERBOSE: + print " No plugin command specified." + if self.server.plugout: + print " Listener connections will be made via plugout %s (--plugout %s)." % self.server.plugout, self.server.plugout); + else if outlevel >= O_VERBOSE: + print " No plugout command specified." + + if self.server.protocol > P_POP2 and MAILBOX_PROTOCOL(ctl): + { + if !self.oldsaved: + print " No UIDs saved from this host." + else + { + struct idlist *idp; + int count = 0; + + for (idp = self.oldsaved; idp; idp = idp.next: + ++count; + + print " %d UIDs saved." % count); + if outlevel >= O_VERBOSE: + for (idp = self.oldsaved; idp; idp = idp.next: + printf("\t%s", idp.id); + } + } + + if self.tracepolls: + print " Poll trace information will be added to the Received header." + else if outlevel >= O_VERBOSE: + print " No poll trace information will be added to the Received header.." + + if self.properties: + print " Pass-through properties \"%s\"." % self.properties + + if __name__ == '__main__': # C version queried FETCHMAILUSER, then USER, then LOGNAME. diff --git a/fetchmailconf b/fetchmailconf index a6c3d172..86ab9445 100755 --- a/fetchmailconf +++ b/fetchmailconf @@ -1844,10 +1844,10 @@ whether to use POP or IMAP, and so forth). self.configbutton.pack() Message(self, text=""" -Use `Test fetchmail' to run fetchmail with debugging enabled. +Use `Run fetchmail' to run fetchmail with debugging enabled. This is a good way to test out a new configuration. """, width=600).pack(side=TOP) - Button(self, text='Test fetchmail',fg='blue', command=self.test).pack() + Button(self, text='Run fetchmail',fg='blue', command=self.test).pack() Message(self, text=""" Use `Run fetchmail' to run fetchmail in foreground. diff --git a/imap.c b/imap.c index c3ec1395..3e6d80fc 100644 --- a/imap.c +++ b/imap.c @@ -416,9 +416,8 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) * in a challenge-response. */ - if ((ctl->server.authenticate == A_ANY - || ctl->server.authenticate == A_CRAM_MD5) - && strstr(capabilities, "AUTH=CRAM-MD5")) + if ((ctl->server.authenticate == A_ANY && strstr(capabilities, "AUTH=CRAM-MD5")) + || ctl->server.authenticate == A_CRAM_MD5) { if ((ok = do_cram_md5 (sock, "AUTHENTICATE", ctl, NULL))) { diff --git a/pop3.c b/pop3.c index 02e38c3f..50673ffe 100644 --- a/pop3.c +++ b/pop3.c @@ -354,9 +354,8 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting) } #endif /* OPIE_ENABLE */ - if (has_cram && - (ctl->server.authenticate == A_CRAM_MD5 || - ctl->server.authenticate == A_ANY)) + if (ctl->server.authenticate == A_CRAM_MD5 || + (has_cram && ctl->server.authenticate == A_ANY)) { ok = do_cram_md5(sock, "AUTH", ctl, NULL); if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY) diff --git a/sink.c b/sink.c index aee27f7e..a8395661 100644 --- a/sink.c +++ b/sink.c @@ -624,10 +624,15 @@ int stuffline(struct query *ctl, char *buf) } else /* if (!protocol->delimited) -- not byte-stuffed already */ { - if (!ctl->mda) - SockWrite(ctl->smtp_socket, buf, 1); /* byte-stuff it */ - else - /* leave it alone */; + if (!ctl->mda) /* byte-stuff it */ + { + if (!ctl->bsmtp) + SockWrite(ctl->smtp_socket, buf, 1); + else + { + fwrite(buf, 1, 1, sinkfp); + } + } } } diff --git a/todo.html b/todo.html index 76285949..5d695b55 100644 --- a/todo.html +++ b/todo.html @@ -19,7 +19,7 @@ content="Known bugs and to-do items in fetchmail" /> Back to Eric's Home Page Up to Site Map -$Date: 2003/02/28 21:57:59 $ +$Date: 2003/07/17 00:55:18 $ @@ -55,6 +55,8 @@ debugger and check this.

    In the SSL support, add authentication of Certifying Authority (Is this a Certifying Authority we recognize?).

    +

    Debian wishlist item 181157: ssl key learning for self-signed certificates.

    +

    Laszlo Vecsey writes: "I believe qmail uses a technique of writing temporary files to nfs, and then moving them into place to ensure that they're written. Actually a hardlink is made to the @@ -80,7 +82,7 @@ reports.

    Back to Eric's Home Page Up to Site Map -$Date: 2003/02/28 21:57:59 $ +$Date: 2003/07/17 00:55:18 $ diff --git a/torturetest.glade b/torturetest.glade index 4f648a97..d2cf07ee 100644 --- a/torturetest.glade +++ b/torturetest.glade @@ -107,7 +107,7 @@ True 9 2 - True + False 0 0 @@ -475,64 +475,127 @@ - + + True + True + True + True + 0 + + True + * + False + + + 1 + 2 + 0 + 1 + + + + + + True - True + False 0 - + True - True - POP3 - True - GTK_RELIEF_NORMAL - False - False - True + True + 0 + + + + True + True + POP3 + True + GTK_RELIEF_NORMAL + False + False + True + + + 0 + False + False + + + + + + True + True + APOP + True + GTK_RELIEF_NORMAL + False + False + True + POP3_radiobutton + + + 0 + False + False + + + + + + True + True + IMAP + True + GTK_RELIEF_NORMAL + False + False + True + POP3_radiobutton + + + 0 + False + False + + 0 - False - False + True + True - + True - True - APOP - True - GTK_RELIEF_NORMAL - False - False - True - POP3_radiobutton 0 - False - False + True + True - + True True - IMAP + SSL True GTK_RELIEF_NORMAL False False True - POP3_radiobutton 0 - False - False + True + True @@ -545,32 +608,11 @@ fill - - - - True - True - True - True - 0 - - True - * - False - - - 1 - 2 - 0 - 1 - - - 0 - True - True + False + False @@ -592,56 +634,339 @@ 0 - + True True True - Update - True GTK_RELIEF_NORMAL + + + + + True + 0.5 + 0.5 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-apply + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + Update + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + - + True True True - New - True GTK_RELIEF_NORMAL + + + + True + 0.5 + 0.5 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-new + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + New + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + - + True True True - Test - True GTK_RELIEF_NORMAL + + + + True + 0.5 + 0.5 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-execute + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + Test + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + - + True True True - Quit - True GTK_RELIEF_NORMAL + + + + True + 0.5 + 0.5 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-quit + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + Quit + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + + + + + + + True + True + True + GTK_RELIEF_NORMAL + + + + + True + 0.5 + 0.5 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-print + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + Dump + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + 0 - True - True + False + False diff --git a/torturetest.py b/torturetest.py index c0c07e09..f8a3535c 100755 --- a/torturetest.py +++ b/torturetest.py @@ -2,23 +2,27 @@ import sys, getopt, os, smtplib, commands, time, gtk, gtk.glade +protocols = ('POP3', 'APOP', 'IMAP',) + class TestSite: temp = "/usr/tmp/torturestest-%d" % os.getpid() def __init__(self, line=None): "Initialize site data from the external representation." - self.site = "" + self.host = "" self.mailaddr = "" self.username = "" self.password = "" self.protocol = "" + self.ssl = "" self.options = "" self.capabilities = "" self.recognition = "" self.comment = "" if line: (self.host, self.mailaddr, self.username, self.password, \ - self.protocol, self.options, self.capabilities, self.recognition, self.comment) = \ + self.protocol, self.ssl, self.options, self.capabilities, \ + self.recognition, self.comment) = \ line.strip().split(":") if not self.mailaddr: self.mailaddr = self.username @@ -27,9 +31,9 @@ class TestSite: self.output = None def allattrs(self): - "Return a tuple consisting of alll this site's attributes." + "Return a tuple consisting of all this site's attributes." return (self.host, self.mailaddr, self.username, self.password, \ - self.protocol, self.options, self.capabilities, \ + self.protocol, self.ssl, self.options, self.capabilities, \ self.recognition, self.comment) def __repr__(self): @@ -43,6 +47,7 @@ class TestSite: "Username: %s\n" \ "Password: %s\n" \ "Protocol: %s\n" \ + "SSL: %s\n" \ "Options: %s\n" \ "Capabilities: %s\n" \ "Recognition: %s\n" \ @@ -51,9 +56,13 @@ class TestSite: def entryprint(self): "Print a .fetchmailrc entry corresponding to a site entry." - return "poll %s-%s via %s with proto %s %s\n" \ - " user %s there with password '%s' is esr here\n\n" \ + rep = "poll %s-%s via %s with proto %s %s\n" \ + " user %s there with password '%s' is esr here" \ % (self.host,self.protocol,self.host,self.protocol,self.options,self.username,self.password) + if self.ssl and self.ssl != 'False': + rep += " options ssl" + rep += "\n\n" + return rep def tableprint(self): "Print an HTML server-type table entry." @@ -84,14 +93,20 @@ class TestSite: server.sendmail(fromaddr, toaddr, msg) server.quit() - def fetch(self): + def fetch(self, logfile=False): "Run a mail fetch on this site." try: ofp = open(TestSite.temp, "w") - ofp.write('defaults mda "(echo; echo \'From torturetest\' `date`; cat) >>TEST.LOG"\n') + if logfile: + mda = "(echo; echo \'From torturetest\' `date`;cat) >>TEST.LOG" + else: + mda = 'cat' + ofp.write('defaults mda "%s"\n' % mda) ofp.write(self.entryprint()) ofp.close() (self.status, self.output) = commands.getstatusoutput("fetchmail -d0 -v -f - <%s"%TestSite.temp) + if self.status: + os.system("cat " + TestSite.temp) finally: os.remove(TestSite.temp) @@ -121,6 +136,7 @@ class TortureGUI: # File in initial values self.combo = self.wtree.get_widget("combo1") self.combo.set_popdown_strings(map(lambda x: x.comment, sitelist)) + self.sslcheck = self.wtree.get_widget("ssl_checkbox") self.site = sitelist[0] self.display(self.site) @@ -131,6 +147,7 @@ class TortureGUI: 'on_newbutton_clicked', 'on_testbutton_clicked', 'on_quitbutton_clicked', + 'on_dumpbutton_clicked', 'on_combo_entry1_activate'): mydict[key] = getattr(self, key) self.wtree.signal_autoconnect(mydict) @@ -164,22 +181,32 @@ class TortureGUI: def display(self, site): for member in TortureGUI.field_map: self.set_widget(member + "_entry", getattr(site, member)) - for proto in ('POP3', 'APOP', 'IMAP'): + for proto in protocols: self.wtree.get_widget(proto + "_radiobutton").set_active(site.protocol == proto) - self.combo.entry.set_text(self.site.comment) + self.sslcheck.set_active(int(site.ssl != '' and site.ssl != 'False')) + self.combo.entry.set_text(site.comment) def update(self, site): for member in TortureGUI.field_map: setattr(site, member, self.get_widget(member + "_entry")) - for proto in ('POP3', 'APOP', 'IMAP'): - if self.wtree.get_widget(proto + "_radiobutton").get_active(): - site.proto = proto + for proto in protocols: + if self.wtree.get_widget(proto + "_radiobutton").get_active(): + site.protocol = proto + if self.wtree.get_widget("ssl_checkbox").get_active(): + site.ssl = "True" + else: + site.ssl = "False" # Housekeeping def on_torturetest_destroy(self, obj): gtk.mainquit() def on_updatebutton_clicked(self, obj): - self.update() + self.update(self.site) + print self.site + if self.site.comment: + self.combo.entry.set_text(self.site.comment) + else: + self.combo.entry.set_text(self.site.host) def on_newbutton_clicked(self, obj): global sitelist sitelist = [TestSite()] + sitelist @@ -187,11 +214,13 @@ class TortureGUI: self.display(self.site) self.combo.entry.set_text("") def on_testbutton_clicked(self, obj): - self.site.fetch() - print site.output + self.site.fetch(False) + print self.site.output def on_quitbutton_clicked(self, obj): gtk.mainquit() - + def on_dumpbutton_clicked(self, obj): + print `self.site` + def on_combo_entry1_activate(self, obj): key = self.combo.entry.get_text() for site in sitelist: @@ -274,7 +303,7 @@ if __name__ == "__main__": os.system("fetchmail -q") for site in sitelist: print "Testing " + site.id() - site.fetch() + site.fetch(True) if verbose: print site.output if site.failed(): diff --git a/transact.c b/transact.c index 6674a3ff..fb8fb043 100644 --- a/transact.c +++ b/transact.c @@ -381,6 +381,7 @@ int readheaders(int sock, flag headers_ok, has_nuls; int olderrs, good_addresses, bad_addresses; int retain_mail = 0; + flag already_has_return_path = FALSE; sizeticker = 0; has_nuls = headers_ok = FALSE; @@ -676,9 +677,15 @@ int readheaders(int sock, * not trigger bounces if delivery fails. What we *do* need to do is * make sure we never try to rewrite such a blank Return-Path. We * handle this with a check for <> in the rewrite logic above. + * + * Also, if an email has multiple Return-Path: statement, we only + * read the first occurance, as some spam email has more than one + * Return-Path. + * */ - if (!strncasecmp("Return-Path:", line, 12) && (cp = nxtaddr(line))) + if ((already_has_return_path==FALSE) && !strncasecmp("Return-Path:", line, 12) && (cp = nxtaddr(line))) { + already_has_return_path = TRUE; strncpy(msgblk.return_path, cp, sizeof(msgblk.return_path)); msgblk.return_path[sizeof(msgblk.return_path)-1] = '\0'; if (!ctl->mda) { -- cgit v1.2.3