X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=fetchmailconf.py;h=b4b0a1ae7facd4431a39b776e39cefb071b9ab5e;hb=87bcf29364c4640edb87cc2186b965d1a564d70c;hp=ce493627e1df0d6100b179fde5b27dcab10a1133;hpb=aab18800bcf364ee5d3f34f96fb88ceec2b50bb9;p=~andy%2Ffetchmail diff --git a/fetchmailconf.py b/fetchmailconf.py index ce493627..b4b0a1ae 100755 --- a/fetchmailconf.py +++ b/fetchmailconf.py @@ -5,7 +5,7 @@ # Matthias Andree # Requires Python with Tkinter, and the following OS-dependent services: # posix, posixpath, socket -version = "1.46" +version = "1.58" from Tkinter import * from Dialog import * @@ -22,6 +22,7 @@ class Configuration: self.postmaster = None # No last-resort address, initially self.bouncemail = TRUE # Bounce errors to users self.spambounce = FALSE # Bounce spam errors + self.softbounce = TRUE # Treat permanent error as temporary self.properties = None # No exiguous properties self.invisible = FALSE # Suppress Received line & spoof? self.syslog = FALSE # Use syslogd for logging? @@ -33,6 +34,7 @@ class Configuration: ('postmaster', 'String'), ('bouncemail', 'Boolean'), ('spambounce', 'Boolean'), + ('softbounce', 'Boolean'), ('properties', 'String'), ('syslog', 'Boolean'), ('invisible', 'Boolean')) @@ -55,10 +57,16 @@ class Configuration: str = str + ("set spambounce\n") else: str = str + ("set no spambounce\n") + if self.softbounce: + str = str + ("set softbounce\n") + else: + str = str + ("set no softbounce\n") if self.properties != ConfigurationDefaults.properties: str = str + ("set properties \"%s\"\n" % (self.properties,)); if self.poll_interval > 0: str = str + "set daemon " + `self.poll_interval` + "\n" + if self.invisible: + str = str + ("set invisible\n") for site in self.servers: str = str + repr(site) return str @@ -80,7 +88,6 @@ class Server: self.interval = 0 # Skip interval self.protocol = 'auto' # Default to auto protocol self.service = None # Service name to use - self.uidl = FALSE # Don't use RFC1725 UIDLs by default self.auth = 'any' # Default to password authentication self.timeout = 300 # 5-minute timeout self.envelope = 'Received' # Envelope-address header @@ -97,6 +104,8 @@ class Server: self.esmtpname = None # ESMTP 2554 name self.esmtppassword = None # ESMTP 2554 password self.tracepolls = FALSE # Add trace-poll info to headers + self.badheader = FALSE # Pass messages with bad headers on? + self.retrieveerror = 'abort' # Policy when message retrieval errors encountered self.users = [] # List of user entries for site Server.typemap = ( ('pollname', 'String'), @@ -105,7 +114,6 @@ class Server: ('interval', 'Int'), ('protocol', 'String'), ('service', 'String'), - ('uidl', 'Boolean'), ('auth', 'String'), ('timeout', 'Int'), ('envelope', 'String'), @@ -121,7 +129,9 @@ class Server: ('esmtpname', 'String'), ('esmtppassword', 'String'), ('principal', 'String'), - ('tracepolls','Boolean')) + ('tracepolls','Boolean'), + ('badheader', 'Boolean'), + ('retrieveerror', 'String')) def dump(self, folded): res = "" @@ -132,7 +142,7 @@ class Server: res = res + (" via " + str(self.via) + "\n"); if self.protocol != ServerDefaults.protocol: res = res + " with proto " + self.protocol - if self.service and self.service != defaultports[self.protocol] and self.service != ianaservices[defaultports[self.protocol]]: + if self.service and self.protocol and self.service != defaultports[self.protocol] and defaultports[self.protocol] and self.service != ianaservices[defaultports[self.protocol]]: res = res + " service " + self.service if self.timeout != ServerDefaults.timeout: res = res + " timeout " + `self.timeout` @@ -147,12 +157,9 @@ class Server: res = res + (" qvirtual " + str(self.qvirtual) + "\n"); if self.auth != ServerDefaults.auth: res = res + " auth " + self.auth - if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl: - res = res + " and options" if self.dns != ServerDefaults.dns: + res = res + " and options" res = res + flag2str(self.dns, 'dns') - if self.uidl != ServerDefaults.uidl: - res = res + flag2str(self.uidl, 'uidl') if folded: res = res + "\n " else: res = res + " " @@ -189,6 +196,16 @@ class Server: if self.esmtppassword: res = res + " esmtppassword " + `self.esmtppassword` if self.interface or self.monitor or self.principal or self.plugin or self.plugout: + if folded: + res = res + "\n " + + if self.badheader: + res = res + "bad-header accept " + if self.retrieveerror == 'continue': + res = res + "retrieve-error continue " + if self.retrieveerror == 'markseen': + res = res + "retrieve-error markseen " + if self.badheader or self.retrieveerror != ServerDefaults.retrieveerror: if folded: res = res + "\n" @@ -235,6 +252,7 @@ class User: self.antispam = "" # Listener's spam-block code self.keep = FALSE # Keep messages self.flush = FALSE # Flush messages + self.limitflush = FALSE # Flush oversized messages self.fetchall = FALSE # Fetch old messages self.rewrite = TRUE # Rewrite message headers self.forcecr = FALSE # Force LF -> CR/LF @@ -248,7 +266,7 @@ class User: self.warnings = 3600 # Size warning interval (see tunable.h) self.fetchlimit = 0 # Max messages fetched per batch self.fetchsizelimit = 100 # Max message sizes fetched per transaction - self.fastuidl = 10 # Do fast uidl 9 out of 10 times + self.fastuidl = 4 # Do fast uidl 3 out of 4 times self.batchlimit = 0 # Max message forwarded per batch self.expunge = 0 # Interval between expunges (IMAP) self.ssl = 0 # Enable Seccure Socket Layer @@ -257,6 +275,7 @@ class User: self.sslproto = None # Force SSL? self.sslcertck = 0 # Enable strict SSL cert checking self.sslcertpath = None # Path to trusted certificates + self.sslcommonname = None # SSL CommonName to expect self.sslfingerprint = None # SSL key fingerprint to check self.properties = None # Extension properties User.typemap = ( @@ -274,6 +293,7 @@ class User: ('antispam', 'String'), ('keep', 'Boolean'), ('flush', 'Boolean'), + ('limitflush', 'Boolean'), ('fetchall', 'Boolean'), ('rewrite', 'Boolean'), ('forcecr', 'Boolean'), @@ -295,6 +315,7 @@ class User: ('sslcert', 'String'), ('sslcertck', 'Boolean'), ('sslcertpath', 'String'), + ('sslcommonname', 'String'), ('sslfingerprint', 'String'), ('properties', 'String')) @@ -310,6 +331,7 @@ class User: res = res + " here" if (self.keep != UserDefaults.keep or self.flush != UserDefaults.flush + or self.limitflush != UserDefaults.limitflush or self.fetchall != UserDefaults.fetchall or self.rewrite != UserDefaults.rewrite or self.forcecr != UserDefaults.forcecr @@ -324,6 +346,8 @@ class User: res = res + flag2str(self.keep, 'keep') if self.flush != UserDefaults.flush: res = res + flag2str(self.flush, 'flush') + if self.limitflush != UserDefaults.limitflush: + res = res + flag2str(self.limitflush, 'limitflush') if self.fetchall != UserDefaults.fetchall: res = res + flag2str(self.fetchall, 'fetchall') if self.rewrite != UserDefaults.rewrite: @@ -366,6 +390,8 @@ class User: res = res + flag2str(self.sslcertck, 'sslcertck') if self.sslcertpath and self.sslcertpath != UserDefaults.sslcertpath: res = res + " sslcertpath " + `self.sslcertpath` + if self.sslcommonname and self.sslcommonname != UserDefaults.sslcommonname: + res = res + " sslcommonname " + `self.sslcommonname` if self.sslfingerprint and self.sslfingerprint != UserDefaults.sslfingerprint: res = res + " sslfingerprint " + `self.sslfingerprint` if self.expunge != UserDefaults.expunge: @@ -392,7 +418,7 @@ class User: if self.mailboxes: res = res + " folder" for x in self.mailboxes: - res = res + " " + x + res = res + ' "%s"' % x res = res + "\n" for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'): if getattr(self, fld): @@ -411,8 +437,7 @@ class User: # # IANA port assignments and bogus 1109 entry -ianaservices = {"pop2":109, - "pop3":110, +ianaservices = {"pop3":110, "1109":1109, "imap":143, "smtp":25, @@ -420,15 +445,14 @@ ianaservices = {"pop2":109, # fetchmail protocol to IANA service name defaultports = {"auto":None, - "POP2":"pop2", "POP3":"pop3", - "APOP":"pop3", "KPOP":"1109", "IMAP":"imap", "ETRN":"smtp", "ODMR":"odmr"} -authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp") +authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp", + "msn", "ntlm", "apop", "cram-md5") listboxhelp = { 'title' : 'List Selection Help', @@ -660,9 +684,12 @@ needed to create a simple fetchmail setup. These include: 4. A protocol to use (POP, IMAP, ETRN, etc.) -5. A polling interval. +5. A poll interval in seconds. + If 0, fetchmail will run in the foreground once when started. + If > 0, fetchmail will run in the background and start a new poll + cycle after the interval has elapsed. -6. Options to fetch old messages as well as new, uor to suppress +6. Options to fetch old messages as well as new, or to suppress deletion of fetched message. The novice-configuration code will assume that you want to forward mail @@ -710,6 +737,14 @@ Send spam bounces? postmaster (depending on the "Bounces to sender?" option. Otherwise, spam bounces are not sent (the default). +Use soft bounces? + If this option is on, permanent delivery errors are treated as + temporary, i. e. mail is kept on the upstream server. Useful + during testing and after configuration changes, and on by + default. + If this option is off, permanent delivery errors delete + undeliverable mail from the upstream. + Invisible If false (the default) fetchmail generates a Received line into each message and generates a HELO from the machine it is running on. @@ -800,11 +835,18 @@ class ConfigurationEdit(Frame, MyWidget): sb = Frame(gf) Checkbutton(sb, - {'text':'send spam bounces?', + {'text':'Send spam bounces?', 'variable':self.spambounce, 'relief':GROOVE}).pack(side=LEFT, anchor=W) sb.pack(fill=X) + sb = Frame(gf) + Checkbutton(sb, + {'text':'Treat permanent errors as temporary?', + 'variable':self.softbounce, + 'relief':GROOVE}).pack(side=LEFT, anchor=W) + sb.pack(fill=X) + sf = Frame(gf) Checkbutton(sf, {'text':'Log to syslog?', @@ -864,14 +906,17 @@ class ConfigurationEdit(Frame, MyWidget): # Pre-1.5.2 compatibility... except os.error: pass + oldumask = os.umask(077) fm = open(self.outfile, 'w') + os.umask(oldumask) if fm: - fm.write("# Configuration created %s by fetchmailconf\n" % time.ctime(time.time())) + # be paranoid + if fm != sys.stdout: + os.chmod(self.outfile, 0600) + fm.write("# Configuration created %s by fetchmailconf %s\n" % (time.ctime(time.time()), version)) fm.write(`self.configuration`) if self.outfile: fm.close() - if fm != sys.stdout: - os.chmod(self.outfile, 0600) self.destruct() # @@ -917,6 +962,13 @@ the normal operation of fetchmail when it is run with no arguments. If it is off, fetchmail will only query this host when it is given as a command-line argument. +The `Retrieve Error Policy' specifies how server errors during +message retrieval are handled. The default behaviour is to abort the +current session. Both the continue and markseen options will skip +the message with the error, but continue the session allowing for +downloading of subsequent messages. Additionally, the markseen +option will mark the skipped message as seen. + The `True name of server' box should specify the actual DNS name to query. By default this is the same as the poll name. @@ -962,7 +1014,12 @@ sechelp = { 'title' : 'Security option help', 'banner': 'Security', 'text' : """ -The `interface' option allows you to specify a range +The 'authorization mode' allows you to choose the +mode that fetchmail uses to log in to your server. You +can usually leave this at 'any', but you will have to pick +'NTLM' and 'MSN' manually for the nonce. + +The 'interface' option allows you to specify a range of IP addresses to monitor for activity. If these addresses are not active, fetchmail will not poll. Specifying this may protect you from a spoofing attack @@ -983,8 +1040,10 @@ 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 sslcertck option enables strict checking of SSL server -certificates (and sslcertpath gives trusted certificate -directory). With sslfingerprint, you can specify a finger- +certificates (and sslcertpath gives the trusted certificate +directory). The sslcommonname option helps if the server is +misconfigured and returning "Server CommonName mismatch" +warnings. With sslfingerprint, you can specify a finger- print the server's key is checked against. """} @@ -1065,7 +1124,6 @@ class ServerEdit(Frame, MyWidget): # a custom port number you should be in expert mode and playing # close enough attention to notice this... self.service.set(defaultports[proto]) - if not proto in ("POP3", "APOP", "KPOP"): self.uidl.state = DISABLED def user_edit(self, username, mode): self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel()) @@ -1085,6 +1143,11 @@ class ServerEdit(Frame, MyWidget): ctlwin = Frame(leftwin, relief=RAISED, bd=5) Label(ctlwin, text="Run Controls").pack(side=TOP) Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP) + Checkbutton(ctlwin, text='Pass messages with bad headers?', + variable=self.badheader).pack(side=TOP) + retrieveerrorlist = ['abort', 'continue', 'markseen'] + Label(ctlwin, text="Retrieve Error Policy").pack(side=TOP) + ButtonBar(ctlwin, '', self.retrieveerror, retrieveerrorlist, 1, None) LabeledEntry(ctlwin, 'True name of ' + host + ':', self.via, leftwidth).pack(side=TOP, fill=X) LabeledEntry(ctlwin, 'Cycles to skip between polls:', @@ -1097,10 +1160,8 @@ class ServerEdit(Frame, MyWidget): # Compute the available protocols from the compile-time options protolist = ['auto'] - if 'pop2' in feature_options: - protolist.append("POP2") if 'pop3' in feature_options: - protolist = protolist + ["POP3", "APOP", "KPOP"] + protolist = protolist + ["POP3", "KPOP"] if 'sdps' in feature_options: protolist.append("SDPS") if 'imap' in feature_options: @@ -1119,9 +1180,6 @@ class ServerEdit(Frame, MyWidget): LabeledEntry(protwin, 'On server TCP/IP service:', self.service, leftwidth).pack(side=TOP, fill=X) self.defaultPort() - Checkbutton(protwin, - text="POP3: track `seen' with client-side UIDLs?", - variable=self.uidl).pack(side=TOP) Button(protwin, text='Probe for supported protocols', fg='blue', command=self.autoprobe).pack(side=LEFT) Button(protwin, text='Help', fg='blue', @@ -1163,8 +1221,8 @@ class ServerEdit(Frame, MyWidget): 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) + ButtonBar(secwin, 'Authorization mode:', + self.auth, authlist, 2, 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) @@ -1195,7 +1253,7 @@ class ServerEdit(Frame, MyWidget): else: realhost = self.server.pollname greetline = None - for protocol in ("IMAP","POP3","POP2"): + for protocol in ("IMAP","POP3"): service = defaultports[protocol] sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: @@ -1220,18 +1278,6 @@ out before getting a response. warnings = '' # OK, now try to recognize potential problems - if protocol == "POP2": - warnings = warnings + """ -It appears you have somehow found a mailserver running only POP2. -Congratulations. Have you considered a career in archaeology? - -Unfortunately, stock fetchmail binaries don't include POP2 support anymore. -Unless the first line of your fetchmail -V output includes the string "POP2", -you'll have to build it from sources yourself with the configure -switch --enable-POP2. - -""" - ### POP3 servers start here if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0: @@ -1422,13 +1468,6 @@ 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.

