]> Pileus Git - ~andy/fetchmail/blobdiff - fetchmailconf
Bug fixes and internationalization improvements.
[~andy/fetchmail] / fetchmailconf
index 846ed1d7136a1d96a2aacfec9f7b24c9e89aef16..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.13"
+version = "1.28"
 
 from Tkinter import *
 from Dialog import *
@@ -19,6 +19,7 @@ class Configuration:
        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?
@@ -28,6 +29,7 @@ class Configuration:
            ('logfile',         'String'),
            ('idfile',          'String'),
            ('postmaster',      'String'),
+           ('bouncemail',      'Boolean'),
            ('properties',      'String'),
            ('syslog',          'Boolean'),
            ('invisible',       'Boolean'))
@@ -42,6 +44,10 @@ class Configuration:
            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:
@@ -68,7 +74,7 @@ class Server:
        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
@@ -90,7 +96,7 @@ class Server:
            ('protocol',  'String'),
            ('port',      'Int'),
            ('uidl',      'Boolean'),
-           ('auth',      'String'),
+           ('preauth',      'String'),
            ('timeout',   'Int'),
            ('envelope',  'String'),
            ('envskip',   'Int'),
@@ -105,69 +111,69 @@ class Server:
            ('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)):
@@ -190,7 +196,6 @@ class User:
         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
@@ -209,13 +214,18 @@ class User:
        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'),
@@ -238,23 +248,28 @@ class User:
            ('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
@@ -263,60 +278,72 @@ class User:
                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) + "]"
@@ -335,7 +362,7 @@ defaultports = {"auto":0,
                "IMAP-K4":143,
                "ETRN":25}
 
-authlist = ("password", "kerberos")
+preauthlist = ("password", "kerberos", "ssh")
 
 listboxhelp = {
     'title' : 'List Selection Help',
@@ -390,10 +417,18 @@ def helpwin(helpdict):
     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:
@@ -453,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()
@@ -471,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()
@@ -591,6 +630,10 @@ Postmaster
         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.
@@ -628,16 +671,21 @@ class ConfigurationEdit(Frame, MyWidget):
        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')
 
@@ -658,6 +706,7 @@ class ConfigurationEdit(Frame, MyWidget):
             # 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)
@@ -666,6 +715,13 @@ class ConfigurationEdit(Frame, MyWidget):
         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?',
@@ -832,8 +888,12 @@ fetchmail from triggering an expensive dial-out if the
 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,
@@ -885,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()
@@ -909,7 +969,8 @@ class ServerEdit(Frame, MyWidget):
 
     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):
@@ -917,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):
@@ -1006,16 +1067,16 @@ class ServerEdit(Frame, MyWidget):
                 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:
@@ -1028,7 +1089,7 @@ class ServerEdit(Frame, MyWidget):
             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:
@@ -1071,7 +1132,10 @@ you'll have to build it from sources yourself with the configure
 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
@@ -1081,21 +1145,6 @@ It is strongly recommended that you find a better POP3 server.  The fetchmail
 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
@@ -1114,19 +1163,163 @@ user entries associated with this server.
 # 
 # +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 + """
@@ -1134,14 +1327,6 @@ It looks like you could use APOP on this server and avoid sending it your
 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 + """
@@ -1153,16 +1338,21 @@ 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 + """
 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
@@ -1234,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):
@@ -1268,6 +1460,16 @@ class UserEdit(Frame, MyWidget):
                      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: ",
@@ -1325,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':
@@ -1338,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'):
@@ -1371,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;
@@ -1393,6 +1599,7 @@ Or you can just select `Quit' to leave the configurator now and
 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()
@@ -1406,7 +1613,7 @@ return to the main panel.
         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):
@@ -1419,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)
@@ -1461,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,
@@ -1495,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)
 
@@ -1531,7 +1742,7 @@ def copy_instance(toclass, fromdict):
 # 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)
@@ -1579,7 +1790,11 @@ def copy_instance(toclass, fromdict):
 
 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
@@ -1613,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
@@ -1638,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)
@@ -1668,7 +1883,7 @@ gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
     # `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)