# Matthias Andree <matthias.andree@gmx.de>
# Requires Python with Tkinter, and the following OS-dependent services:
# posix, posixpath, socket
-version = "1.51 $Revision$"
+version = "1.58"
from Tkinter import *
from Dialog import *
self.postmaster = None # No last-resort address, initially
self.bouncemail = TRUE # Bounce errors to users
self.spambounce = FALSE # Bounce spam errors
+ self.softbounce = TRUE # Treat permanent error as temporary
self.properties = None # No exiguous properties
self.invisible = FALSE # Suppress Received line & spoof?
self.syslog = FALSE # Use syslogd for logging?
('postmaster', 'String'),
('bouncemail', 'Boolean'),
('spambounce', 'Boolean'),
+ ('softbounce', 'Boolean'),
('properties', 'String'),
('syslog', 'Boolean'),
('invisible', 'Boolean'))
str = str + ("set spambounce\n")
else:
str = str + ("set no spambounce\n")
+ if self.softbounce:
+ str = str + ("set softbounce\n")
+ else:
+ str = str + ("set no softbounce\n")
if self.properties != ConfigurationDefaults.properties:
str = str + ("set properties \"%s\"\n" % (self.properties,));
if self.poll_interval > 0:
str = str + "set daemon " + `self.poll_interval` + "\n"
+ if self.invisible:
+ str = str + ("set invisible\n")
for site in self.servers:
str = str + repr(site)
return str
self.interval = 0 # Skip interval
self.protocol = 'auto' # Default to auto protocol
self.service = None # Service name to use
- self.uidl = FALSE # Don't use RFC1725 UIDLs by default
self.auth = 'any' # Default to password authentication
self.timeout = 300 # 5-minute timeout
self.envelope = 'Received' # Envelope-address header
self.esmtpname = None # ESMTP 2554 name
self.esmtppassword = None # ESMTP 2554 password
self.tracepolls = FALSE # Add trace-poll info to headers
+ self.badheader = FALSE # Pass messages with bad headers on?
+ self.retrieveerror = 'abort' # Policy when message retrieval errors encountered
self.users = [] # List of user entries for site
Server.typemap = (
('pollname', 'String'),
('interval', 'Int'),
('protocol', 'String'),
('service', 'String'),
- ('uidl', 'Boolean'),
('auth', 'String'),
('timeout', 'Int'),
('envelope', 'String'),
('esmtpname', 'String'),
('esmtppassword', 'String'),
('principal', 'String'),
- ('tracepolls','Boolean'))
+ ('tracepolls','Boolean'),
+ ('badheader', 'Boolean'),
+ ('retrieveerror', 'String'))
def dump(self, folded):
res = ""
res = res + (" qvirtual " + str(self.qvirtual) + "\n");
if self.auth != ServerDefaults.auth:
res = res + " auth " + self.auth
- if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl:
- res = res + " and options"
if self.dns != ServerDefaults.dns:
+ res = res + " and options"
res = res + flag2str(self.dns, 'dns')
- if self.uidl != ServerDefaults.uidl:
- res = res + flag2str(self.uidl, 'uidl')
if folded: res = res + "\n "
else: res = res + " "
if self.esmtppassword:
res = res + " esmtppassword " + `self.esmtppassword`
if self.interface or self.monitor or self.principal or self.plugin or self.plugout:
+ if folded:
+ res = res + "\n "
+
+ if self.badheader:
+ res = res + "bad-header accept "
+ if self.retrieveerror == 'continue':
+ res = res + "retrieve-error continue "
+ if self.retrieveerror == 'markseen':
+ res = res + "retrieve-error markseen "
+ if self.badheader or self.retrieveerror != ServerDefaults.retrieveerror:
if folded:
res = res + "\n"
self.warnings = 3600 # Size warning interval (see tunable.h)
self.fetchlimit = 0 # Max messages fetched per batch
self.fetchsizelimit = 100 # Max message sizes fetched per transaction
- self.fastuidl = 10 # Do fast uidl 9 out of 10 times
+ self.fastuidl = 4 # Do fast uidl 3 out of 4 times
self.batchlimit = 0 # Max message forwarded per batch
self.expunge = 0 # Interval between expunges (IMAP)
self.ssl = 0 # Enable Seccure Socket Layer
self.sslproto = None # Force SSL?
self.sslcertck = 0 # Enable strict SSL cert checking
self.sslcertpath = None # Path to trusted certificates
+ self.sslcommonname = None # SSL CommonName to expect
self.sslfingerprint = None # SSL key fingerprint to check
self.properties = None # Extension properties
User.typemap = (
('sslcert', 'String'),
('sslcertck', 'Boolean'),
('sslcertpath', 'String'),
+ ('sslcommonname', 'String'),
('sslfingerprint', 'String'),
('properties', 'String'))
res = res + flag2str(self.sslcertck, 'sslcertck')
if self.sslcertpath and self.sslcertpath != UserDefaults.sslcertpath:
res = res + " sslcertpath " + `self.sslcertpath`
+ if self.sslcommonname and self.sslcommonname != UserDefaults.sslcommonname:
+ res = res + " sslcommonname " + `self.sslcommonname`
if self.sslfingerprint and self.sslfingerprint != UserDefaults.sslfingerprint:
res = res + " sslfingerprint " + `self.sslfingerprint`
if self.expunge != UserDefaults.expunge:
if self.mailboxes:
res = res + " folder"
for x in self.mailboxes:
- res = res + " " + x
+ res = res + ' "%s"' % x
res = res + "\n"
for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'):
if getattr(self, fld):
#
# IANA port assignments and bogus 1109 entry
-ianaservices = {"pop2":109,
- "pop3":110,
+ianaservices = {"pop3":110,
"1109":1109,
"imap":143,
"smtp":25,
# fetchmail protocol to IANA service name
defaultports = {"auto":None,
- "POP2":"pop2",
"POP3":"pop3",
- "APOP":"pop3",
"KPOP":"1109",
"IMAP":"imap",
"ETRN":"smtp",
"ODMR":"odmr"}
authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp",
- "msn", "ntlm")
+ "msn", "ntlm", "apop", "cram-md5")
listboxhelp = {
'title' : 'List Selection Help',
4. A protocol to use (POP, IMAP, ETRN, etc.)
-5. A polling interval.
+5. A poll interval in seconds.
+ If 0, fetchmail will run in the foreground once when started.
+ If > 0, fetchmail will run in the background and start a new poll
+ cycle after the interval has elapsed.
-6. Options to fetch old messages as well as new, uor to suppress
+6. Options to fetch old messages as well as new, or to suppress
deletion of fetched message.
The novice-configuration code will assume that you want to forward mail
postmaster (depending on the "Bounces to sender?" option. Otherwise,
spam bounces are not sent (the default).
+Use soft bounces?
+ If this option is on, permanent delivery errors are treated as
+ temporary, i. e. mail is kept on the upstream server. Useful
+ during testing and after configuration changes, and on by
+ default.
+ If this option is off, permanent delivery errors delete
+ undeliverable mail from the upstream.
+
Invisible
If false (the default) fetchmail generates a Received line into
each message and generates a HELO from the machine it is running on.
sb = Frame(gf)
Checkbutton(sb,
- {'text':'send spam bounces?',
+ {'text':'Send spam bounces?',
'variable':self.spambounce,
'relief':GROOVE}).pack(side=LEFT, anchor=W)
sb.pack(fill=X)
+ sb = Frame(gf)
+ Checkbutton(sb,
+ {'text':'Treat permanent errors as temporary?',
+ 'variable':self.softbounce,
+ 'relief':GROOVE}).pack(side=LEFT, anchor=W)
+ sb.pack(fill=X)
+
sf = Frame(gf)
Checkbutton(sf,
{'text':'Log to syslog?',
If it is off, fetchmail will only query this host when it is given as
a command-line argument.
+The `Retrieve Error Policy' specifies how server errors during
+message retrieval are handled. The default behaviour is to abort the
+current session. Both the continue and markseen options will skip
+the message with the error, but continue the session allowing for
+downloading of subsequent messages. Additionally, the markseen
+option will mark the skipped message as seen.
+
The `True name of server' box should specify the actual DNS name
to query. By default this is the same as the poll name.
supporting Secure Sockets Layer. The sslkey and sslcert options
declare key and certificate files for use with SSL.
The sslcertck option enables strict checking of SSL server
-certificates (and sslcertpath gives trusted certificate
-directory). With sslfingerprint, you can specify a finger-
+certificates (and sslcertpath gives the trusted certificate
+directory). The sslcommonname option helps if the server is
+misconfigured and returning "Server CommonName mismatch"
+warnings. With sslfingerprint, you can specify a finger-
print the server's key is checked against.
"""}
# a custom port number you should be in expert mode and playing
# close enough attention to notice this...
self.service.set(defaultports[proto])
- if not proto in ("POP3", "APOP", "KPOP"): self.uidl.state = DISABLED
def user_edit(self, username, mode):
self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
ctlwin = Frame(leftwin, relief=RAISED, bd=5)
Label(ctlwin, text="Run Controls").pack(side=TOP)
Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
+ Checkbutton(ctlwin, text='Pass messages with bad headers?',
+ variable=self.badheader).pack(side=TOP)
+ retrieveerrorlist = ['abort', 'continue', 'markseen']
+ Label(ctlwin, text="Retrieve Error Policy").pack(side=TOP)
+ ButtonBar(ctlwin, '', self.retrieveerror, retrieveerrorlist, 1, None)
LabeledEntry(ctlwin, 'True name of ' + host + ':',
self.via, leftwidth).pack(side=TOP, fill=X)
LabeledEntry(ctlwin, 'Cycles to skip between polls:',
# Compute the available protocols from the compile-time options
protolist = ['auto']
- if 'pop2' in feature_options:
- protolist.append("POP2")
if 'pop3' in feature_options:
- protolist = protolist + ["POP3", "APOP", "KPOP"]
+ protolist = protolist + ["POP3", "KPOP"]
if 'sdps' in feature_options:
protolist.append("SDPS")
if 'imap' in feature_options:
LabeledEntry(protwin, 'On server TCP/IP service:',
self.service, leftwidth).pack(side=TOP, fill=X)
self.defaultPort()
- Checkbutton(protwin,
- text="POP3: track `seen' with client-side UIDLs?",
- variable=self.uidl).pack(side=TOP)
Button(protwin, text='Probe for supported protocols', fg='blue',
command=self.autoprobe).pack(side=LEFT)
Button(protwin, text='Help', fg='blue',
else:
realhost = self.server.pollname
greetline = None
- for protocol in ("IMAP","POP3","POP2"):
+ for protocol in ("IMAP","POP3"):
service = defaultports[protocol]
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
warnings = ''
# OK, now try to recognize potential problems
- if protocol == "POP2":
- warnings = warnings + """
-It appears you have somehow found a mailserver running only POP2.
-Congratulations. Have you considered a career in archaeology?
-
-Unfortunately, stock fetchmail binaries don't include POP2 support anymore.
-Unless the first line of your fetchmail -V output includes the string "POP2",
-you'll have to build it from sources yourself with the configure
-switch --enable-POP2.
-
-"""
-
### POP3 servers start here
if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
if string.find(greetline, "Domino IMAP4") > 0:
warnings = warnings + """
Your IMAP server appears to be Lotus Domino. This server, at least up
-to version 4.6.2a, has a bug in its generation of MIME boundaries (see
+to version 5.0.2, has a bug in its generation of MIME boundaries (see
the details in the fetchmail FAQ). As a result, even MIME aware MUAs
will see attachments as part of the message text. If your Domino server's
POP3 facility is enabled, we recommend you fall back on it.
password in clear. You should talk to the mailserver administrator about
this.
-"""
- if string.find(greetline, "IMAP2bis") > 0:
- warnings = warnings + """
-IMAP2bis servers have a minor problem; they can't peek at messages without
-marking them seen. If you take a line hit during the retrieval, the
-interrupted message may get left on the server, marked seen.
-
-To work around this, it is recommended that you set the `fetchall'
-option on all user entries associated with this server, so any stuck
-mail will be retrieved next time around.
-
-To fix this bug, upgrade to an IMAP4 server. The fetchmail FAQ includes
-a pointer to an open-source implementation.
-
"""
if string.find(greetline, "IMAP4rev1") > 0:
warnings = warnings + """
variable=self.sslcertck).pack(side=TOP, fill=X)
LabeledEntry(sslwin, 'SSL trusted certificate directory:',
self.sslcertpath, '14').pack(side=TOP, fill=X)
+ LabeledEntry(sslwin, 'SSL CommonName:',
+ self.sslcommonname, '14').pack(side=TOP, fill=X)
LabeledEntry(sslwin, 'SSL key fingerprint:',
self.sslfingerprint, '14').pack(side=TOP, fill=X)
sslwin.pack(fill=X, anchor=N)
Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
ListEdit("Domains:",
self.user.fetchdomains, None, None, targwin, None)
- LabeledEntry(targwin, 'Append to MAIL FROM line:',
+ LabeledEntry(targwin, 'Use domain on RCPT TO line:',
self.smtpaddress, '26').pack(side=TOP, fill=X)
- LabeledEntry(targwin, 'Set RCPT To address:',
+ LabeledEntry(targwin, 'Set fixed RCPT TO address:',
self.smtpname, '26').pack(side=TOP, fill=X)
LabeledEntry(targwin, 'Connection setup command:',
self.preconnect, '26').pack(side=TOP, fill=X)
optional = ('interface', 'monitor',
'esmtpname', 'esmtppassword',
'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
- 'sslcertpath', 'sslfingerprint', 'showdots')
+ 'sslcertpath', 'sslcommonname', 'sslfingerprint', 'showdots')
class_sig = setdiff(toclass.__dict__.keys(), optional)
class_sig.sort()
dict_keys = setdiff(fromdict.keys(), optional)
#
# Process options
- (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV")
+ (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV", ["help",
+ "version"])
dump = rcfile = None;
for (switch, val) in options:
if (switch == '-d'):
dump = TRUE
elif (switch == '-f'):
rcfile = val
- elif (switch == '-h'):
+ elif (switch == '-h' or switch == '--help'):
print """
-Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|-V}
--d - dump configuration (for debugging)
--f fmrc - read alternate fetchmailrc file
--h - print this help text and quit
--V - print fetchmailconf version and quit
+Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|--help|-V|--version}
+ -d - dump configuration (for debugging)
+ -f fmrc - read alternate fetchmailrc file
+--help, -h - print this help text and quit
+--version, -V - print fetchmailconf version and quit
"""
sys.exit(0)
- elif (switch == '-V'):
+ elif (switch == '-V' or switch == '--version'):
print "fetchmailconf %s" % version
+ print """
+Copyright (C) 1997 - 2003 Eric S. Raymond
+Copyright (C) 2005, 2006, 2008, 2009 Matthias Andree
+fetchmailconf comes with ABSOLUTELY NO WARRANTY. This is free software, you are
+welcome to redistribute it under certain conditions. Please see the file
+COPYING in the source or documentation directory for details."""
sys.exit(0)
# Get client host's FQDN