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:  | 
