# by Eric S. Raymond, <esr@snark.thyrsus.com>.
# Requires Python with Tkinter, and the following OS-dependent services:
# posix, posixpath, socket
-version = "1.13"
+version = "1.28"
from Tkinter import *
from Dialog import *
self.logfile = None # No logfile, initially
self.idfile = os.environ["HOME"] + "/.fetchids" # Default idfile, initially
self.postmaster = None # No last-resort address, initially
+ self.bouncemail = TRUE # Bounce errors to users
self.properties = None # No exiguous properties
self.invisible = FALSE # Suppress Received line & spoof?
self.syslog = FALSE # Use syslogd for logging?
('logfile', 'String'),
('idfile', 'String'),
('postmaster', 'String'),
+ ('bouncemail', 'Boolean'),
('properties', 'String'),
('syslog', 'Boolean'),
('invisible', 'Boolean'))
str = str + ("set idfile \"%s\"\n" % (self.idfile,));
if self.postmaster != ConfigurationDefaults.postmaster:
str = str + ("set postmaster \"%s\"\n" % (self.postmaster,));
+ if self.bouncemail:
+ str = str + ("set bouncemail\n")
+ else:
+ str = str + ("set nobouncemail\n")
if self.properties != ConfigurationDefaults.properties:
str = str + ("set properties \"%s\"\n" % (self.properties,));
if self.poll_interval > 0:
self.protocol = 'auto' # Default to auto protocol
self.port = 0 # Port number to use
self.uidl = FALSE # Don't use RFC1725 UIDLs by default
- self.auth = 'password' # Default to password authentication
+ self.preauth = 'password' # Default to password authentication
self.timeout = 300 # 5-minute timeout
self.envelope = 'Received' # Envelope-address header
self.envskip = 0 # Number of envelope headers to skip
('protocol', 'String'),
('port', 'Int'),
('uidl', 'Boolean'),
- ('auth', 'String'),
+ ('preauth', 'String'),
('timeout', 'Int'),
('envelope', 'String'),
('envskip', 'Int'),
('netsec', 'String'))
def dump(self, folded):
- str = ""
- if self.active: str = str + "poll"
- else: str = str + "skip"
- str = str + (" " + self.pollname)
+ res = ""
+ if self.active: res = res + "poll"
+ else: res = res + "skip"
+ res = res + (" " + self.pollname)
if self.via:
- str = str + (" via \"%s\"\n" % (self.via,));
+ res = res + (" via " + str(self.via) + "\n");
if self.protocol != ServerDefaults.protocol:
- str = str + " with proto " + self.protocol
+ res = res + " with proto " + self.protocol
if self.port != defaultports[self.protocol] and self.port != 0:
- str = str + " port " + `self.port`
+ res = res + " port " + `self.port`
if self.timeout != ServerDefaults.timeout:
- str = str + " timeout " + `self.timeout`
+ res = res + " timeout " + `self.timeout`
if self.interval != ServerDefaults.interval:
- str = str + " interval " + `self.interval`
+ res = res + " interval " + `self.interval`
if self.envelope != ServerDefaults.envelope or self.envskip != ServerDefaults.envskip:
if self.envskip:
- str = str + " envelope " + self.envskip + " " + self.envelope
+ res = res + " envelope " + `self.envskip` + " " + self.envelope
else:
- str = str + " envelope " + self.envelope
+ res = res + " envelope " + self.envelope
if self.qvirtual:
- str = str + (" qvirtual \"%s\"\n" % (self.qvirtual,));
- if self.auth != ServerDefaults.auth:
- str = str + " auth " + self.auth
+ res = res + (" qvirtual " + str(self.qvirtual) + "\n");
+ if self.preauth != ServerDefaults.preauth:
+ res = res + " preauth " + self.preauth
if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl:
- str = str + " and options"
+ res = res + " and options"
if self.dns != ServerDefaults.dns:
- str = str + flag2str(self.dns, 'dns')
+ res = res + flag2str(self.dns, 'dns')
if self.uidl != ServerDefaults.uidl:
- str = str + flag2str(self.uidl, 'uidl')
- if folded: str = str + "\n "
- else: str = str + " "
+ res = res + flag2str(self.uidl, 'uidl')
+ if folded: res = res + "\n "
+ else: res = res + " "
if self.aka:
- str = str + "aka"
+ res = res + "aka"
for x in self.aka:
- str = str + " " + x
- if self.aka and self.localdomains: str = str + " "
+ res = res + " " + x
+ if self.aka and self.localdomains: res = res + " "
if self.localdomains:
- str = str + ("localdomains")
+ res = res + ("localdomains")
for x in self.localdomains:
- str = str + " " + x
+ res = res + " " + x
if (self.aka or self.localdomains):
if folded:
- str = str + "\n "
+ res = res + "\n "
else:
- str = str + " "
+ res = res + " "
if self.interface:
- str = str + "interface \"" + self.interface + "\""
+ res = res + "interface " + str(self.interface)
if self.monitor:
- str = str + "monitor \"" + self.monitor + "\""
+ res = res + "monitor " + str(self.monitor)
if self.netsec:
- str = str + "netsec \"" + self.netsec + "\""
+ res = res + "netsec " + str(self.netsec)
if self.interface or self.monitor or self.netsec:
if folded:
- str = str + "\n"
+ res = res + "\n"
- if str[-1] == " ": str = str[0:-1]
+ if res[-1] == " ": res = res[0:-1]
for user in self.users:
- str = str + repr(user)
- str = str + "\n"
- return str;
+ res = res + repr(user)
+ res = res + "\n"
+ return res;
def __delitem__(self, name):
for ui in range(len(self.users)):
else:
print "Can't get your username!"
sys.exit(1)
- self.remote = os.environ["USER"]# Remote username
self.localnames = [self.remote,]# Local names
self.password = None # Password for mail account access
self.mailboxes = [] # Remote folders to retrieve from
self.forcecr = FALSE # Force LF -> CR/LF
self.stripcr = FALSE # Strip CR
self.pass8bits = FALSE # Force BODY=7BIT
- self.mimedecode = TRUE # Undo MIME armoring
+ self.mimedecode = FALSE # Undo MIME armoring
self.dropstatus = FALSE # Drop incoming Status lines
+ self.dropdelivered = FALSE # Drop incoming Delivered-To lines
+ self.idle = FALSE # IDLE after poll
self.limit = 0 # Message size limit
self.warnings = 0 # Size warning interval
self.fetchlimit = 0 # Max messages fetched per batch
self.batchlimit = 0 # Max message forwarded per batch
- self.expunge = 1 # Interval between expunges (IMAP)
+ self.expunge = 0 # Interval between expunges (IMAP)
+ self.ssl = 0 # Enable Seccure Socket Layer
+ self.sslkey = None # SSL key filename
+ self.sslcert = None # SSL certificate filename
self.properties = None # Extension properties
User.typemap = (
('remote', 'String'),
('pass8bits', 'Boolean'),
('mimedecode', 'Boolean'),
('dropstatus', 'Boolean'),
+ ('dropdelivered', 'Boolean'),
+ ('idle', 'Boolean'),
('limit', 'Int'),
('warnings', 'Int'),
('fetchlimit', 'Int'),
('batchlimit', 'Int'),
('expunge', 'Int'),
- ('properties', 'String'))
+ ('ssl', 'Boolean'),
+ ('sslkey', 'String'),
+ ('sslcert', 'String'),
+ ('properties', 'String'))
def __repr__(self):
- str = " "
- str = str + "user \"" + self.remote + "\" there ";
+ res = " "
+ res = res + "user " + `self.remote` + " there ";
if self.password:
- str = str + "with password \"" + self.password + '" '
+ res = res + "with password " + `self.password` + " "
if self.localnames:
- str = str + "is"
+ res = res + "is"
for x in self.localnames:
- str = str + " " + x
- str = str + " here"
+ res = res + " " + x
+ res = res + " here"
if (self.keep != UserDefaults.keep
or self.flush != UserDefaults.flush
or self.fetchall != UserDefaults.fetchall
or self.stripcr != UserDefaults.stripcr
or self.pass8bits != UserDefaults.pass8bits
or self.mimedecode != UserDefaults.mimedecode
- or self.dropstatus != UserDefaults.dropstatus):
- str = str + " options"
+ or self.dropstatus != UserDefaults.dropstatus
+ or self.dropdelivered != UserDefaults.dropdelivered
+ or self.idle != UserDefaults.idle):
+ res = res + " options"
if self.keep != UserDefaults.keep:
- str = str + flag2str(self.keep, 'keep')
+ res = res + flag2str(self.keep, 'keep')
if self.flush != UserDefaults.flush:
- str = str + flag2str(self.flush, 'flush')
+ res = res + flag2str(self.flush, 'flush')
if self.fetchall != UserDefaults.fetchall:
- str = str + flag2str(self.fetchall, 'fetchall')
+ res = res + flag2str(self.fetchall, 'fetchall')
if self.rewrite != UserDefaults.rewrite:
- str = str + flag2str(self.rewrite, 'rewrite')
+ res = res + flag2str(self.rewrite, 'rewrite')
if self.forcecr != UserDefaults.forcecr:
- str = str + flag2str(self.forcecr, 'forcecr')
+ res = res + flag2str(self.forcecr, 'forcecr')
if self.stripcr != UserDefaults.stripcr:
- str = str + flag2str(self.stripcr, 'stripcr')
+ res = res + flag2str(self.stripcr, 'stripcr')
if self.pass8bits != UserDefaults.pass8bits:
- str = str + flag2str(self.pass8bits, 'pass8bits')
+ res = res + flag2str(self.pass8bits, 'pass8bits')
if self.mimedecode != UserDefaults.mimedecode:
- str = str + flag2str(self.mimedecode, 'mimedecode')
+ res = res + flag2str(self.mimedecode, 'mimedecode')
if self.dropstatus != UserDefaults.dropstatus:
- str = str + flag2str(self.dropstatus, 'dropstatus')
+ res = res + flag2str(self.dropstatus, 'dropstatus')
+ if self.dropdelivered != UserDefaults.dropdelivered:
+ res = res + flag2str(self.dropdelivered, 'dropdelivered')
+ if self.idle != UserDefaults.idle:
+ res = res + flag2str(self.idle, 'idle')
if self.limit != UserDefaults.limit:
- str = str + " limit " + `self.limit`
+ res = res + " limit " + `self.limit`
if self.warnings != UserDefaults.warnings:
- str = str + " warnings " + `self.warnings`
+ res = res + " warnings " + `self.warnings`
if self.fetchlimit != UserDefaults.fetchlimit:
- str = str + " fetchlimit " + `self.fetchlimit`
+ res = res + " fetchlimit " + `self.fetchlimit`
if self.batchlimit != UserDefaults.batchlimit:
- str = str + " batchlimit " + `self.batchlimit`
+ res = res + " batchlimit " + `self.batchlimit`
+ if self.ssl and self.ssl != UserDefaults.ssl:
+ res = res + flag2str(self.ssl, 'ssl')
+ if self.sslkey and self.sslkey != UserDefaults.sslkey:
+ res = res + " sslkey " + `self.sslkey`
+ if self.sslcert and self.sslcert != UserDefaults.sslcert:
+ res = res + " sslcert " + `self.sslcert`
if self.expunge != UserDefaults.expunge:
- str = str + " expunge " + `self.expunge`
- str = str + "\n"
+ res = res + " expunge " + `self.expunge`
+ res = res + "\n"
trimmed = self.smtphunt;
if trimmed != [] and trimmed[len(trimmed) - 1] == "localhost":
trimmed = trimmed[0:len(trimmed) - 1]
if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
trimmed = trimmed[0:len(trimmed) - 1]
if trimmed != []:
- str = str + " smtphost "
+ res = res + " smtphost "
for x in trimmed:
- str = str + " " + x
- str = str + "\n"
+ res = res + " " + x
+ res = res + "\n"
if self.mailboxes:
- str = str + " folder"
+ res = res + " folder"
for x in self.mailboxes:
- str = str + " " + x
- str = str + "\n"
+ res = res + " " + x
+ res = res + "\n"
for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'):
if getattr(self, fld):
- str = str + " %s %s\n" % (fld, `getattr(self, fld)`)
+ res = res + " %s %s\n" % (fld, `getattr(self, fld)`)
if self.lmtp != UserDefaults.lmtp:
- str = str + flag2str(self.lmtp, 'lmtp')
+ res = res + flag2str(self.lmtp, 'lmtp')
if self.antispam != UserDefaults.antispam:
- str = str + " antispam " + self.antispam + "\n"
- return str;
+ res = res + " antispam " + self.antispam + "\n"
+ return res;
def __str__(self):
return "[User: " + repr(self) + "]"
"IMAP-K4":143,
"ETRN":25}
-authlist = ("password", "kerberos")
+preauthlist = ("password", "kerberos", "ssh")
listboxhelp = {
'title' : 'List Selection Help',
helpwin.title(helpdict['title'])
helpwin.iconname(helpdict['title'])
Label(helpwin, text=helpdict['banner']).pack()
- textwin = Message(helpwin, text=helpdict['text'], width=600)
- textwin.pack()
+ textframe = Frame(helpwin)
+ scroll = Scrollbar(textframe)
+ helpwin.textwidget = Text(textframe, setgrid=TRUE)
+ textframe.pack(side=TOP, expand=YES, fill=BOTH)
+ helpwin.textwidget.config(yscrollcommand=scroll.set)
+ helpwin.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
+ scroll.config(command=helpwin.textwidget.yview)
+ scroll.pack(side=RIGHT, fill=BOTH)
+ helpwin.textwidget.insert(END, helpdict['text']);
Button(helpwin, text='Done',
command=lambda x=helpwin: Widget.destroy(x), bd=2).pack()
+ textframe.pack(side=TOP)
def make_icon_window(base, image):
try:
helpwin(self.helptxt)
def handleList(self, event):
- self.editItem();
+ self.editItem();
def handleNew(self, event):
item = self.newval.get()
- entire = self.listwidget.get(0, self.listwidget.index('end'));
- if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
- self.listwidget.insert('end', item)
- if self.list != None: self.list.append(item)
- self.newval.set('')
+ if item:
+ entire = self.listwidget.get(0, self.listwidget.index('end'));
+ if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
+ self.listwidget.insert('end', item)
+ if self.list != None: self.list.append(item)
+ if self.editor:
+ apply(self.editor, (item,))
+ self.newval.set('')
def editItem(self):
select = self.listwidget.curselection()
index = select[0]
if index and self.editor:
label = self.listwidget.get(index);
- apply(self.editor, (label,))
+ if self.editor:
+ apply(self.editor, (label,))
def deleteItem(self):
select = self.listwidget.curselection()
invoking user as the address of last resort unless that user is
root. If that user is root, fetchmail sends to `postmaster'.
+Bounces to sender?
+ If this option is on (the default) error mail goes to the sender.
+ Otherwise it goes to the postmaster.
+
Invisible
If false (the default) fetchmail generates a Received line into
each message and generates a HELO from the machine it is running on.
self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
def server_delete(self, sitename):
- if self.configuration.has_keys(sitename):
+ try:
+ for user in self.subwidgets.keys():
+ user.destruct()
del self.configuration[sitename]
+ except:
+ pass
def edit(self, mode):
self.mode = mode
Frame.__init__(self, self.container)
self.master.title('fetchmail ' + self.mode + ' configurator');
self.master.iconname('fetchmail ' + self.mode + ' configurator');
+ self.master.protocol('WM_DELETE_WINDOW', self.nosave)
self.keepalive = [] # Use this to anchor the PhotoImage object
- make_icon_window(self, fetchmail_gif)
+ make_icon_window(self, fetchmail_icon)
Pack.config(self)
self.post(Configuration, 'configuration')
# Set the postmaster
log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
log.pack(side=RIGHT, anchor=E)
+
# Set the poll interval
de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
de.pack(side=RIGHT, anchor=E)
df.pack()
if self.mode != 'novice':
+ pf = Frame(gf)
+ Checkbutton(pf,
+ {'text':'Bounces to sender?',
+ 'variable':self.bouncemail,
+ 'relief':GROOVE}).pack(side=LEFT, anchor=W)
+ pf.pack(fill=X)
+
sf = Frame(gf)
Checkbutton(sf,
{'text':'Log to syslog?',
interface is not already active.
The `interface' and `monitor' options are available
-only for Linux systems. See the fetchmail manual page
-for details on these.
+only for Linux and freeBSD systems. See the fetchmail
+manual page for details on these.
+
+The ssl option enables SSL communication with a mailserver
+supporting Secure Sockets Layer. The sslkey and sslcert options
+declare key and certificate files for use with SSL.
The `netsec' option will be configurable only if fetchmail
was compiled with IPV6 support. If you need to use it,
self.post(Server, 'server')
self.makeWidgets(self.server.pollname, mode)
self.keepalive = [] # Use this to anchor the PhotoImage object
- make_icon_window(self, fetchmail_gif)
+ make_icon_window(self, fetchmail_icon)
# self.grab_set()
# self.focus_set()
# self.wait_window()
def refreshPort(self):
proto = self.protocol.get()
- self.port.set(defaultports[proto])
+ if self.port.get() == 0:
+ self.port.set(defaultports[proto])
if not proto in ("POP3", "APOP", "KPOP"): self.uidl.state = DISABLED
def user_edit(self, username, mode):
def user_delete(self, username):
if self.subwidgets.has_key(username):
- del self.subwidgets[username]
+ self.subwidgets[username].destruct()
del self.server[username]
def makeWidgets(self, host, mode):
self.server.localdomains, None, None, mdropwin, multihelp)
mdropwin.pack(fill=X)
- if os_type == 'linux' or 'netsec' in feature_options:
+ if os_type == 'linux' or os_type == 'freebsd' or 'netsec' in feature_options:
secwin = Frame(rightwin, relief=RAISED, bd=5)
Label(secwin, text="Security").pack(side=TOP)
# Don't actually let users set this. KPOP sets it implicitly
- # ButtonBar(secwin, 'Authorization mode:',
- # self.auth, authlist, 1, None).pack(side=TOP)
- if os_type == 'linux' or 'interface' in dictmembers:
+ # ButtonBar(secwin, 'Preauthorization mode:',
+ # self.preauth, preauthlist, 1, None).pack(side=TOP)
+ if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
LabeledEntry(secwin, 'IP range to check before poll:',
self.interface, leftwidth).pack(side=TOP, fill=X)
- if os_type == 'linux' or 'monitor' in dictmembers:
+ if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
LabeledEntry(secwin, 'Interface to monitor:',
self.monitor, leftwidth).pack(side=TOP, fill=X)
if 'netsec' in feature_options or 'netsec' in dictmembers:
rightwin.pack(side=LEFT, anchor=N);
def autoprobe(self):
- # Note: this only handles case (1) near fetchmail.c:892
+ # Note: this only handles case (1) near fetchmail.c:1032
# We're assuming people smart enough to set up ssh tunneling
# won't need autoprobing.
if self.server.via:
switch --enable-POP2.
"""
- if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
+
+### POP3 servers start here
+
+ if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
warnings = warnings + """
This appears to be an old version of the UC Davis POP server. These are
dangerously unreliable (among other problems, they may drop your mailbox
FAQ includes pointers to good ones.
"""
- if string.find(greetline, "usa.net") > 0:
- warnings = warnings + """
-You appear to be using USA.NET's free mail service. Their POP3 servers
-(at least as of the 2.2 version in use mid-1998) are quite flaky, but
-fetchmail can compensate. They seem to require that fetchall be switched on
-(otherwise you won't necessarily see all your mail, not even new mail).
-They also botch the TOP command the fetchmail normally uses for retrieval
-(it only retrieves about 10 lines rather than the number specified).
-Turning on fetchall will disable the use of TOP.
-
-Therefore, it is strongly recommended that you turn on `fetchall' on all
-user entries associated with this server.
-
-"""
-
# Steve VanDevender <stevev@efn.org> writes:
# The only system I have seen this happen with is cucipop-1.31
# under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
#
# +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
#
- if string.find(greetline, "Cubic Circle") > 0:
+ if string.find(greetline, "Cubic Circle") > 0:
warnings = warnings + """
I see your server is running cucipop. Better make sure the server box
isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
under that version, and doesn't cope with the result gracefully. Newer
SunOS and Solaris machines run cucipop OK.
+
"""
- if string.find(greetline, "QPOP") > 0:
+ if string.find(greetline, "David POP3 Server") > 0:
+ warnings = warnings + """
+This POP3 serrver is badly broken. You should get rid of it -- and the
+brain-dead NT operating system it rode in on.
+
+"""
+# The greeting line on the server known to be buggy is:
+# +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
+#
+ if string.find(greetline, "FTGate") > 0:
+ warnings = warnings + """
+This POP server has a weird bug; it says OK twice in response to TOP.
+Its response to RETR is normal, so use the `fetchall' option.
+
+"""
+ if string.find(greetline, "OpenMail") > 0:
+ warnings = warnings + """
+You appear to be using some version of HP OpenMail. Many versions of
+OpenMail do not process the "TOP" command correctly; the symptom is that
+only the header and first line of each message is retrieved. To work
+around this bug, turn on `fetchall' on all user entries associated with
+this server.
+
+"""
+ if string.find(greetline, "POP-Max") > 0:
+ warnings = warnings + """
+The Mail Max POP3 server screws up on mail with attachments. It
+reports the message size with attachments included, but doesn't
+download them on a RETR or TOP (this violates the IMAP RFCs). It also
+doesn't implement TOP correctly. You should get rid of it -- and the
+brain-dead NT server it rode in on.
+
+"""
+ if string.find(greetline, "POP3 Server Ready") > 0:
+ warnings = warnings + """
+Some server that uses this greeting line has been observed to choke on
+TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
+
+"""
+ if string.find(greetline, "QPOP") > 0:
warnings = warnings + """
This appears to be a version of Eudora qpopper. That's good. Fetchmail
-knows all about qpopper.
+knows all about qpopper. However, be aware that the 2.53 version of
+qpopper does something odd that causes fetchmail to hang with a socket
+error on very large messages. This is probably not a fetchmail bug, as
+it has been observed with fetchpop. The fix is to upgrade to qpopper
+3.0beta or a more recent version. Better yet, switch to IMAP.
+
+"""
+ if string.find(greetline, " sprynet.com") > 0:
+ warnings = warnings + """
+You appear to be using a SpryNet server. In mid-1999 it was reported that
+the SpryNet TOP command marks messages seen. Therefore, for proper error
+recovery in the event of a line drop, it is strongly recommended that you
+turn on `fetchall' on all user entries associated with this server.
+
+"""
+ if string.find(greetline, "TEMS POP3") > 0:
+ warnings = warnings + """
+Your POP3 server has "TEMS" in its header line. At least one such
+server does not process the "TOP" command correctly; the symptom is
+that fetchmail hangs when trying to retrieve mail. To work around
+this bug, turn on `fetchall' on all user entries associated with this
+server.
"""
+ if string.find(greetline, " spray.se") > 0:
+ warnings = warnings + """
+Your POP3 server has "spray.se" in its header line. In May 2000 at
+least one such server did not process the "TOP" command correctly; the
+symptom is that messages are treated as headerless. To work around
+this bug, turn on `fetchall' on all user entries associated with this
+server.
+
+"""
+ if string.find(greetline, " usa.net") > 0:
+ warnings = warnings + """
+You appear to be using USA.NET's free mail service. Their POP3 servers
+(at least as of the 2.2 version in use mid-1998) are quite flaky, but
+fetchmail can compensate. They seem to require that fetchall be switched on
+(otherwise you won't necessarily see all your mail, not even new mail).
+They also botch the TOP command the fetchmail normally uses for retrieval
+(it only retrieves about 10 lines rather than the number specified).
+Turning on fetchall will disable the use of TOP.
+
+Therefore, it is strongly recommended that you turn on `fetchall' on all
+user entries associated with this server.
+
+"""
+ if string.find(greetline, " Novonyx POP3") > 0:
+ warnings = warnings + """
+Your mailserver is running Novonyx POP3. This server, at least as of
+version 2.17, seems to have problems handling and reporting seen bits.
+You may have to use the fetchall option.
+
+"""
+
+### IMAP servers start here
+
+ if string.find(greetline, "GroupWise") > 0:
+ warnings = warnings + """
+The Novell GroupWise IMAP server would be better named GroupFoolish;
+it is (according to the designer of IMAP) unusably broken. Among
+other things, it doesn't include a required content length in its
+BODY[TEXT] response.<p>
+
+Fetchmail works around this problem, but we strongly recommend voting
+with your dollars for a server that isn't brain-dead. If you stick
+with code as shoddy as GroupWise seems to be, you will probably pay
+for it with other problems.<p>
+
+"""
+ if string.find(greetline, "Netscape IMAP4rev1 Service 3.6") > 0:
+ warnings = warnings + """
+This server violates the RFC2060 requirement that a BODY[TEXT] fetch should
+set the messages's Seen flag. As a result, if you use the keep option the
+same messages will be downloaded over and over.
+
+"""
+ if string.find(greetline, "InterChange") > 0:
+ warnings = warnings + """
+The InterChange IMAP server screws up on mail with attachments. It
+doesn't fetch them if you give it a BODY[TEXT] request, though it
+does if you request RFC822.TEXT. According to the IMAP RFCs and their
+maintainer these should be equivalent -- and we can't drop the
+BODY[TEXT] form because M$ Exchange (quite legally under RFC2062)
+rejects it.
+
+"""
+ if string.find(greetline, "Imail") > 0:
+ warnings = warnings + """
+We've seen a bug report indicating that this IMAP server (at least as of
+version 5.0.7) returns an invalid body size for messages with MIME
+attachments; the effect is to drop the attachments on the floor. We
+recommend you upgrade to a non-broken IMAP server.
+
+"""
+ 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
+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.
+
+"""
+
+### Checks for protocol variants start here
+
closebrak = string.find(greetline, ">")
if closebrak > 0 and greetline[closebrak+1] == "\r":
warnings = warnings + """
password in clear. You should talk to the mailserver administrator about
this.
-"""
- if string.find(greetline, "csi.com") > 0:
- warnings = warnings + """
-It appears you're talking to CompuServe. You can use their special RPA
-service for authentication, but only if your fetchmail -V output's first
-line contains the string "RPA". This is not included in stock fetchmail
-binaries; to compile it in, rebuild from sources with the configure
-option --enable-RPA.
"""
if string.find(greetline, "IMAP2bis") > 0:
warnings = warnings + """
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 + """
I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
has therefore been extremely well tested with this class of server.
+
"""
if warnings == '':
warnings = warnings + """
Fetchmail doesn't know anything special about this server type.
+
"""
# Display success window with warnings
self.post(User, 'user')
self.makeWidgets(mode, self.parent.server.pollname)
self.keepalive = [] # Use this to anchor the PhotoImage object
- make_icon_window(self, fetchmail_gif)
+ make_icon_window(self, fetchmail_icon)
# self.grab_set()
# self.focus_set()
# self.wait_window()
return self
def destruct(self):
- del self.parent.subwidgets[self.user.remote]
+ # Yes, this test can fail -- if you delete the parent window.
+ if self.parent.subwidgets.has_key(self.user.remote):
+ del self.parent.subwidgets[self.user.remote]
Widget.destroy(self.master)
def nosave(self):
self.password, '12').pack(side=TOP, fill=X)
secwin.pack(fill=X, anchor=N)
+ if 'ssl' in feature_options or 'ssl' in dictmembers:
+ sslwin = Frame(leftwin, relief=RAISED, bd=5)
+ Checkbutton(sslwin, text="Use SSL?",
+ variable=self.ssl).pack(side=TOP, fill=X)
+ LabeledEntry(sslwin, 'SSL key:',
+ self.sslkey, '14').pack(side=TOP, fill=X)
+ LabeledEntry(sslwin, 'SSL certificate:',
+ self.sslcert, '14').pack(side=TOP, fill=X)
+ sslwin.pack(fill=X, anchor=N)
+
names = Frame(leftwin, relief=RAISED, bd=5)
Label(names, text="Local names").pack(side=TOP)
ListEdit("New name: ",
variable=self.mimedecode).pack(side=TOP, anchor=W)
Checkbutton(optwin, text="Drop Status lines from forwarded messages",
variable=self.dropstatus).pack(side=TOP, anchor=W)
+ Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
+ variable=self.dropdelivered).pack(side=TOP, anchor=W)
optwin.pack(fill=X)
if mode != 'novice':
self.fetchlimit, '30').pack(side=TOP, fill=X)
LabeledEntry(limwin, 'Max messages to forward per poll:',
self.batchlimit, '30').pack(side=TOP, fill=X)
- if self.parent.server.protocol in ('IMAP', 'IMAP-K4', 'IMAP-GSS'):
- LabeledEntry(limwin, 'Interval between expunges (IMAP):',
+ if self.parent.server.protocol != 'ETRN':
+ LabeledEntry(limwin, 'Interval between expunges:',
self.expunge, '30').pack(side=TOP, fill=X)
+ Checkbutton(limwin, text="Idle after each poll (IMAP only)",
+ variable=self.idle).pack(side=TOP, anchor=W)
limwin.pack(fill=X)
if self.parent.server.protocol in ('IMAP', 'IMAP-K4', 'IMAP-GSS'):
self.master.iconname('fetchmail configurator');
Pack.config(self)
self.keepalive = [] # Use this to anchor the PhotoImage object
- make_icon_window(self, fetchmail_gif)
+ make_icon_window(self, fetchmail_icon)
Message(self, text="""
Use `Novice Configuration' for basic fetchmail setup;
return to the main panel.
""", width=600).pack(side=TOP)
Button(self, text='Quit', fg='blue', command=self.leave).pack()
+ master.protocol("WM_DELETE_WINDOW", self.leave)
def novice(self):
self.master.destroy()
self.master.destroy()
self.onexit()
-# Run a command an a scrolling text widget, displaying its output
+# Run a command in a scrolling text widget, displaying its output
class RunWindow(Frame):
def __init__(self, command, master, parent):
text="Running "+command,
bd=2).pack(side=TOP, pady=10)
self.keepalive = [] # Use this to anchor the PhotoImage object
- make_icon_window(self, fetchmail_gif)
+ make_icon_window(self, fetchmail_icon)
# This is a scrolling text window
textframe = Frame(self)
text='Fetchmailconf ' + version,
bd=2).pack(side=TOP, pady=10)
self.keepalive = [] # Use this to anchor the PhotoImage object
- make_icon_window(self, fetchmail_gif)
+ make_icon_window(self, fetchmail_icon)
self.debug = 0
+ ## Test icon display with the following:
+ # icon_image = PhotoImage(data=fetchmail_icon)
+ # Label(self, image=icon_image).pack(side=TOP, pady=10)
+ # self.keepalive.append(icon_image)
+
Message(self, text="""
Use `Configure fetchmail' to tell fetchmail about the remote
servers it should poll (the host name, your username there,
Configurator(self.outfile, Toplevel(),
lambda self=self: self.configbutton.configure(state=NORMAL),
self)
-
def test(self):
RunWindow("fetchmail -d0 -v --nosyslog", Toplevel(), self)
# The `optional' fields are the ones we can ignore for purposes of
# conformability checking; they'll still get copied if they are
# present in the dictionary.
- optional = ('interface', 'monitor', 'netsec')
+ optional = ('interface', 'monitor', 'netsec', 'ssl', 'sslkey', 'sslcert')
class_sig = setdiff(toclass.__dict__.keys(), optional)
class_sig.sort()
dict_keys = setdiff(fromdict.keys(), optional)
if __name__ == '__main__':
- fetchmail_gif = """
+ if not os.environ.has_key("DISPLAY"):
+ print "fetchmailconf must be run under X"
+ sys.exit(1)
+
+ fetchmail_icon = """
R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
"""
-# Note on making icons: the above was generated by the following procedure:
+# The base64 data in the string above was generated by the following procedure:
#
# import base64
-# data = open("fetchmail.gif", "rb").read()
-# print "fetchmail_gif =\\"
-# print repr(base64.encodestring(data))
+# print base64.encodestring(open("fetchmail.gif", "rb").read())
#
# Process options
ServerDefaults = Server()
UserDefaults = User()
- # Read the existing configuration
+ # Read the existing configuration. We set the umask to 077 to make sure
+ # that group & other read/write permissions are shut off -- we wouldn't
+ # want crackers to snoop password information out of the tempfile.
tmpfile = "/tmp/fetchmailconf." + `os.getpid()`
if rcfile:
- cmd = "fetchmail -f " + rcfile + " --configdump --nosyslog >" + tmpfile
+ cmd = "umask 077; fetchmail -f " + rcfile + " --configdump --nosyslog >" + tmpfile
else:
- cmd = "fetchmail --configdump --nosyslog >" + tmpfile
+ cmd = "umask 077; fetchmail --configdump --nosyslog >" + tmpfile
try:
s = os.system(cmd)
# `Configuration' is the top level of the object tree we're going to mung.
# The dictmembers list is used to track the set of fields the dictionary
# contains; in particular, we can use it to tell whether things like the
- # monitor, interface, and netsec fields are present.
+ # monitor, interface, netsec, ssl, sslkey, or sslcert fields are present.
dictmembers = []
Fetchmailrc = Configuration()
copy_instance(Fetchmailrc, fetchmailrc)