]> Pileus Git - ~andy/fetchmail/blobdiff - fetchmailconf
Bug fixes and internationalization improvements.
[~andy/fetchmail] / fetchmailconf
index 88804ccd61a97510c6d7928b348d209dc502dead..15ccc2fbf02234e060d0a15ecbd43983ae4d6a71 100755 (executable)
@@ -4,7 +4,7 @@
 # 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 *
@@ -127,7 +127,7 @@ class Server:
            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:
@@ -216,6 +216,8 @@ class User:
        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
@@ -246,6 +248,8 @@ class User:
            ('pass8bits',   'Boolean'),
            ('mimedecode',  'Boolean'),
            ('dropstatus',  'Boolean'),
+            ('dropdelivered', 'Boolean'),
+           ('idle',        'Boolean'),
            ('limit',       'Int'),
            ('warnings',    'Int'),
            ('fetchlimit',  'Int'),
@@ -258,9 +262,9 @@ class User:
 
     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:
@@ -274,7 +278,9 @@ class User:
                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')
@@ -294,6 +300,10 @@ class User:
            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:
@@ -307,7 +317,7 @@ class User:
        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"
@@ -478,15 +488,18 @@ class ListEdit(Frame):
        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()
@@ -496,7 +509,8 @@ class ListEdit(Frame):
            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()
@@ -658,6 +672,8 @@ class ConfigurationEdit(Frame, MyWidget):
 
     def server_delete(self, sitename):
         try:
+            for user in self.subwidgets.keys():
+                user.destruct()
             del self.configuration[sitename]
         except:
            pass
@@ -669,7 +685,7 @@ class ConfigurationEdit(Frame, MyWidget):
        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')
 
@@ -929,7 +945,7 @@ class ServerEdit(Frame, MyWidget):
        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()
@@ -962,7 +978,7 @@ class ServerEdit(Frame, MyWidget):
 
     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):
@@ -1154,6 +1170,12 @@ 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, "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)
@@ -1175,10 +1197,11 @@ 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).  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:
@@ -1197,7 +1220,7 @@ 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:
+            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
@@ -1214,7 +1237,16 @@ this bug, turn on `fetchall' on all user entries associated with this
 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
@@ -1227,6 +1259,13 @@ 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
@@ -1253,9 +1292,12 @@ 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 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:
@@ -1265,6 +1307,15 @@ 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
@@ -1373,14 +1424,16 @@ class UserEdit(Frame, MyWidget):
        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):
@@ -1474,6 +1527,8 @@ class UserEdit(Frame, MyWidget):
                    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':
@@ -1487,9 +1542,11 @@ class UserEdit(Frame, MyWidget):
                      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'):
@@ -1520,7 +1577,7 @@ class Configurator(Frame):
        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;
@@ -1569,7 +1626,7 @@ class RunWindow(Frame):
                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)
@@ -1611,9 +1668,14 @@ class MainWindow(Frame):
                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,
@@ -1645,7 +1707,6 @@ Or you can just select `Quit' to exit the launcher now.
         Configurator(self.outfile, Toplevel(),
                      lambda self=self: self.configbutton.configure(state=NORMAL),
                      self)
-
     def test(self):
        RunWindow("fetchmail -d0 -v --nosyslog", Toplevel(), self)
 
@@ -1733,7 +1794,7 @@ if __name__ == '__main__':
         print "fetchmailconf must be run under X"
         sys.exit(1)
 
-    fetchmail_gif = """
+    fetchmail_icon = """
 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
@@ -1767,12 +1828,10 @@ OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
 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
@@ -1792,12 +1851,14 @@ gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
     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)