-""" - 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 + """ @@ -1453,7 +1492,7 @@ 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 +to version 5.0.2, has a bug in its generation of MIME boundaries (see the details in the fetchmail FAQ). As a result, even MIME aware MUAs will see attachments as part of the message text. If your Domino server's POP3 facility is enabled, we recommend you fall back on it. @@ -1469,20 +1508,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, "IMAP2bis") > 0: - warnings = warnings + """ -IMAP2bis servers have a minor problem; they can't peek at messages without -marking them seen. If you take a line hit during the retrieval, the -interrupted message may get left on the server, marked seen. - -To work around this, it is recommended that you set the `fetchall' -option on all user entries associated with this server, so any stuck -mail will be retrieved next time around. - -To fix this bug, upgrade to an IMAP4 server. The fetchmail FAQ includes -a pointer to an open-source implementation. - """ if string.find(greetline, "IMAP4rev1") > 0: warnings = warnings + """ @@ -1501,6 +1526,7 @@ Fetchmail doesn't know anything special about this server type. title = "Autoprobe of " + realhost + " succeeded" confirm = "The " + protocol + " server said:\n\n" + greetline + warnings self.protocol.set(protocol) + self.service.set(defaultports[protocol]) confwin.title(title) confwin.iconname(title) Label(confwin, text=title).pack() @@ -1630,6 +1656,8 @@ class UserEdit(Frame, MyWidget): variable=self.sslcertck).pack(side=TOP, fill=X) LabeledEntry(sslwin, 'SSL trusted certificate directory:', self.sslcertpath, '14').pack(side=TOP, fill=X) + LabeledEntry(sslwin, 'SSL CommonName:', + self.sslcommonname, '14').pack(side=TOP, fill=X) LabeledEntry(sslwin, 'SSL key fingerprint:', self.sslfingerprint, '14').pack(side=TOP, fill=X) sslwin.pack(fill=X, anchor=N) @@ -1649,9 +1677,9 @@ class UserEdit(Frame, MyWidget): Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP) ListEdit("Domains:", self.user.fetchdomains, None, None, targwin, None) - LabeledEntry(targwin, 'Append to MAIL FROM line:', + LabeledEntry(targwin, 'Use domain on RCPT TO line:', self.smtpaddress, '26').pack(side=TOP, fill=X) - LabeledEntry(targwin, 'Set RCPT To address:', + LabeledEntry(targwin, 'Set fixed RCPT TO address:', self.smtpname, '26').pack(side=TOP, fill=X) LabeledEntry(targwin, 'Connection setup command:', self.preconnect, '26').pack(side=TOP, fill=X) @@ -1684,6 +1712,8 @@ class UserEdit(Frame, MyWidget): if mode != 'novice': Checkbutton(optwin, text="Flush seen messages before retrieval", variable=self.flush).pack(side=TOP, anchor=W) + Checkbutton(optwin, text="Flush oversized messages before retrieval", + variable=self.limitflush).pack(side=TOP, anchor=W) Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply", variable=self.rewrite).pack(side=TOP, anchor=W) Checkbutton(optwin, text="Force CR/LF at end of each line", @@ -1821,7 +1851,7 @@ class RunWindow(Frame): # first. This avoids some obscure version-skew errors that can occur # if you pick up an old fetchmail from the standard system locations. os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"] - child_stdout = os.popen(command + " 2>&1", "r") + child_stdout = os.popen(command + " 2>&1 " + tmpfile + cmd = "umask 077 && fetchmail " + tmpfile else: - cmd = "umask 077; fetchmail --configdump --nosyslog >" + tmpfile + cmd = "umask 077 && fetchmail " + tmpfile try: s = os.system(cmd)