diff options
Diffstat (limited to 'fetchmailconf')
-rwxr-xr-x | fetchmailconf | 929 |
1 files changed, 929 insertions, 0 deletions
diff --git a/fetchmailconf b/fetchmailconf new file mode 100755 index 00000000..f8ba2e11 --- /dev/null +++ b/fetchmailconf @@ -0,0 +1,929 @@ +#!/usr/bin/python +# +# A GUI configurator for generating Fetchmail configuration files. + +from Tkinter import * +from Dialog import * +import sys +import time +import os +import string + +# +# Define the data structures the GUIs will be tossing around +# +class Controls: + def __init__(self): + self.foreground = FALSE; # Run in background + self.daemon = 300 # Default to 5-minute timeout + self.syslog = FALSE # Use syslogd for logging? + self.logfile = None # No logfile, initially + + def __repr__(self): + str = ""; + if self.syslog: + str = str + ("set syslog\n") + elif self.logfile: + str = str + ("set logfile \"%s\"\n" % (self.logfile,)); + if not self.foreground and self.daemon: + str = str + ("set daemon %s\n" % (self.daemon,)) + return str + "\n" + + def __str__(self): + return "[Server: " + repr(self) + "]" + +class Server: + def __init__(self): + self.pollname = None # Poll label + self.via = None # True name of host + self.active = TRUE # Poll status + self.interval = 0 # Skip interval + 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.timeout = 300 # 5-minute timeout + self.envelope = "Received" # Envelope-address header + self.aka = [] # List of DNS aka names + self.dns = TRUE # Enable DNS lookup on multidrop + self.localdomains = [] # Domains to be considered local + self.interface = None # IP address and range + self.monitor = None # IP address and range + self.userlist = [] # List of user entries for site + self.typemap = ( + ('pollname', 'String'), + ('via', 'String'), + ('active', 'Boolean'), + ('interval', 'Int'), + ('protocol', 'String'), + ('interval', 'Int'), + ('port', 'Int'), + ('uidl', 'Boolean'), + ('auth', 'String'), + ('timeout', 'Int'), + ('envelope', 'String'), + # leave aka out + ('dns', 'Boolean'), + # leave localdomains out + ('interface', 'String'), + ('monitor', 'String')) + + def dump(self, folded): + str = "" + if self.active: str = str + "poll" + else: str = str + "skip" + str = str + (" " + self.pollname) + if self.via != self.pollname: + str = str + " via " + self.via + if self.protocol != ServerDefaults.protocol: + str = str + " with proto " + self.protocol + if self.port != defaultports[self.protocol]: + str = str + " port " + `self.port` + if self.timeout != ServerDefaults.timeout: + str = str + " timeout " + `self.timeout` + if self.interval != ServerDefaults.interval: + str = str + " interval " + `self.interval` + if self.envelope != ServerDefaults.envelope: + str = str + " envelope " + self.envelope + if self.auth != ServerDefaults.auth: + str = str + " auth " + self.auth + if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl: + str = str + " and options" + if self.dns != ServerDefaults.dns: + str = str + flag2str(self.dns, 'dns') + if self.uidl != ServerDefaults.uidl: + str = str + flag2str(self.uidl, 'uidl') + if folded: str = str + "\n\t" + else: str = str + " " + + if self.aka: + str = str + "aka" + for x in self.aka: + str = str + " " + x + if self.aka and self.localdomains: str = str + " " + if self.localdomains: + str = str + ("localdomains") + for x in self.localdomains: + str = str + " " + x + if (self.aka or self.localdomains): + if folded: + str = str + "\n\t" + else: + str = str + " " + + if self.interface: str = str + " interface " + self.interface + if self.monitor: str = str + " monitor " + self.monitor + if (self.interface or self.monitor): + if folded: + str = str + "\n" + + if str[-1] == "\t": str = str[0:-1] + return str; + + def __repr__(self): + return self.dump(TRUE) + + def __str__(self): + return "[Server: " + self.dump(FALSE) + "]" + +class User: + def __init__(self): + self.username = "" # Remote username + self.localnames = None # Local names + self.password = "" # Password for mail account access + self.folder = "" # Remote folder to retrieve from + self.smtphost = 'localhost' # Host to forward to + self.mda = "" # Mail Delivery Agent + self.preconnect = "" # Connection setup + self.postconnect = "" # Connection wrapup + self.keep = FALSE # Keep messages + self.flush = FALSE # Flush messages + self.fetchall = FALSE # Fetch old messages + self.rewrite = TRUE # Rewrite message headers + self.forcecr = FALSE # Force LF -> CR/LF + self.stripcr = FALSE # Strip CR + self.pass8bits = FALSE # Force BODY=7BIT + self.dropstatus = FALSE # Force BODY=7BIT + self.limit = 0 # Message size limit + self.fetchlimit = 0 # Max messages fetched per batch + self.batchlimit = 0 # Max message forwarded per batch + self.expunge = 1 # Interval between expunges (IMAP) + self.typemap = ( + ('username', 'String'), + ('folder', 'String'), + # leave out localnames + ('password', 'String'), + ('smtphost', 'String'), + ('preconnect', 'String'), + ('postconnect', 'String'), + ('mda', 'String'), + ('keep', 'Boolean'), + ('flush', 'Boolean'), + ('fetchall', 'Boolean'), + ('rewrite', 'Boolean'), + ('forcecr', 'Boolean'), + ('stripcr', 'Boolean'), + ('pass8bits', 'Boolean'), + ('dropstatus', 'Boolean'), + ('limit', 'Int'), + ('fetchlimit', 'Int'), + ('batchlimit', 'Int'), + ('expunge', 'Int')) + + def __repr__(self): + str = "" + str = str + "user " + self.user; + if self.password: str = str + "with password " + self.password + if self.localnames: + str = str + "localnames" + for x in self.localnames: + str = str + " " + x + if (self.keep or self.flush or self.fetchall or self.rewrite or + self.forcecr or self.stripcr or self.pass8bits or self.dropstatus): + str = str + " options" + if self.keep != UserDefaults.keep: + str = str + flag2str(self.keep, 'keep') + if self.flush != UserDefaults.flush: + str = str + flag2str(self.flush, 'flush') + if self.fetchall != UserDefaults.fetchall: + str = str + flag2str(self.fetchall, 'fetchall') + if self.rewrite != UserDefaults.rewrite: + str = str + flag2str(self.rewrite, 'rewrite') + if self.forcecr != UserDefaults.forcecr: + str = str + flag2str(self.forcecr, 'forcecr') + if self.stripcr != UserDefaults.stripcr: + str = str + flag2str(self.stripcr, 'stripcr') + if self.pass8bits != UserDefaults.pass8bits: + str = str + flag2str(self.pass8bits, 'pass8bits') + if self.dropstatus != UserDefaults.dropstatus: + str = str + flag2str(self.dropstatus, 'dropstatus') + if self.limit != UserDefaults.limit: + str = str + " limit " + `self.limit` + if self.fetchlimit != UserDefaults.fetchlimit: + str = str + " fetchlimit " + `self.fetchlimit` + if self.batchlimit != UserDefaults.batchlimit: + str = str + " batchlimit " + `self.batchlimit` + if self.expunge != UserDefaults.expunge: + str = str + " expunge " + `self.expunge` + + def __str__(self): + return "[User: " + repr(self) + "]" + +# +# Helper code +# + +defaultports = {"auto":0, + "POP2":109, + "POP3":110, "APOP":110, "KPOP":1109, "IMAP":143, + "IMAP-K4":143, + "ETRN":25} + +protolist = ("auto", "POP2", "POP3", "APOP", "KPOP", "IMAP", "IMAP-K4", "ETRN") + +authlist = ("password", "kerberos") + +listboxhelp = { + 'title' : 'List Selection Help', + 'banner': 'List Selection', + 'text' : """ +You must select an item in the list box (by clicking on it). +"""} + +def flag2str(value, string): +# make a string representation of a .fetchmailrc flag or negated flag + str = "" + if value != None: + str = str + (" ") + if value == FALSE: str = str + ("no ") + str = str + string; + return str + +class LabeledEntry(Frame): +# widget consisting of entry field with caption to left + def bind(self, key, action): + self.E.bind(key, action) + def focus_set(self): + self.E.focus_set() + def __init__(self, Master, text, textvar, width): + Frame.__init__(self, Master) + self.L = Label(self, {'text':text, 'width':width, 'anchor':'w'}) + self.E = Entry(self, {'textvar':textvar}) + self.L.pack({'side':'left'}) + self.E.pack({'side':'left', 'expand':'1', 'fill':'x'}) + +def ButtonBar(frame, legend, ref, alternatives, depth, command): +# array of radio buttons, caption to left, picking from a string list + bar = Frame(frame) + width = len(alternatives) / depth; + Label(bar, text=legend).pack(side=LEFT, anchor=N) + for column in range(width): + subframe = Frame(bar) + for row in range(depth): + ind = width * row + column + Radiobutton(subframe, + {'text':alternatives[ind], + 'variable':ref, + 'value':alternatives[ind], + 'command':command}).pack(side=TOP, anchor=W) + subframe.pack(side=LEFT) + bar.pack(side=TOP); + return bar + +def helpwin(helpdict): +# help message window with a self-destruct button + helpwin = Toplevel() + helpwin.title(helpdict['title']) + helpwin.iconname(helpdict['title']) + Label(helpwin, text=helpdict['banner']).pack() + textwin = Message(helpwin, text=helpdict['text'], width=600) + textwin.pack() + Button(helpwin, text='Done', + command=lambda x=helpwin: Widget.destroy(x), + relief=SUNKEN, bd=2).pack() + +class ListEdit(Frame): +# edit a list of values (duplicates not allowed) with a supplied editor hook + def __init__(self, newlegend, list, editor, master, helptxt): + self.editor = editor + self.list = list + + # Set up a widget to accept new sites + self.newval = StringVar(master) + newwin = LabeledEntry(master, newlegend, self.newval, '12') + newwin.bind('<Double-1>', self.handleNew) + newwin.bind('<Return>', self.handleNew) + newwin.pack(side=TOP, fill=X, anchor=E) + + # Create the sitelist for site-configuration selection + listframe = Frame(master) + scroll = Scrollbar(listframe) + listwidget = Listbox(listframe, height=0, selectmode='browse') + if list: + for dnsname in list: + listwidget.insert('end', dnsname) + listframe.pack(side=TOP, expand=YES, fill=BOTH) + listwidget.config(yscrollcommand=scroll.set, relief=SUNKEN) + listwidget.pack(side=LEFT, expand=YES, fill=BOTH) + scroll.config(command=listwidget.yview, relief=SUNKEN) + scroll.pack(side=RIGHT, fill=BOTH) + listwidget.config(selectmode=SINGLE, setgrid=TRUE) + listwidget.bind('<Double-1>', self.handleList); + listwidget.bind('<Return>', self.handleList); + self.listwidget = listwidget + + bf = Frame(master); + if self.editor: + Button(bf, text='Edit', command=self.editItem).pack(side=LEFT) + Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT) + if helptxt: + self.helptxt = helptxt + Button(bf, text='Help', fg='blue', + command=self.help).pack(side=RIGHT) + bf.pack(fill=X) + + def help(self): + helpwin(self.helptxt) + + def handleList(self, event): + 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('') + + def editItem(self): + select = self.listwidget.curselection() + if not select: + helpwin(listboxhelp) + else: + index = select[0] + if index and self.editor: + label = self.listwidget.get(index); + apply(self.editor, (label,)) + + def deleteItem(self): + select = self.listwidget.curselection() + if not select: + helpwin(listboxhelp) + else: + index = select[0] + if index: + self.listwidget.delete(index) + if self.list != None: del self.list[index] + +def ConfirmQuit(frame, context): + ans = Dialog(frame, + title = 'Quit?', + text = 'Really quit ' + context + ' without saving?', + bitmap = 'question', + strings = ('Yes', 'No'), + default = 1) + return ans.num == 0 +# +# First, code to set the global fetchmail run controls. +# + +confighelp = { + 'title' : 'Fetchmail configurator help', + 'banner': 'Configurator help', + 'text' : """ +In the `Configurator Controls' panel, you can: + +Press `Save' to save the new fetchmail configuration you have created. +Press `Quit' to exit without saving. +Press `Help' to bring up this help message. + +In the `Configurator Controls' panel, you can set the following options that +control how fetchmail runs: + +Poll interval + Number of seconds to wait between polls in the background. + Ignored if the `Run in Foreground?' option is on. + +Logfile + If empty, emit progress and error messages to stderr. + Otherwise this gives the name of the files to write to. + This field is ignored if the "Log to syslog?" option is on. + +In the `Remote Mail Configurations' panel, you can: + +1. Enter the name of a new remote mail server you want fetchmail to query. + +To do this, simply enter a label for the poll configuration in the +`New Server:' box. The label should be a DNS name of the server (unless +you are using ssh or some other tunneling method and will fill in the `via' +option on the site configuration screen). + +2. Change the configuration of an existing site. + +To do this, find the site's label in the listbox and double-click it. +This will take you to a site configuration dialogue. +"""} + +class ControlEdit(Frame): + def PostControls(self): + self.foreground = BooleanVar(self) + self.foreground.set(self.controls.foreground) + self.daemon = StringVar(self) + self.daemon.set(`self.controls.daemon`) + self.syslog = BooleanVar(self) + self.syslog.set(self.controls.syslog); + self.logfile = StringVar(self) + if self.controls.logfile: self.logfile.set(self.controls.logfile); + + gf = Frame(self, relief=RAISED, bd = 5) + + Label(gf, + text='Fetchmail Run Controls', + bd=2).pack(side=TOP, pady=10) + + df = Frame(gf, relief=RAISED, bd=2) + + # Run in foreground? + Checkbutton(df, + {'text':'Run in foreground?', + 'variable':self.foreground, + 'relief':GROOVE}).pack(side=LEFT,anchor=W) + + # Set the poll interval + de = LabeledEntry(df, ' Poll interval:', self.daemon, '14') + de.pack(side=RIGHT, anchor=E) + + df.pack(); + + sf = Frame(gf, relief=RAISED, bd=2) + + # Use syslog for logging? + Checkbutton(sf, + {'text':'Log to syslog?', + 'variable':self.syslog, + 'relief':GROOVE}).pack(side=LEFT, anchor=W) + + # Set the logfile + log = LabeledEntry(sf, ' Logfile:', self.logfile, '14') + log.pack(side=RIGHT, anchor=E) + + sf.pack(fill=X) + gf.pack(fill=X) + + def GatherControls(self): + self.controls.daemon = self.daemon.get() + self.controls.foreground = self.foreground.get() + self.controls.logfile = self.logfile.get() + self.controls.syslog = self.syslog.get() + +# +# Server editing stuff. +# +remotehelp = { + 'title' : 'Remote site help', + 'banner': 'Remote sites', + 'text' : """ +When you add a site name to the list here, +you initialize an entry telling fetchmail +how to poll a new site. + +When you select a sitename (by double- +clicking it, or by single-clicking to +select and then clicking the Edit button), +you will open a window to configure that +site. +"""} + +serverhelp = { + 'title' : 'Server options help', + 'banner': 'Server Options', + 'text' : """ +The server options screen controls fetchmail +options that apply to one of your mailservers. + +Once you have a mailserver configuration set +up as you like it, you can select `Save' to +store it in the server list maintained in +the main configuration window. + +If you wish to discard changes to a server +configuration, select `Quit'. +"""} + +controlhelp = { + 'title' : 'Run Control help', + 'banner': 'Run Controls', + 'text' : """ +If the `Poll normally' checkbox is on, the host is polled as part of +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 `True name of server' box should specify the actual DNS name +to query. By default this is the same as the poll name. + +Normally each host described in the file is queried once each +poll cycle. If `Cycles to skip between polls' is greater than 0, +that's the number of poll cycles that are skipped between the +times this post is actually polled. + +The `Server timeout' is the number of seconds fetchmail will wait +for a reply from the mailserver before concluding it is hung and +giving up. +"""} + +protohelp = { + 'title' : 'Protocol and Port help', + 'banner': 'Protocol and Port', + 'text' : """ +These options control the remote-mail protocol +and TCP/IP service port used to query this +server. + +The `Protocol' button bar offers you a choice of +all the different protocols available. The `auto' +protocol is a special mode that probes the host +ports for POP3 and IMAP to see if either is +available. + +Normally the TCP/IP service port to use is +dictated by the protocol choice. The `Port' +field lets you set a non-standard port. +"""} + +sechelp = { + 'title' : 'Security option help', + 'banner': 'Security', + 'text' : """ +These options control the security procedure used +to protect mail transfer + +Normally the mail fetch is validated using an +ordinary password logon. If your server speaks +MIT Kerberos IV it is possible to pre-authenticate +the exxchange with a Kerberos ticket. + +The `interface' and `monitor' options are available +only for Linux systems. See the fetchmail manual page +for details on these. +"""} + +multihelp = { + 'title' : 'Multidrop option help', + 'banner': 'Multidrop', + 'text' : """ +These options are only useful with multidrop mode. +See the manual page for extended discussion. +"""} + +suserhelp = { + 'title' : 'User list help', + 'banner': 'User list', + 'text' : """ +When you add a user name to the list here, +you initialize an entry telling fetchmail +to poll the site on behalf of the new user. + +When you select a username (by double- +clicking it, or by single-clicking to +select and then clicking the Edit button), +you will open a window to configure the +user's options on that site. +"""} + +class ServerEdit(Frame): + def __init__(self, host, sitelist, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.master.title('Fetchmail host ' + host); + self.master.iconname('Fetchmail host ' + host); + self.server = Server() + self.server.pollname = host + self.server.via = host + self.sitelist = sitelist + self.post() + self.createWidgets(host) +# self.grab_set() +# self.focus_set() +# self.wait_window() + + def post(self): + # we can't abstract this away, execs would happen in the wrong scope + for x in self.server.typemap: + target = "self." + x[0] + source = "self.server." + x[0] + if x[1] == 'Boolean': + exec target + " = BooleanVar(self)" + if eval(source): + exec target + ".set(" + source + ")" + elif x[1] == 'String': + exec target + " = StringVar(self)" + if eval(source): + exec target + ".set(" + source + ")" + elif x[1] == 'Int': + exec target + " = IntVar(self)" + if eval(source): + exec target + ".set(" + source + ")" + + def gather(self): + for x in self.server.typemap: + setattr(self.server, x[0], getattr(self, x[0]).get()) + + def nosave(self): + if ConfirmQuit(self, 'server option editing'): + Widget.destroy(self.master) + + def save(self): + self.gather() + self.sitelist.append(self.server) + Widget.destroy(self.master) + + def refreshPort(self): + proto = self.protocol.get() + self.port.set(defaultports[proto]) + if not proto in ("POP3", "APOP", "KPOP"): self.uidl = FALSE + + def createWidgets(self, host): + topwin = Frame(self, relief=RAISED, bd=5) + Label(topwin, text="Server options for " + host).pack(side=TOP,pady=10) + Button(topwin, text='Save', fg='blue', + command=self.save).pack(side=LEFT) + Button(topwin, text='Quit', fg='blue', + command=self.nosave).pack(side=LEFT) + Button(topwin, text='Help', fg='blue', + command=lambda: helpwin(serverhelp)).pack(side=RIGHT) + topwin.pack(fill=X) + + leftwin = Frame(self); + leftwidth = '26'; + + 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) + LabeledEntry(ctlwin, 'True name of ' + host + ':', + self.via, leftwidth).pack(side=TOP, fill=X) + LabeledEntry(ctlwin, 'Cycles to skip between polls:', + self.interval, leftwidth).pack(side=TOP, fill=X) + LabeledEntry(ctlwin, 'Server timeout (seconds):', + self.timeout, leftwidth).pack(side=TOP, fill=X) + Button(ctlwin, text='Help', fg='blue', + command=lambda: helpwin(controlhelp)).pack(side=RIGHT) + ctlwin.pack(fill=X) + + protwin = Frame(leftwin, relief=RAISED, bd=5) + Label(protwin, text="Protocol and Port").pack(side=TOP) + pb = ButtonBar(protwin, 'Protocol:', self.protocol, protolist, 2, self.refreshPort) + LabeledEntry(protwin, 'TCP/IP service port to query:', + self.port, leftwidth).pack(side=TOP, fill=X) + Checkbutton(protwin, + text="Track seen POP3 messages with client-side UIDL list?", + variable=self.uidl).pack(side=TOP) + Button(protwin, text='Help', fg='blue', + command=lambda: helpwin(protohelp)).pack(side=RIGHT) + protwin.pack(fill=X) + + secwin = Frame(leftwin, relief=RAISED, bd=5) + Label(secwin, text="Security").pack(side=TOP) + ButtonBar(secwin, 'Authorization mode:', + self.auth, authlist, 1, None).pack(side=TOP) + + if os.popen("uname").readlines()[0] == 'Linux\n': + LabeledEntry(secwin, 'Interface to check before poll:', + self.interface, leftwidth).pack(side=TOP, fill=X) + LabeledEntry(secwin, 'IP range to watch for activity:', + self.monitor, leftwidth).pack(side=TOP, fill=X) + + Button(secwin, text='Help', fg='blue', + command=lambda: helpwin(sechelp)).pack(side=RIGHT) + secwin.pack(fill=X) + + leftwin.pack(side=LEFT, anchor=N); + rightwin = Frame(self); + + mdropwin = Frame(rightwin, relief=RAISED, bd=5) + Label(mdropwin, text="Multidrop options").pack(side=TOP) + LabeledEntry(mdropwin, 'Envelope address header:', + self.envelope, '24').pack(side=TOP, fill=X) + Checkbutton(mdropwin, text="Enable multidrop DNS lookup?", + variable=self.dns).pack(side=TOP) + Label(mdropwin, text="DNS aliases").pack(side=TOP) + ListEdit("New site alias: ", self.server.aka, None, mdropwin, None) + Label(mdropwin, text="Domains to be considered local").pack(side=TOP) + ListEdit("New domain: ", + self.server.localdomains, None, mdropwin, multihelp) + mdropwin.pack(fill=X) + + userwin = Frame(rightwin, relief=RAISED, bd=5) + Label(userwin, text="User entries for " + host).pack(side=TOP) + ListEdit("New user: ", None, self.edituser, userwin, suserhelp) + userwin.pack(fill=X) + + rightwin.pack(side=LEFT); + + def edituser(self, user): + UserEdit(user, self.server.userlist, Toplevel()) + +# +# User editing stuff +# + +userhelp = { + 'title' : 'User option help', + 'banner': 'User options', + 'text' : """ +You may use this panel to set options +that may differ between individual +users on your site. + +Note: if a site entry has more than one +local user, messages will be retrieved +in multidrop mode. This complicates +the configuration issues; see the manual +page section on multidrop mode. +"""} + +localhelp = { + 'title' : 'Local name help', + 'banner': 'Local names', + 'text' : """ +The local name(s) in a user entry are the +people on the client machine who should +receive mail from the poll described. + +Note: if a user entry has more than one +local name, messages will be retrieved +in multidrop mode. This complicates +the configuration issues; see the manual +page section on multidrop mode. +"""} + +class UserEdit(Frame): + def __init__(self, user, userlist, master=None): + Frame.__init__(self, master) + Pack.config(self) + self.master.title('Fetchmail user ' + user); + self.master.iconname('Fetchmail user ' + user); + self.user = User() + self.user.remote = user + self.user.localnames = [user] + self.userlist = userlist + self.post() + self.createWidgets() +# self.grab_set() +# self.focus_set() +# self.wait_window() + + def post(self): + # we can't abstract this away, execs would happen in the wrong scope + for x in self.user.typemap: + target = "self." + x[0] + source = "self.user." + x[0] + if x[1] == 'Boolean': + exec target + " = BooleanVar(self)" + if eval(source): + exec target + ".set(" + source + ")" + elif x[1] == 'String': + exec target + " = StringVar(self)" + if eval(source): + exec target + ".set(" + source + ")" + elif x[1] == 'Int': + exec target + " = IntVar(self)" + if eval(source): + exec target + ".set(" + source + ")" + + def gather(self): + for x in self.user.typemap: + setattr(self.user, x[0], getattr(self, x[0]).get()) + + def nosave(self): + if ConfirmQuit(self, 'user option editing'): + Widget.destroy(self.master) + + def save(self): + self.gather() + self.userlist.append(self.user) + Widget.destroy(self.master) + + def createWidgets(self): + topwin = Frame(self, relief=RAISED, bd=5) + Label(topwin, + text="User options for " + self.user.remote).pack(side=TOP,pady=10) + Button(topwin, text='Save', fg='blue', + command=self.save).pack(side=LEFT) + Button(topwin, text='Quit', fg='blue', + command=self.nosave).pack(side=LEFT) + Button(topwin, text='Help', fg='blue', + command=lambda: helpwin(userhelp)).pack(side=RIGHT) + topwin.pack(fill=X) + + leftwin = Frame(self); + + secwin = Frame(leftwin, relief=RAISED, bd=5) + Label(secwin, text="Authentication").pack(side=TOP) + LabeledEntry(secwin, 'Password:', + self.password, '12').pack(side=TOP, fill=X) + LabeledEntry(secwin, 'Folder(s):', + self.folder, '12').pack(side=TOP, fill=X) + secwin.pack(fill=X, anchor=N) + + names = Frame(leftwin, relief=RAISED, bd=5) + Label(names, text="Local names").pack(side=TOP) + ListEdit("New name: ", self.user.localnames, None, names, localhelp) + names.pack(fill=X, anchor=N) + + targwin = Frame(leftwin, relief=RAISED, bd=5) + Label(targwin, text="Forwarding Options").pack(side=TOP) + LabeledEntry(targwin, 'System to forward to:', + self.smtphost, '26').pack(side=TOP, fill=X) + LabeledEntry(targwin, 'Connection setup command:', + self.preconnect, '26').pack(side=TOP, fill=X) + LabeledEntry(targwin, 'Connection wrapup command:', + self.postconnect, '26').pack(side=TOP, fill=X) + LabeledEntry(targwin, 'Local delivery agent:', + self.mda, '26').pack(side=TOP, fill=X) + targwin.pack(fill=X, anchor=N) + + leftwin.pack(side=LEFT, fill=X, anchor=N) + rightwin = Frame(self) + + optwin = Frame(rightwin, relief=RAISED, bd=5) + Label(optwin, text="Processing Options").pack(side=TOP) + Checkbutton(optwin, text="Suppress deletion of messages after reading", + variable=self.keep).pack(side=TOP, anchor=W) + Checkbutton(optwin, text="Flush seen messages before retrieval", + variable=self.flush).pack(side=TOP, anchor=W) + Checkbutton(optwin, text="Fetch old messages as well as new", + variable=self.fetchall).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", + variable=self.forcecr).pack(side=TOP, anchor=W) + Checkbutton(optwin, text="Strip CR from end of eacgh line", + variable=self.stripcr).pack(side=TOP, anchor=W) + Checkbutton(optwin, text="Pass 8 bits even theough SMTP says 7BIT", + variable=self.pass8bits).pack(side=TOP, anchor=W) + Checkbutton(optwin, text="Drop Status lines from forwarded messages", + variable=self.dropstatus).pack(side=TOP, anchor=W) + optwin.pack(fill=X) + + limwin = Frame(rightwin, relief=RAISED, bd=5) + Label(limwin, text="Resource Limits").pack(side=TOP) + LabeledEntry(limwin, 'Message size limit:', + self.limit, '30').pack(side=TOP, fill=X) + LabeledEntry(limwin, 'Max messages to fetch per poll:', + self.fetchlimit, '30').pack(side=TOP, fill=X) + LabeledEntry(limwin, 'Max messages to forward per poll:', + self.batchlimit, '30').pack(side=TOP, fill=X) + LabeledEntry(limwin, 'Interval between expunges (IMAP):', + self.expunge, '30').pack(side=TOP, fill=X) + limwin.pack(fill=X) + + rightwin.pack(side=LEFT) + + +# +# Configure drives the configuration dialogue. It may call multiple +# instances of ServerEdit to do its job. +# + +class Configure(Frame, ControlEdit): + def __init__(self, master=None): + Frame.__init__(self, master) + self.master.title('fetchmail configurator'); + self.master.iconname('fetchmail configurator'); + Pack.config(self) + self.MakeDispose() + self.controls = Controls() + self.PostControls() + self.MakeSitelist(master) + self.sites = [] + + def MakeDispose(self): + # Set the disposal of the given configuration + dispose = Frame(self, relief=RAISED, bd=5); + Label(dispose, + text='Configurator Controls', + bd=2).pack(side=TOP, pady=10) + Button(dispose, text='Save', fg='blue', + command=self.save).pack(side=LEFT) + Button(dispose, text='Quit', fg='blue', + command=self.nosave).pack(side=LEFT) + Button(dispose, text='Help', fg='blue', + command=lambda: helpwin(confighelp)).pack(side=RIGHT) + dispose.pack(side=TOP, fill=X); + + def MakeSitelist(self, master): + lf = Frame(master, relief=RAISED, bd=5) + Label(lf, + text='Remote Mail Server Configurations', + bd=2).pack(side=TOP, pady=10) + ListEdit('New Server:', None, self.editsite, lf, remotehelp) + lf.pack(fill=X) + + def editsite(self, site): + ServerEdit(site, self.sites, Toplevel()) + + def save(self): + self.GatherControls() + sys.stdout.write("# Configuration created %s\n" % time.ctime(time.time())) + sys.stdout.write(`self.controls`) + for site in self.sites: + sys.stdout.write(`site`) + for user in self.sites.userlist: + sys.stdout.write(`user`) + self.quit() + + def nosave(self): + if ConfirmQuit(self, "configuration editor"): + self.quit() + +if __name__ == '__main__': + ServerDefaults = Server() + UserDefaults = User() + Configure().mainloop() + +# The following sets edit modes for GNU EMACS +# Local Variables: +# mode:python +# End: |