# by Eric S. Raymond, <esr@snark.thyrsus.com>.
# Requires Python with Tkinter, and the following OS-dependent services:
# posix, posixpath, socket
-version = "1.22"
+version = "1.28"
from Tkinter import *
from Dialog import *
res = res + " interval " + `self.interval`
if self.envelope != ServerDefaults.envelope or self.envskip != ServerDefaults.envskip:
if self.envskip:
- res = res + " envelope " + self.envskip + " " + self.envelope
+ res = res + " envelope " + `self.envskip` + " " + self.envelope
else:
res = res + " envelope " + self.envelope
if self.qvirtual:
self.pass8bits = FALSE # Force BODY=7BIT
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
('pass8bits', 'Boolean'),
('mimedecode', 'Boolean'),
('dropstatus', 'Boolean'),
+ ('dropdelivered', 'Boolean'),
+ ('idle', 'Boolean'),
('limit', 'Int'),
('warnings', 'Int'),
('fetchlimit', 'Int'),
def __repr__(self):
res = " "
- res = res + "user " + str(self.remote) + " there ";
+ res = res + "user " + `self.remote` + " there ";
if self.password:
- res = res + "with password " + str(self.password) + " "
+ res = res + "with password " + `self.password` + " "
if self.localnames:
res = res + "is"
for x in self.localnames:
or self.stripcr != UserDefaults.stripcr
or self.pass8bits != UserDefaults.pass8bits
or self.mimedecode != UserDefaults.mimedecode
- or self.dropstatus != UserDefaults.dropstatus):
+ or self.dropstatus != UserDefaults.dropstatus
+ or self.dropdelivered != UserDefaults.dropdelivered
+ or self.idle != UserDefaults.idle):
res = res + " options"
if self.keep != UserDefaults.keep:
res = res + flag2str(self.keep, 'keep')
res = res + flag2str(self.mimedecode, 'mimedecode')
if self.dropstatus != UserDefaults.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:
res = res + " limit " + `self.limit`
if self.warnings != UserDefaults.warnings:
if self.sslkey and self.sslkey != UserDefaults.sslkey:
res = res + " sslkey " + `self.sslkey`
if self.sslcert and self.sslcert != UserDefaults.sslcert:
- res = res + " ssl " + `self.sslcert`
+ res = res + " sslcert " + `self.sslcert`
if self.expunge != UserDefaults.expunge:
res = res + " expunge " + `self.expunge`
res = res + "\n"
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()
def server_delete(self, sitename):
try:
+ for user in self.subwidgets.keys():
+ user.destruct()
del self.configuration[sitename]
except:
pass
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')
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 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):
under that version, and doesn't cope with the result gracefully. Newer
SunOS and Solaris machines run cucipop OK.
+"""
+ 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, "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). You should get rid of it --
-and the brain-dead NT server it rode in on.
+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:
3.0beta or a more recent version. Better yet, switch to IMAP.
"""
- if string.find(greetline, "sprynet.com") > 0:
+ 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
server.
"""
- if string.find(greetline, "usa.net") > 0:
+ 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
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, "InterChange") > 0:
warnings = warnings + """
-The InterChange IMAP 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). You should get rid of it.
+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:
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
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):
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;
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)
print "fetchmailconf must be run under X"
sys.exit(1)
- fetchmail_gif = """
+ 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)