3 # A GUI configurator for generating fetchmail configuration files.
4 # by Eric S. Raymond, <esr@snark.thyrsus.com>,
5 # Matthias Andree <matthias.andree@gmx.de>
6 # Requires Python with Tkinter, and the following OS-dependent services:
7 # posix, posixpath, socket
12 import sys, time, os, string, socket, getopt, tempfile
15 # Define the data structures the GUIs will be tossing around
19 self.poll_interval = 0 # Normally, run in foreground
20 self.logfile = None # No logfile, initially
21 self.idfile = os.environ["HOME"] + "/.fetchids" # Default idfile, initially
22 self.postmaster = None # No last-resort address, initially
23 self.bouncemail = TRUE # Bounce errors to users
24 self.spambounce = FALSE # Bounce spam errors
25 self.properties = None # No exiguous properties
26 self.invisible = FALSE # Suppress Received line & spoof?
27 self.syslog = FALSE # Use syslogd for logging?
28 self.servers = [] # List of included sites
29 Configuration.typemap = (
30 ('poll_interval', 'Int'),
31 ('logfile', 'String'),
33 ('postmaster', 'String'),
34 ('bouncemail', 'Boolean'),
35 ('spambounce', 'Boolean'),
36 ('properties', 'String'),
37 ('syslog', 'Boolean'),
38 ('invisible', 'Boolean'))
42 if self.syslog != ConfigurationDefaults.syslog:
43 str = str + ("set syslog\n")
45 str = str + ("set logfile \"%s\"\n" % (self.logfile,));
46 if self.idfile != ConfigurationDefaults.idfile:
47 str = str + ("set idfile \"%s\"\n" % (self.idfile,));
48 if self.postmaster != ConfigurationDefaults.postmaster:
49 str = str + ("set postmaster \"%s\"\n" % (self.postmaster,));
51 str = str + ("set bouncemail\n")
53 str = str + ("set nobouncemail\n")
55 str = str + ("set spambounce\n")
57 str = str + ("set no spambounce\n")
58 if self.properties != ConfigurationDefaults.properties:
59 str = str + ("set properties \"%s\"\n" % (self.properties,));
60 if self.poll_interval > 0:
61 str = str + "set daemon " + `self.poll_interval` + "\n"
62 for site in self.servers:
63 str = str + repr(site)
66 def __delitem__(self, name):
67 for si in range(len(self.servers)):
68 if self.servers[si].pollname == name:
73 return "[Configuration: " + repr(self) + "]"
77 self.pollname = None # Poll label
78 self.via = None # True name of host
79 self.active = TRUE # Poll status
80 self.interval = 0 # Skip interval
81 self.protocol = 'auto' # Default to auto protocol
82 self.service = None # Service name to use
83 self.uidl = FALSE # Don't use RFC1725 UIDLs by default
84 self.auth = 'any' # Default to password authentication
85 self.timeout = 300 # 5-minute timeout
86 self.envelope = 'Received' # Envelope-address header
87 self.envskip = 0 # Number of envelope headers to skip
88 self.qvirtual = None # Name prefix to strip
89 self.aka = [] # List of DNS aka names
90 self.dns = TRUE # Enable DNS lookup on multidrop
91 self.localdomains = [] # Domains to be considered local
92 self.interface = None # IP address and range
93 self.monitor = None # IP address and range
94 self.plugin = None # Plugin command for going to server
95 self.plugout = None # Plugin command for going to listener
96 self.principal = None # Kerberos principal
97 self.esmtpname = None # ESMTP 2554 name
98 self.esmtppassword = None # ESMTP 2554 password
99 self.tracepolls = FALSE # Add trace-poll info to headers
100 self.users = [] # List of user entries for site
102 ('pollname', 'String'),
104 ('active', 'Boolean'),
106 ('protocol', 'String'),
107 ('service', 'String'),
111 ('envelope', 'String'),
113 ('qvirtual', 'String'),
116 # leave localdomains out
117 ('interface', 'String'),
118 ('monitor', 'String'),
119 ('plugin', 'String'),
120 ('plugout', 'String'),
121 ('esmtpname', 'String'),
122 ('esmtppassword', 'String'),
123 ('principal', 'String'),
124 ('tracepolls','Boolean'))
126 def dump(self, folded):
128 if self.active: res = res + "poll"
129 else: res = res + "skip"
130 res = res + (" " + self.pollname)
132 res = res + (" via " + str(self.via) + "\n");
133 if self.protocol != ServerDefaults.protocol:
134 res = res + " with proto " + self.protocol
135 if self.service and self.service != defaultports[self.protocol] and self.service != ianaservices[defaultports[self.protocol]]:
136 res = res + " service " + self.service
137 if self.timeout != ServerDefaults.timeout:
138 res = res + " timeout " + `self.timeout`
139 if self.interval != ServerDefaults.interval:
140 res = res + " interval " + `self.interval`
141 if self.envelope != ServerDefaults.envelope or self.envskip != ServerDefaults.envskip:
143 res = res + " envelope " + `self.envskip` + " " + self.envelope
145 res = res + " envelope " + self.envelope
147 res = res + (" qvirtual " + str(self.qvirtual) + "\n");
148 if self.auth != ServerDefaults.auth:
149 res = res + " auth " + self.auth
150 if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl:
151 res = res + " and options"
152 if self.dns != ServerDefaults.dns:
153 res = res + flag2str(self.dns, 'dns')
154 if self.uidl != ServerDefaults.uidl:
155 res = res + flag2str(self.uidl, 'uidl')
156 if folded: res = res + "\n "
157 else: res = res + " "
163 if self.aka and self.localdomains: res = res + " "
164 if self.localdomains:
165 res = res + ("localdomains")
166 for x in self.localdomains:
168 if (self.aka or self.localdomains):
175 res = res + "tracepolls\n"
178 res = res + " interface " + str(self.interface)
180 res = res + " monitor " + str(self.monitor)
182 res = res + " plugin " + `self.plugin`
184 res = res + " plugout " + `self.plugout`
186 res = res + " principal " + `self.principal`
188 res = res + " esmtpname " + `self.esmtpname`
189 if self.esmtppassword:
190 res = res + " esmtppassword " + `self.esmtppassword`
191 if self.interface or self.monitor or self.principal or self.plugin or self.plugout:
195 if res[-1] == " ": res = res[0:-1]
197 for user in self.users:
198 res = res + repr(user)
202 def __delitem__(self, name):
203 for ui in range(len(self.users)):
204 if self.users[ui].remote == name:
209 return self.dump(TRUE)
212 return "[Server: " + self.dump(FALSE) + "]"
216 if os.environ.has_key("USER"):
217 self.remote = os.environ["USER"] # Remote username
218 elif os.environ.has_key("LOGNAME"):
219 self.remote = os.environ["LOGNAME"]
221 print "Can't get your username!"
223 self.localnames = [self.remote,]# Local names
224 self.password = None # Password for mail account access
225 self.mailboxes = [] # Remote folders to retrieve from
226 self.smtphunt = [] # Hosts to forward to
227 self.fetchdomains = [] # Domains to fetch from
228 self.smtpaddress = None # Append this to MAIL FROM line
229 self.smtpname = None # Use this for RCPT TO
230 self.preconnect = None # Connection setup
231 self.postconnect = None # Connection wrapup
232 self.mda = None # Mail Delivery Agent
233 self.bsmtp = None # BSMTP output file
234 self.lmtp = FALSE # Use LMTP rather than SMTP?
235 self.antispam = "" # Listener's spam-block code
236 self.keep = FALSE # Keep messages
237 self.flush = FALSE # Flush messages
238 self.fetchall = FALSE # Fetch old messages
239 self.rewrite = TRUE # Rewrite message headers
240 self.forcecr = FALSE # Force LF -> CR/LF
241 self.stripcr = FALSE # Strip CR
242 self.pass8bits = FALSE # Force BODY=7BIT
243 self.mimedecode = FALSE # Undo MIME armoring
244 self.dropstatus = FALSE # Drop incoming Status lines
245 self.dropdelivered = FALSE # Drop incoming Delivered-To lines
246 self.idle = FALSE # IDLE after poll
247 self.limit = 0 # Message size limit
248 self.warnings = 3600 # Size warning interval (see tunable.h)
249 self.fetchlimit = 0 # Max messages fetched per batch
250 self.fetchsizelimit = 100 # Max message sizes fetched per transaction
251 self.fastuidl = 10 # Do fast uidl 9 out of 10 times
252 self.batchlimit = 0 # Max message forwarded per batch
253 self.expunge = 0 # Interval between expunges (IMAP)
254 self.ssl = 0 # Enable Seccure Socket Layer
255 self.sslkey = None # SSL key filename
256 self.sslcert = None # SSL certificate filename
257 self.sslproto = None # Force SSL?
258 self.sslcertck = 0 # Enable strict SSL cert checking
259 self.sslcertpath = None # Path to trusted certificates
260 self.sslfingerprint = None # SSL key fingerprint to check
261 self.properties = None # Extension properties
263 ('remote', 'String'),
264 # leave out mailboxes and localnames
265 ('password', 'String'),
266 # Leave out smtphunt, fetchdomains
267 ('smtpaddress', 'String'),
268 ('smtpname', 'String'),
269 ('preconnect', 'String'),
270 ('postconnect', 'String'),
274 ('antispam', 'String'),
276 ('flush', 'Boolean'),
277 ('fetchall', 'Boolean'),
278 ('rewrite', 'Boolean'),
279 ('forcecr', 'Boolean'),
280 ('stripcr', 'Boolean'),
281 ('pass8bits', 'Boolean'),
282 ('mimedecode', 'Boolean'),
283 ('dropstatus', 'Boolean'),
284 ('dropdelivered', 'Boolean'),
288 ('fetchlimit', 'Int'),
289 ('fetchsizelimit', 'Int'),
291 ('batchlimit', 'Int'),
294 ('sslkey', 'String'),
295 ('sslcert', 'String'),
296 ('sslcertck', 'Boolean'),
297 ('sslcertpath', 'String'),
298 ('sslfingerprint', 'String'),
299 ('properties', 'String'))
303 res = res + "user " + `self.remote` + " there ";
305 res = res + "with password " + `self.password` + " "
308 for x in self.localnames:
309 res = res + " " + `x`
311 if (self.keep != UserDefaults.keep
312 or self.flush != UserDefaults.flush
313 or self.fetchall != UserDefaults.fetchall
314 or self.rewrite != UserDefaults.rewrite
315 or self.forcecr != UserDefaults.forcecr
316 or self.stripcr != UserDefaults.stripcr
317 or self.pass8bits != UserDefaults.pass8bits
318 or self.mimedecode != UserDefaults.mimedecode
319 or self.dropstatus != UserDefaults.dropstatus
320 or self.dropdelivered != UserDefaults.dropdelivered
321 or self.idle != UserDefaults.idle):
322 res = res + " options"
323 if self.keep != UserDefaults.keep:
324 res = res + flag2str(self.keep, 'keep')
325 if self.flush != UserDefaults.flush:
326 res = res + flag2str(self.flush, 'flush')
327 if self.fetchall != UserDefaults.fetchall:
328 res = res + flag2str(self.fetchall, 'fetchall')
329 if self.rewrite != UserDefaults.rewrite:
330 res = res + flag2str(self.rewrite, 'rewrite')
331 if self.forcecr != UserDefaults.forcecr:
332 res = res + flag2str(self.forcecr, 'forcecr')
333 if self.stripcr != UserDefaults.stripcr:
334 res = res + flag2str(self.stripcr, 'stripcr')
335 if self.pass8bits != UserDefaults.pass8bits:
336 res = res + flag2str(self.pass8bits, 'pass8bits')
337 if self.mimedecode != UserDefaults.mimedecode:
338 res = res + flag2str(self.mimedecode, 'mimedecode')
339 if self.dropstatus != UserDefaults.dropstatus:
340 res = res + flag2str(self.dropstatus, 'dropstatus')
341 if self.dropdelivered != UserDefaults.dropdelivered:
342 res = res + flag2str(self.dropdelivered, 'dropdelivered')
343 if self.idle != UserDefaults.idle:
344 res = res + flag2str(self.idle, 'idle')
345 if self.limit != UserDefaults.limit:
346 res = res + " limit " + `self.limit`
347 if self.warnings != UserDefaults.warnings:
348 res = res + " warnings " + `self.warnings`
349 if self.fetchlimit != UserDefaults.fetchlimit:
350 res = res + " fetchlimit " + `self.fetchlimit`
351 if self.fetchsizelimit != UserDefaults.fetchsizelimit:
352 res = res + " fetchsizelimit " + `self.fetchsizelimit`
353 if self.fastuidl != UserDefaults.fastuidl:
354 res = res + " fastuidl " + `self.fastuidl`
355 if self.batchlimit != UserDefaults.batchlimit:
356 res = res + " batchlimit " + `self.batchlimit`
357 if self.ssl and self.ssl != UserDefaults.ssl:
358 res = res + flag2str(self.ssl, 'ssl')
359 if self.sslkey and self.sslkey != UserDefaults.sslkey:
360 res = res + " sslkey " + `self.sslkey`
361 if self.sslcert and self.sslcert != UserDefaults.sslcert:
362 res = res + " sslcert " + `self.sslcert`
363 if self.sslproto and self.sslproto != UserDefaults.sslproto:
364 res = res + " sslproto " + `self.sslproto`
365 if self.sslcertck and self.sslcertck != UserDefaults.sslcertck:
366 res = res + flag2str(self.sslcertck, 'sslcertck')
367 if self.sslcertpath and self.sslcertpath != UserDefaults.sslcertpath:
368 res = res + " sslcertpath " + `self.sslcertpath`
369 if self.sslfingerprint and self.sslfingerprint != UserDefaults.sslfingerprint:
370 res = res + " sslfingerprint " + `self.sslfingerprint`
371 if self.expunge != UserDefaults.expunge:
372 res = res + " expunge " + `self.expunge`
374 trimmed = self.smtphunt;
375 if trimmed != [] and trimmed[len(trimmed) - 1] == "localhost":
376 trimmed = trimmed[0:len(trimmed) - 1]
377 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
378 trimmed = trimmed[0:len(trimmed) - 1]
380 res = res + " smtphost "
384 trimmed = self.fetchdomains
385 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
386 trimmed = trimmed[0:len(trimmed) - 1]
388 res = res + " fetchdomains "
393 res = res + " folder"
394 for x in self.mailboxes:
397 for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'):
398 if getattr(self, fld):
399 res = res + " %s %s\n" % (fld, `getattr(self, fld)`)
400 if self.lmtp != UserDefaults.lmtp:
401 res = res + flag2str(self.lmtp, 'lmtp')
402 if self.antispam != UserDefaults.antispam:
403 res = res + " antispam " + self.antispam + "\n"
407 return "[User: " + repr(self) + "]"
413 # IANA port assignments and bogus 1109 entry
414 ianaservices = {"pop2":109,
421 # fetchmail protocol to IANA service name
422 defaultports = {"auto":None,
431 authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp")
434 'title' : 'List Selection Help',
435 'banner': 'List Selection',
437 You must select an item in the list box (by clicking on it).
440 def flag2str(value, string):
441 # make a string representation of a .fetchmailrc flag or negated flag
445 if value == FALSE: str = str + ("no ")
449 class LabeledEntry(Frame):
450 # widget consisting of entry field with caption to left
451 def bind(self, key, action):
452 self.E.bind(key, action)
455 def __init__(self, Master, text, textvar, lwidth, ewidth=12):
456 Frame.__init__(self, Master)
457 self.L = Label(self, {'text':text, 'width':lwidth, 'anchor':'w'})
458 self.E = Entry(self, {'textvar':textvar, 'width':ewidth})
459 self.L.pack({'side':'left'})
460 self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
462 def ButtonBar(frame, legend, ref, alternatives, depth, command):
463 # array of radio buttons, caption to left, picking from a string list
465 width = (len(alternatives)+1) / depth;
466 Label(bar, text=legend).pack(side=LEFT)
467 for column in range(width):
468 subframe = Frame(bar)
469 for row in range(depth):
470 ind = width * row + column
471 if ind < len(alternatives):
472 Radiobutton(subframe,
473 {'text':alternatives[ind],
475 'value':alternatives[ind],
476 'command':command}).pack(side=TOP, anchor=W)
478 # This is just a spacer
479 Radiobutton(subframe,
480 {'text':" ",'state':DISABLED}).pack(side=TOP, anchor=W)
481 subframe.pack(side=LEFT)
485 def helpwin(helpdict):
486 # help message window with a self-destruct button
488 helpwin.title(helpdict['title'])
489 helpwin.iconname(helpdict['title'])
490 Label(helpwin, text=helpdict['banner']).pack()
491 textframe = Frame(helpwin)
492 scroll = Scrollbar(textframe)
493 helpwin.textwidget = Text(textframe, setgrid=TRUE)
494 textframe.pack(side=TOP, expand=YES, fill=BOTH)
495 helpwin.textwidget.config(yscrollcommand=scroll.set)
496 helpwin.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
497 scroll.config(command=helpwin.textwidget.yview)
498 scroll.pack(side=RIGHT, fill=BOTH)
499 helpwin.textwidget.insert(END, helpdict['text']);
500 Button(helpwin, text='Done',
501 command=lambda x=helpwin: x.destroy(), bd=2).pack()
502 textframe.pack(side=TOP)
504 def make_icon_window(base, image):
506 # Some older pythons will error out on this
507 icon_image = PhotoImage(data=image)
508 icon_window = Toplevel()
509 Label(icon_window, image=icon_image, bg='black').pack()
510 base.master.iconwindow(icon_window)
511 # Avoid TkInter brain death. PhotoImage objects go out of
512 # scope when the enclosing function returns. Therefore
513 # we have to explicitly link them to something.
514 base.keepalive.append(icon_image)
518 class ListEdit(Frame):
519 # edit a list of values (duplicates not allowed) with a supplied editor hook
520 def __init__(self, newlegend, list, editor, deletor, master, helptxt):
522 self.deletor = deletor
525 # Set up a widget to accept new elements
526 self.newval = StringVar(master)
527 newwin = LabeledEntry(master, newlegend, self.newval, '12')
528 newwin.bind('<Double-1>', self.handleNew)
529 newwin.bind('<Return>', self.handleNew)
530 newwin.pack(side=TOP, fill=X, anchor=E)
532 # Edit the existing list
533 listframe = Frame(master)
534 scroll = Scrollbar(listframe)
535 self.listwidget = Listbox(listframe, height=0, selectmode='browse')
538 self.listwidget.insert(END, x)
539 listframe.pack(side=TOP, expand=YES, fill=BOTH)
540 self.listwidget.config(yscrollcommand=scroll.set)
541 self.listwidget.pack(side=LEFT, expand=YES, fill=BOTH)
542 scroll.config(command=self.listwidget.yview)
543 scroll.pack(side=RIGHT, fill=BOTH)
544 self.listwidget.config(selectmode=SINGLE, setgrid=TRUE)
545 self.listwidget.bind('<Double-1>', self.handleList);
546 self.listwidget.bind('<Return>', self.handleList);
550 Button(bf, text='Edit', command=self.editItem).pack(side=LEFT)
551 Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT)
553 self.helptxt = helptxt
554 Button(bf, text='Help', fg='blue',
555 command=self.help).pack(side=RIGHT)
559 helpwin(self.helptxt)
561 def handleList(self, event):
564 def handleNew(self, event):
565 item = self.newval.get()
567 entire = self.listwidget.get(0, self.listwidget.index('end'));
568 if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
569 self.listwidget.insert('end', item)
570 if self.list != None: self.list.append(item)
572 apply(self.editor, (item,))
576 select = self.listwidget.curselection()
581 if index and self.editor:
582 label = self.listwidget.get(index);
584 apply(self.editor, (label,))
586 def deleteItem(self):
587 select = self.listwidget.curselection()
591 index = string.atoi(select[0])
592 label = self.listwidget.get(index);
593 self.listwidget.delete(index)
594 if self.list != None:
596 if self.deletor != None:
597 apply(self.deletor, (label,))
599 def ConfirmQuit(frame, context):
602 text = 'Really quit ' + context + ' without saving?',
604 strings = ('Yes', 'No'),
608 def dispose_window(master, legend, help, savelegend='OK'):
609 dispose = Frame(master, relief=RAISED, bd=5)
610 Label(dispose, text=legend).pack(side=TOP,pady=10)
611 Button(dispose, text=savelegend, fg='blue',
612 command=master.save).pack(side=LEFT)
613 Button(dispose, text='Quit', fg='blue',
614 command=master.nosave).pack(side=LEFT)
615 Button(dispose, text='Help', fg='blue',
616 command=lambda x=help: helpwin(x)).pack(side=RIGHT)
621 # Common methods for Tkinter widgets -- deals with Tkinter declaration
622 def post(self, widgetclass, field):
623 for x in widgetclass.typemap:
624 if x[1] == 'Boolean':
625 setattr(self, x[0], BooleanVar(self))
626 elif x[1] == 'String':
627 setattr(self, x[0], StringVar(self))
629 setattr(self, x[0], IntVar(self))
630 source = getattr(getattr(self, field), x[0])
632 getattr(self, x[0]).set(source)
634 def fetch(self, widgetclass, field):
635 for x in widgetclass.typemap:
636 setattr(getattr(self, field), x[0], getattr(self, x[0]).get())
639 # First, code to set the global fetchmail run controls.
642 configure_novice_help = {
643 'title' : 'Fetchmail novice configurator help',
644 'banner': 'Novice configurator help',
646 In the `Novice Configurator Controls' panel, you can:
648 Press `Save' to save the new fetchmail configuration you have created.
649 Press `Quit' to exit without saving.
650 Press `Help' to bring up this help message.
652 In the `Novice Configuration' panels, you will set up the basic data
653 needed to create a simple fetchmail setup. These include:
655 1. The name of the remote site you want to query.
657 2. Your login name on that site.
659 3. Your password on that site.
661 4. A protocol to use (POP, IMAP, ETRN, etc.)
663 5. A polling interval.
665 6. Options to fetch old messages as well as new, uor to suppress
666 deletion of fetched message.
668 The novice-configuration code will assume that you want to forward mail
669 to a local sendmail listener with no special options.
672 configure_expert_help = {
673 'title' : 'Fetchmail expert configurator help',
674 'banner': 'Expert configurator help',
676 In the `Expert Configurator Controls' panel, you can:
678 Press `Save' to save the new fetchmail configuration you have edited.
679 Press `Quit' to exit without saving.
680 Press `Help' to bring up this help message.
682 In the `Run Controls' panel, you can set the following options that
683 control how fetchmail runs:
686 Number of seconds to wait between polls in the background.
687 If zero, fetchmail will run in foreground.
690 If empty, emit progress and error messages to stderr.
691 Otherwise this gives the name of the files to write to.
692 This field is ignored if the "Log to syslog?" option is on.
695 If empty, store seen-message IDs in .fetchids under user's home
696 directory. If nonempty, use given file name.
699 Who to send multidrop mail to as a last resort if no address can
700 be matched. Normally empty; in this case, fetchmail treats the
701 invoking user as the address of last resort unless that user is
702 root. If that user is root, fetchmail sends to `postmaster'.
705 If this option is on (the default) error mail goes to the sender.
706 Otherwise it goes to the postmaster.
709 If this option is on, spam bounces are sent to the sender or
710 postmaster (depending on the "Bounces to sender?" option. Otherwise,
711 spam bounces are not sent (the default).
714 If false (the default) fetchmail generates a Received line into
715 each message and generates a HELO from the machine it is running on.
716 If true, fetchmail generates no Received line and HELOs as if it were
719 In the `Remote Mail Configurations' panel, you can:
721 1. Enter the name of a new remote mail server you want fetchmail to query.
723 To do this, simply enter a label for the poll configuration in the
724 `New Server:' box. The label should be a DNS name of the server (unless
725 you are using ssh or some other tunneling method and will fill in the `via'
726 option on the site configuration screen).
728 2. Change the configuration of an existing site.
730 To do this, find the site's label in the listbox and double-click it.
731 This will take you to a site configuration dialogue.
735 class ConfigurationEdit(Frame, MyWidget):
736 def __init__(self, configuration, outfile, master, onexit):
738 self.configuration = configuration
739 self.outfile = outfile
740 self.container = master
742 ConfigurationEdit.mode_to_help = {
743 'novice':configure_novice_help, 'expert':configure_expert_help
746 def server_edit(self, sitename):
747 self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
749 def server_delete(self, sitename):
751 for user in self.subwidgets.keys():
753 del self.configuration[sitename]
757 def edit(self, mode):
759 Frame.__init__(self, self.container)
760 self.master.title('fetchmail ' + self.mode + ' configurator');
761 self.master.iconname('fetchmail ' + self.mode + ' configurator');
762 self.master.protocol('WM_DELETE_WINDOW', self.nosave)
763 self.keepalive = [] # Use this to anchor the PhotoImage object
764 make_icon_window(self, fetchmail_icon)
766 self.post(Configuration, 'configuration')
769 'Configurator ' + self.mode + ' Controls',
770 ConfigurationEdit.mode_to_help[self.mode],
773 gf = Frame(self, relief=RAISED, bd = 5)
775 text='Fetchmail Run Controls',
776 bd=2).pack(side=TOP, pady=10)
781 if self.mode != 'novice':
783 log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
784 log.pack(side=RIGHT, anchor=E)
786 # Set the poll interval
787 de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
788 de.pack(side=RIGHT, anchor=E)
793 if self.mode != 'novice':
796 {'text':'Bounces to sender?',
797 'variable':self.bouncemail,
798 'relief':GROOVE}).pack(side=LEFT, anchor=W)
803 {'text':'send spam bounces?',
804 'variable':self.spambounce,
805 'relief':GROOVE}).pack(side=LEFT, anchor=W)
810 {'text':'Log to syslog?',
811 'variable':self.syslog,
812 'relief':GROOVE}).pack(side=LEFT, anchor=W)
813 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
814 log.pack(side=RIGHT, anchor=E)
818 {'text':'Invisible mode?',
819 'variable':self.invisible,
820 'relief':GROOVE}).pack(side=LEFT, anchor=W)
822 log = LabeledEntry(gf, ' Idfile:', self.idfile, '14')
823 log.pack(side=RIGHT, anchor=E)
827 # Expert mode allows us to edit multiple sites
828 lf = Frame(self, relief=RAISED, bd=5)
830 text='Remote Mail Server Configurations',
831 bd=2).pack(side=TOP, pady=10)
832 ListEdit('New Server:',
833 map(lambda x: x.pollname, self.configuration.servers),
834 lambda site, self=self: self.server_edit(site),
835 lambda site, self=self: self.server_delete(site),
840 for sitename in self.subwidgets.keys():
841 self.subwidgets[sitename].destruct()
842 self.master.destroy()
846 if ConfirmQuit(self, self.mode + " configuration editor"):
850 for sitename in self.subwidgets.keys():
851 self.subwidgets[sitename].save()
852 self.fetch(Configuration, 'configuration')
856 elif not os.path.isfile(self.outfile) or Dialog(self,
857 title = 'Overwrite existing run control file?',
858 text = 'Really overwrite existing run control file?',
860 strings = ('Yes', 'No'),
861 default = 1).num == 0:
863 os.rename(self.outfile, self.outfile + "~")
864 # Pre-1.5.2 compatibility...
867 fm = open(self.outfile, 'w')
869 fm.write("# Configuration created %s by fetchmailconf\n" % time.ctime(time.time()))
870 fm.write(`self.configuration`)
874 os.chmod(self.outfile, 0600)
878 # Server editing stuff.
881 'title' : 'Remote site help',
882 'banner': 'Remote sites',
884 When you add a site name to the list here,
885 you initialize an entry telling fetchmail
886 how to poll a new site.
888 When you select a sitename (by double-
889 clicking it, or by single-clicking to
890 select and then clicking the Edit button),
891 you will open a window to configure that
896 'title' : 'Server options help',
897 'banner': 'Server Options',
899 The server options screen controls fetchmail
900 options that apply to one of your mailservers.
902 Once you have a mailserver configuration set
903 up as you like it, you can select `OK' to
904 store it in the server list maintained in
905 the main configuration window.
907 If you wish to discard changes to a server
908 configuration, select `Quit'.
912 'title' : 'Run Control help',
913 'banner': 'Run Controls',
915 If the `Poll normally' checkbox is on, the host is polled as part of
916 the normal operation of fetchmail when it is run with no arguments.
917 If it is off, fetchmail will only query this host when it is given as
918 a command-line argument.
920 The `True name of server' box should specify the actual DNS name
921 to query. By default this is the same as the poll name.
923 Normally each host described in the file is queried once each
924 poll cycle. If `Cycles to skip between polls' is greater than 0,
925 that's the number of poll cycles that are skipped between the
926 times this post is actually polled.
928 The `Server timeout' is the number of seconds fetchmail will wait
929 for a reply from the mailserver before concluding it is hung and
934 'title' : 'Protocol and Port help',
935 'banner': 'Protocol and Port',
937 These options control the remote-mail protocol
938 and TCP/IP service port used to query this
941 If you click the `Probe for supported protocols'
942 button, fetchmail will try to find you the most
943 capable server on the selected host (this will
944 only work if you're conncted to the Internet).
945 The probe only checks for ordinary IMAP and POP
946 protocols; fortunately these are the most
947 frequently supported.
949 The `Protocol' button bar offers you a choice of
950 all the different protocols available. The `auto'
951 protocol is the default mode; it probes the host
952 ports for POP3 and IMAP to see if either is
955 Normally the TCP/IP service port to use is
956 dictated by the protocol choice. The `Service'
957 field (only present in expert mode) lets you
958 set a non-standard service (port).
962 'title' : 'Security option help',
963 'banner': 'Security',
965 The `interface' option allows you to specify a range
966 of IP addresses to monitor for activity. If these
967 addresses are not active, fetchmail will not poll.
968 Specifying this may protect you from a spoofing attack
969 if your client machine has more than one IP gateway
970 address and some of the gateways are to insecure nets.
972 The `monitor' option, if given, specifies the only
973 device through which fetchmail is permitted to connect
974 to servers. This option may be used to prevent
975 fetchmail from triggering an expensive dial-out if the
976 interface is not already active.
978 The `interface' and `monitor' options are available
979 only for Linux and freeBSD systems. See the fetchmail
980 manual page for details on these.
982 The ssl option enables SSL communication with a mailserver
983 supporting Secure Sockets Layer. The sslkey and sslcert options
984 declare key and certificate files for use with SSL.
985 The sslcertck option enables strict checking of SSL server
986 certificates (and sslcertpath gives trusted certificate
987 directory). With sslfingerprint, you can specify a finger-
988 print the server's key is checked against.
992 'title' : 'Multidrop option help',
993 'banner': 'Multidrop',
995 These options are only useful with multidrop mode.
996 See the manual page for extended discussion.
1000 'title' : 'User list help',
1001 'banner': 'User list',
1003 When you add a user name to the list here,
1004 you initialize an entry telling fetchmail
1005 to poll the site on behalf of the new user.
1007 When you select a username (by double-
1008 clicking it, or by single-clicking to
1009 select and then clicking the Edit button),
1010 you will open a window to configure the
1011 user's options on that site.
1014 class ServerEdit(Frame, MyWidget):
1015 def __init__(self, host, parent):
1016 self.parent = parent
1018 self.subwidgets = {}
1019 for site in parent.configuration.servers:
1020 if site.pollname == host:
1022 if (self.server == None):
1023 self.server = Server()
1024 self.server.pollname = host
1025 self.server.via = None
1026 parent.configuration.servers.append(self.server)
1028 def edit(self, mode, master=None):
1029 Frame.__init__(self, master)
1031 self.master.title('Fetchmail host ' + self.server.pollname);
1032 self.master.iconname('Fetchmail host ' + self.server.pollname);
1033 self.post(Server, 'server')
1034 self.makeWidgets(self.server.pollname, mode)
1035 self.keepalive = [] # Use this to anchor the PhotoImage object
1036 make_icon_window(self, fetchmail_icon)
1039 # self.wait_window()
1043 for username in self.subwidgets.keys():
1044 self.subwidgets[username].destruct()
1045 del self.parent.subwidgets[self.server.pollname]
1046 self.master.destroy()
1049 if ConfirmQuit(self, 'server option editing'):
1053 self.fetch(Server, 'server')
1054 for username in self.subwidgets.keys():
1055 self.subwidgets[username].save()
1058 def defaultPort(self):
1059 proto = self.protocol.get()
1060 # Callback to reset the port number whenever the protocol type changes.
1061 # We used to only reset the port if it had a default (zero) value.
1062 # This turns out to be a bad idea especially in Novice mode -- if
1063 # you set POP3 and then set IMAP, the port invisibly remained 110.
1064 # Now we reset unconditionally on the theory that if you're setting
1065 # a custom port number you should be in expert mode and playing
1066 # close enough attention to notice this...
1067 self.service.set(defaultports[proto])
1068 if not proto in ("POP3", "APOP", "KPOP"): self.uidl.state = DISABLED
1070 def user_edit(self, username, mode):
1071 self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1073 def user_delete(self, username):
1074 if self.subwidgets.has_key(username):
1075 self.subwidgets[username].destruct()
1076 del self.server[username]
1078 def makeWidgets(self, host, mode):
1079 topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1081 leftwin = Frame(self);
1084 if mode != 'novice':
1085 ctlwin = Frame(leftwin, relief=RAISED, bd=5)
1086 Label(ctlwin, text="Run Controls").pack(side=TOP)
1087 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
1088 LabeledEntry(ctlwin, 'True name of ' + host + ':',
1089 self.via, leftwidth).pack(side=TOP, fill=X)
1090 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
1091 self.interval, leftwidth).pack(side=TOP, fill=X)
1092 LabeledEntry(ctlwin, 'Server timeout (seconds):',
1093 self.timeout, leftwidth).pack(side=TOP, fill=X)
1094 Button(ctlwin, text='Help', fg='blue',
1095 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
1098 # Compute the available protocols from the compile-time options
1099 protolist = ['auto']
1100 if 'pop2' in feature_options:
1101 protolist.append("POP2")
1102 if 'pop3' in feature_options:
1103 protolist = protolist + ["POP3", "APOP", "KPOP"]
1104 if 'sdps' in feature_options:
1105 protolist.append("SDPS")
1106 if 'imap' in feature_options:
1107 protolist.append("IMAP")
1108 if 'etrn' in feature_options:
1109 protolist.append("ETRN")
1110 if 'odmr' in feature_options:
1111 protolist.append("ODMR")
1113 protwin = Frame(leftwin, relief=RAISED, bd=5)
1114 Label(protwin, text="Protocol").pack(side=TOP)
1115 ButtonBar(protwin, '',
1116 self.protocol, protolist, 2,
1118 if mode != 'novice':
1119 LabeledEntry(protwin, 'On server TCP/IP service:',
1120 self.service, leftwidth).pack(side=TOP, fill=X)
1122 Checkbutton(protwin,
1123 text="POP3: track `seen' with client-side UIDLs?",
1124 variable=self.uidl).pack(side=TOP)
1125 Button(protwin, text='Probe for supported protocols', fg='blue',
1126 command=self.autoprobe).pack(side=LEFT)
1127 Button(protwin, text='Help', fg='blue',
1128 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
1129 protwin.pack(fill=X)
1131 userwin = Frame(leftwin, relief=RAISED, bd=5)
1132 Label(userwin, text="User entries for " + host).pack(side=TOP)
1133 ListEdit("New user: ",
1134 map(lambda x: x.remote, self.server.users),
1135 lambda u, m=mode, s=self: s.user_edit(u, m),
1136 lambda u, s=self: s.user_delete(u),
1138 userwin.pack(fill=X)
1140 leftwin.pack(side=LEFT, anchor=N, fill=X);
1142 if mode != 'novice':
1143 rightwin = Frame(self);
1145 mdropwin = Frame(rightwin, relief=RAISED, bd=5)
1146 Label(mdropwin, text="Multidrop options").pack(side=TOP)
1147 LabeledEntry(mdropwin, 'Envelope address header:',
1148 self.envelope, '22').pack(side=TOP, fill=X)
1149 LabeledEntry(mdropwin, 'Envelope headers to skip:',
1150 self.envskip, '22').pack(side=TOP, fill=X)
1151 LabeledEntry(mdropwin, 'Name prefix to strip:',
1152 self.qvirtual, '22').pack(side=TOP, fill=X)
1153 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
1154 variable=self.dns).pack(side=TOP)
1155 Label(mdropwin, text="DNS aliases").pack(side=TOP)
1156 ListEdit("New alias: ", self.server.aka, None, None, mdropwin, None)
1157 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
1158 ListEdit("New domain: ",
1159 self.server.localdomains, None, None, mdropwin, multihelp)
1160 mdropwin.pack(fill=X)
1162 if os_type in ('linux', 'freebsd'):
1163 secwin = Frame(rightwin, relief=RAISED, bd=5)
1164 Label(secwin, text="Security").pack(side=TOP)
1165 # Don't actually let users set this. KPOP sets it implicitly
1166 # ButtonBar(secwin, 'Authorization mode:',
1167 # self.auth, authlist, 1, None).pack(side=TOP)
1168 if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
1169 LabeledEntry(secwin, 'IP range to check before poll:',
1170 self.interface, leftwidth).pack(side=TOP, fill=X)
1171 if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
1172 LabeledEntry(secwin, 'Interface to monitor:',
1173 self.monitor, leftwidth).pack(side=TOP, fill=X)
1174 # Someday this should handle Kerberos 5 too
1175 if 'kerberos' in feature_options:
1176 LabeledEntry(secwin, 'Principal:',
1177 self.principal, '12').pack(side=TOP, fill=X)
1178 # ESMTP authentication
1179 LabeledEntry(secwin, 'ESMTP name:',
1180 self.esmtpname, '12').pack(side=TOP, fill=X)
1181 LabeledEntry(secwin, 'ESMTP password:',
1182 self.esmtppassword, '12').pack(side=TOP, fill=X)
1183 Button(secwin, text='Help', fg='blue',
1184 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
1187 rightwin.pack(side=LEFT, anchor=N);
1189 def autoprobe(self):
1190 # Note: this only handles case (1) near fetchmail.c:1032
1191 # We're assuming people smart enough to set up ssh tunneling
1192 # won't need autoprobing.
1194 realhost = self.server.via
1196 realhost = self.server.pollname
1198 for protocol in ("IMAP","POP3","POP2"):
1199 service = defaultports[protocol]
1200 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1202 sock.connect((realhost, ianaservices[service]))
1203 greetline = sock.recv(1024)
1209 confwin = Toplevel()
1210 if greetline == None:
1211 title = "Autoprobe of " + realhost + " failed"
1213 Fetchmailconf didn't find any mailservers active.
1214 This could mean the host doesn't support any,
1215 or that your Internet connection is down, or
1216 that the host is so slow that the probe timed
1217 out before getting a response.
1221 # OK, now try to recognize potential problems
1223 if protocol == "POP2":
1224 warnings = warnings + """
1225 It appears you have somehow found a mailserver running only POP2.
1226 Congratulations. Have you considered a career in archaeology?
1228 Unfortunately, stock fetchmail binaries don't include POP2 support anymore.
1229 Unless the first line of your fetchmail -V output includes the string "POP2",
1230 you'll have to build it from sources yourself with the configure
1231 switch --enable-POP2.
1235 ### POP3 servers start here
1237 if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
1238 warnings = warnings + """
1239 This appears to be an old version of the UC Davis POP server. These are
1240 dangerously unreliable (among other problems, they may drop your mailbox
1241 on the floor if your connection is interrupted during the session).
1243 It is strongly recommended that you find a better POP3 server. The fetchmail
1244 FAQ includes pointers to good ones.
1247 if string.find(greetline, "comcast.net") > 0:
1248 warnings = warnings + """
1249 The Comcast Maillennium POP3 server only returns the first 80K of a long
1250 message retrieved with TOP. Its response to RETR is normal, so use the
1254 # Steve VanDevender <stevev@efn.org> writes:
1255 # The only system I have seen this happen with is cucipop-1.31
1256 # under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
1257 # 2.x and probably quite a few other systems. It appears to be a
1258 # bug or bad interaction with the SunOS realloc() -- it turns out
1259 # that internally cucipop does allocate a certain data structure in
1260 # multiples of 16, using realloc() to bump it up to the next
1261 # multiple if it needs more.
1263 # The distinctive symptom is that when there are 16 messages in the
1264 # inbox, you can RETR and DELE all 16 messages successfully, but on
1265 # QUIT cucipop returns something like "-ERR Error locking your
1266 # mailbox" and aborts without updating it.
1268 # The cucipop banner looks like:
1270 # +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1272 if string.find(greetline, "Cubic Circle") > 0:
1273 warnings = warnings + """
1274 I see your server is running cucipop. Better make sure the server box
1275 isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
1276 under that version, and doesn't cope with the result gracefully. Newer
1277 SunOS and Solaris machines run cucipop OK.
1279 Also, some versions of cucipop don't assert an exclusive lock on your
1280 mailbox when it's being queried. This means that if you have more than
1281 one fetchmail query running against the same mailbox, bad things can happen.
1283 if string.find(greetline, "David POP3 Server") > 0:
1284 warnings = warnings + """
1285 This POP3 server is badly broken. You should get rid of it -- and the
1286 brain-dead Microsoft operating system it rode in on.
1289 # The greeting line on the server known to be buggy is:
1290 # +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
1292 if string.find(greetline, "FTGate") > 0:
1293 warnings = warnings + """
1294 This POP server has a weird bug; it says OK twice in response to TOP.
1295 Its response to RETR is normal, so use the `fetchall' option.
1298 if string.find(greetline, " geonet.de") > 0:
1299 warnings = warnings + """
1300 You appear to be using geonet. As of late 2002, the TOP command on
1301 geonet's POP3 is broken. Use the fetchall option.
1304 if string.find(greetline, "OpenMail") > 0:
1305 warnings = warnings + """
1306 You appear to be using some version of HP OpenMail. Many versions of
1307 OpenMail do not process the "TOP" command correctly; the symptom is that
1308 only the header and first line of each message is retrieved. To work
1309 around this bug, turn on `fetchall' on all user entries associated with
1313 if string.find(greetline, "Escape character is") > 0:
1314 warnings = warnings + """
1315 Your greeting line looks like it was written by a fetid pile of
1316 camel dung identified to me as `popa3d written by Solar Designer'.
1317 Beware! The UIDL support in this thing is known to be completely broken,
1318 and other things probably are too.
1321 if string.find(greetline, "MercuryP/NLM v1.48") > 0:
1322 warnings = warnings + """
1323 This is not a POP3 server. It has delusions of being one, but after
1324 RETR all messages are automatically marked to be deleted. The only
1325 way to prevent this is to issue an RSET before leaving the server.
1326 Fetchmail does this, but we suspect this is probably broken in lots
1330 if string.find(greetline, "POP-Max") > 0:
1331 warnings = warnings + """
1332 The Mail Max POP3 server screws up on mail with attachments. It
1333 reports the message size with attachments included, but doesn't
1334 download them on a RETR or TOP (this violates the IMAP RFCs). It also
1335 doesn't implement TOP correctly. You should get rid of it -- and the
1336 brain-dead NT server it rode in on.
1339 if string.find(greetline, "POP3 Server Ready") > 0:
1340 warnings = warnings + """
1341 Some server that uses this greeting line has been observed to choke on
1342 TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
1345 if string.find(greetline, "QPOP") > 0:
1346 warnings = warnings + """
1347 This appears to be a version of Eudora qpopper. That's good. Fetchmail
1348 knows all about qpopper. However, be aware that the 2.53 version of
1349 qpopper does something odd that causes fetchmail to hang with a socket
1350 error on very large messages. This is probably not a fetchmail bug, as
1351 it has been observed with fetchpop. The fix is to upgrade to qpopper
1352 3.0beta or a more recent version. Better yet, switch to IMAP.
1355 if string.find(greetline, " sprynet.com") > 0:
1356 warnings = warnings + """
1357 You appear to be using a SpryNet server. In mid-1999 it was reported that
1358 the SpryNet TOP command marks messages seen. Therefore, for proper error
1359 recovery in the event of a line drop, it is strongly recommended that you
1360 turn on `fetchall' on all user entries associated with this server.
1363 if string.find(greetline, "TEMS POP3") > 0:
1364 warnings = warnings + """
1365 Your POP3 server has "TEMS" in its header line. At least one such
1366 server does not process the "TOP" command correctly; the symptom is
1367 that fetchmail hangs when trying to retrieve mail. To work around
1368 this bug, turn on `fetchall' on all user entries associated with this
1372 if string.find(greetline, " spray.se") > 0:
1373 warnings = warnings + """
1374 Your POP3 server has "spray.se" in its header line. In May 2000 at
1375 least one such server did not process the "TOP" command correctly; the
1376 symptom is that messages are treated as headerless. To work around
1377 this bug, turn on `fetchall' on all user entries associated with this
1381 if string.find(greetline, " usa.net") > 0:
1382 warnings = warnings + """
1383 You appear to be using USA.NET's free mail service. Their POP3 servers
1384 (at least as of the 2.2 version in use mid-1998) are quite flaky, but
1385 fetchmail can compensate. They seem to require that fetchall be switched on
1386 (otherwise you won't necessarily see all your mail, not even new mail).
1387 They also botch the TOP command the fetchmail normally uses for retrieval
1388 (it only retrieves about 10 lines rather than the number specified).
1389 Turning on fetchall will disable the use of TOP.
1391 Therefore, it is strongly recommended that you turn on `fetchall' on all
1392 user entries associated with this server.
1395 if string.find(greetline, " Novonyx POP3") > 0:
1396 warnings = warnings + """
1397 Your mailserver is running Novonyx POP3. This server, at least as of
1398 version 2.17, seems to have problems handling and reporting seen bits.
1399 You may have to use the fetchall option.
1402 if string.find(greetline, " IMS POP3") > 0:
1403 warnings = warnings + """
1404 Some servers issuing the greeting line 'IMS POP3' have been known to
1405 do byte-stuffing incorrectly. This means that if a message you receive
1406 has a . (period) at start of line, fetchmail will become confused and
1407 probably wedge itself. (This bug was recorded on IMS POP3 0.86.)
1411 ### IMAP servers start here
1413 if string.find(greetline, "GroupWise") > 0:
1414 warnings = warnings + """
1415 The Novell GroupWise IMAP server would be better named GroupFoolish;
1416 it is (according to the designer of IMAP) unusably broken. Among
1417 other things, it doesn't include a required content length in its
1418 BODY[TEXT] response.<p>
1420 Fetchmail works around this problem, but we strongly recommend voting
1421 with your dollars for a server that isn't brain-dead. If you stick
1422 with code as shoddy as GroupWise seems to be, you will probably pay
1423 for it with other problems.<p>
1426 if string.find(greetline, "Netscape IMAP4rev1 Service 3.6") > 0:
1427 warnings = warnings + """
1428 This server violates the RFC2060 requirement that a BODY[TEXT] fetch should
1429 set the messages's Seen flag. As a result, if you use the keep option the
1430 same messages will be downloaded over and over.
1433 if string.find(greetline, "InterChange") > 0:
1434 warnings = warnings + """
1436 The InterChange IMAP server at release levels below 3.61.08 screws up
1437 on mail with attachments. It doesn't fetch them if you give it a
1438 BODY[TEXT] request, though it does if you request RFC822.TEXT.
1439 According to the IMAP RFCs and their maintainer these should be
1440 equivalent -- and we can't drop the BODY[TEXT] form because M$
1441 Exchange (quite legally under RFC2062) rejectsit. The InterChange
1442 folks claim to have fixed this bug in 3.61.08.
1445 if string.find(greetline, "Imail") > 0:
1446 warnings = warnings + """
1447 We've seen a bug report indicating that this IMAP server (at least as of
1448 version 5.0.7) returns an invalid body size for messages with MIME
1449 attachments; the effect is to drop the attachments on the floor. We
1450 recommend you upgrade to a non-broken IMAP server.
1453 if string.find(greetline, "Domino IMAP4") > 0:
1454 warnings = warnings + """
1455 Your IMAP server appears to be Lotus Domino. This server, at least up
1456 to version 4.6.2a, has a bug in its generation of MIME boundaries (see
1457 the details in the fetchmail FAQ). As a result, even MIME aware MUAs
1458 will see attachments as part of the message text. If your Domino server's
1459 POP3 facility is enabled, we recommend you fall back on it.
1463 ### Checks for protocol variants start here
1465 closebrak = string.find(greetline, ">")
1466 if closebrak > 0 and greetline[closebrak+1] == "\r":
1467 warnings = warnings + """
1468 It looks like you could use APOP on this server and avoid sending it your
1469 password in clear. You should talk to the mailserver administrator about
1473 if string.find(greetline, "IMAP2bis") > 0:
1474 warnings = warnings + """
1475 IMAP2bis servers have a minor problem; they can't peek at messages without
1476 marking them seen. If you take a line hit during the retrieval, the
1477 interrupted message may get left on the server, marked seen.
1479 To work around this, it is recommended that you set the `fetchall'
1480 option on all user entries associated with this server, so any stuck
1481 mail will be retrieved next time around.
1483 To fix this bug, upgrade to an IMAP4 server. The fetchmail FAQ includes
1484 a pointer to an open-source implementation.
1487 if string.find(greetline, "IMAP4rev1") > 0:
1488 warnings = warnings + """
1489 I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
1490 remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
1491 has therefore been extremely well tested with this class of server.
1495 warnings = warnings + """
1496 Fetchmail doesn't know anything special about this server type.
1500 # Display success window with warnings
1501 title = "Autoprobe of " + realhost + " succeeded"
1502 confirm = "The " + protocol + " server said:\n\n" + greetline + warnings
1503 self.protocol.set(protocol)
1504 confwin.title(title)
1505 confwin.iconname(title)
1506 Label(confwin, text=title).pack()
1507 Message(confwin, text=confirm, width=600).pack()
1508 Button(confwin, text='Done',
1509 command=lambda x=confwin: x.destroy(), bd=2).pack()
1512 # User editing stuff
1516 'title' : 'User option help',
1517 'banner': 'User options',
1519 You may use this panel to set options
1520 that may differ between individual
1523 Once you have a user configuration set
1524 up as you like it, you can select `OK' to
1525 store it in the user list maintained in
1526 the site configuration window.
1528 If you wish to discard the changes you have
1529 made to user options, select `Quit'.
1533 'title' : 'Local name help',
1534 'banner': 'Local names',
1536 The local name(s) in a user entry are the
1537 people on the client machine who should
1538 receive mail from the poll described.
1540 Note: if a user entry has more than one
1541 local name, messages will be retrieved
1542 in multidrop mode. This complicates
1543 the configuration issues; see the manual
1544 page section on multidrop mode.
1546 Warning: Be careful with local names
1547 such as foo@bar.com, as that can cause
1548 the mail to be sent to foo@bar.com instead
1549 of sending it to your local system.
1552 class UserEdit(Frame, MyWidget):
1553 def __init__(self, username, parent):
1554 self.parent = parent
1556 for user in parent.server.users:
1557 if user.remote == username:
1559 if self.user == None:
1561 self.user.remote = username
1562 self.user.localnames = [username]
1563 parent.server.users.append(self.user)
1565 def edit(self, mode, master=None):
1566 Frame.__init__(self, master)
1568 self.master.title('Fetchmail user ' + self.user.remote
1569 + ' querying ' + self.parent.server.pollname);
1570 self.master.iconname('Fetchmail user ' + self.user.remote);
1571 self.post(User, 'user')
1572 self.makeWidgets(mode, self.parent.server.pollname)
1573 self.keepalive = [] # Use this to anchor the PhotoImage object
1574 make_icon_window(self, fetchmail_icon)
1577 # self.wait_window()
1581 # Yes, this test can fail -- if you delete the parent window.
1582 if self.parent.subwidgets.has_key(self.user.remote):
1583 del self.parent.subwidgets[self.user.remote]
1584 self.master.destroy()
1587 if ConfirmQuit(self, 'user option editing'):
1592 for x in self.user.localnames: ok = ok + (string.find(x, '@') != -1)
1593 if ok == 0 or Dialog(self,
1594 title = "Really accept an embedded '@' ?",
1595 text = "Local names with an embedded '@', such as in foo@bar "
1596 "might result in your mail being sent to foo@bar.com "
1597 "instead of your local system.\n Are you sure you want "
1598 "a local user name with an '@' in it?",
1599 bitmap = 'question',
1600 strings = ('Yes', 'No'),
1601 default = 1).num == 0:
1602 self.fetch(User, 'user')
1605 def makeWidgets(self, mode, servername):
1606 dispose_window(self,
1607 "User options for " + self.user.remote + " querying " + servername,
1610 if mode != 'novice':
1611 leftwin = Frame(self);
1615 secwin = Frame(leftwin, relief=RAISED, bd=5)
1616 Label(secwin, text="Authentication").pack(side=TOP)
1617 LabeledEntry(secwin, 'Password:',
1618 self.password, '12').pack(side=TOP, fill=X)
1619 secwin.pack(fill=X, anchor=N)
1621 if 'ssl' in feature_options or 'ssl' in dictmembers:
1622 sslwin = Frame(leftwin, relief=RAISED, bd=5)
1623 Checkbutton(sslwin, text="Use SSL?",
1624 variable=self.ssl).pack(side=TOP, fill=X)
1625 LabeledEntry(sslwin, 'SSL key:',
1626 self.sslkey, '14').pack(side=TOP, fill=X)
1627 LabeledEntry(sslwin, 'SSL certificate:',
1628 self.sslcert, '14').pack(side=TOP, fill=X)
1629 Checkbutton(sslwin, text="Check server SSL certificate?",
1630 variable=self.sslcertck).pack(side=TOP, fill=X)
1631 LabeledEntry(sslwin, 'SSL trusted certificate directory:',
1632 self.sslcertpath, '14').pack(side=TOP, fill=X)
1633 LabeledEntry(sslwin, 'SSL key fingerprint:',
1634 self.sslfingerprint, '14').pack(side=TOP, fill=X)
1635 sslwin.pack(fill=X, anchor=N)
1637 names = Frame(leftwin, relief=RAISED, bd=5)
1638 Label(names, text="Local names").pack(side=TOP)
1639 ListEdit("New name: ",
1640 self.user.localnames, None, None, names, localhelp)
1641 names.pack(fill=X, anchor=N)
1643 if mode != 'novice':
1644 targwin = Frame(leftwin, relief=RAISED, bd=5)
1645 Label(targwin, text="Forwarding Options").pack(side=TOP)
1646 Label(targwin, text="Listeners to forward to").pack(side=TOP)
1647 ListEdit("New listener:",
1648 self.user.smtphunt, None, None, targwin, None)
1649 Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
1650 ListEdit("Domains:",
1651 self.user.fetchdomains, None, None, targwin, None)
1652 LabeledEntry(targwin, 'Append to MAIL FROM line:',
1653 self.smtpaddress, '26').pack(side=TOP, fill=X)
1654 LabeledEntry(targwin, 'Set RCPT To address:',
1655 self.smtpname, '26').pack(side=TOP, fill=X)
1656 LabeledEntry(targwin, 'Connection setup command:',
1657 self.preconnect, '26').pack(side=TOP, fill=X)
1658 LabeledEntry(targwin, 'Connection wrapup command:',
1659 self.postconnect, '26').pack(side=TOP, fill=X)
1660 LabeledEntry(targwin, 'Local delivery agent:',
1661 self.mda, '26').pack(side=TOP, fill=X)
1662 LabeledEntry(targwin, 'BSMTP output file:',
1663 self.bsmtp, '26').pack(side=TOP, fill=X)
1664 LabeledEntry(targwin, 'Listener spam-block codes:',
1665 self.antispam, '26').pack(side=TOP, fill=X)
1666 LabeledEntry(targwin, 'Pass-through properties:',
1667 self.properties, '26').pack(side=TOP, fill=X)
1668 Checkbutton(targwin, text="Use LMTP?",
1669 variable=self.lmtp).pack(side=TOP, fill=X)
1670 targwin.pack(fill=X, anchor=N)
1672 if mode != 'novice':
1673 leftwin.pack(side=LEFT, fill=X, anchor=N)
1674 rightwin = Frame(self)
1678 optwin = Frame(rightwin, relief=RAISED, bd=5)
1679 Label(optwin, text="Processing Options").pack(side=TOP)
1680 Checkbutton(optwin, text="Suppress deletion of messages after reading",
1681 variable=self.keep).pack(side=TOP, anchor=W)
1682 Checkbutton(optwin, text="Fetch old messages as well as new",
1683 variable=self.fetchall).pack(side=TOP, anchor=W)
1684 if mode != 'novice':
1685 Checkbutton(optwin, text="Flush seen messages before retrieval",
1686 variable=self.flush).pack(side=TOP, anchor=W)
1687 Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply",
1688 variable=self.rewrite).pack(side=TOP, anchor=W)
1689 Checkbutton(optwin, text="Force CR/LF at end of each line",
1690 variable=self.forcecr).pack(side=TOP, anchor=W)
1691 Checkbutton(optwin, text="Strip CR from end of each line",
1692 variable=self.stripcr).pack(side=TOP, anchor=W)
1693 Checkbutton(optwin, text="Pass 8 bits even though SMTP says 7BIT",
1694 variable=self.pass8bits).pack(side=TOP, anchor=W)
1695 Checkbutton(optwin, text="Undo MIME armoring on header and body",
1696 variable=self.mimedecode).pack(side=TOP, anchor=W)
1697 Checkbutton(optwin, text="Drop Status lines from forwarded messages",
1698 variable=self.dropstatus).pack(side=TOP, anchor=W)
1699 Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
1700 variable=self.dropdelivered).pack(side=TOP, anchor=W)
1703 if mode != 'novice':
1704 limwin = Frame(rightwin, relief=RAISED, bd=5)
1705 Label(limwin, text="Resource Limits").pack(side=TOP)
1706 LabeledEntry(limwin, 'Message size limit:',
1707 self.limit, '30').pack(side=TOP, fill=X)
1708 LabeledEntry(limwin, 'Size warning interval:',
1709 self.warnings, '30').pack(side=TOP, fill=X)
1710 LabeledEntry(limwin, 'Max messages to fetch per poll:',
1711 self.fetchlimit, '30').pack(side=TOP, fill=X)
1712 LabeledEntry(limwin, 'Max message sizes to fetch per transaction:',
1713 self.fetchsizelimit, '30').pack(side=TOP, fill=X)
1714 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1715 LabeledEntry(limwin, 'Use fast UIDL:',
1716 self.fastuidl, '30').pack(side=TOP, fill=X)
1717 LabeledEntry(limwin, 'Max messages to forward per poll:',
1718 self.batchlimit, '30').pack(side=TOP, fill=X)
1719 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1720 LabeledEntry(limwin, 'Interval between expunges:',
1721 self.expunge, '30').pack(side=TOP, fill=X)
1722 Checkbutton(limwin, text="Idle after each poll (IMAP only)",
1723 variable=self.idle).pack(side=TOP, anchor=W)
1726 if self.parent.server.protocol == 'IMAP':
1727 foldwin = Frame(rightwin, relief=RAISED, bd=5)
1728 Label(foldwin, text="Remote folders (IMAP only)").pack(side=TOP)
1729 ListEdit("New folder:", self.user.mailboxes,
1730 None, None, foldwin, None)
1731 foldwin.pack(fill=X, anchor=N)
1733 if mode != 'novice':
1734 rightwin.pack(side=LEFT)
1740 # Top-level window that offers either novice or expert mode
1741 # (but not both at once; it disappears when one is selected).
1744 class Configurator(Frame):
1745 def __init__(self, outfile, master, onexit, parent):
1746 Frame.__init__(self, master)
1747 self.outfile = outfile
1748 self.onexit = onexit
1749 self.parent = parent
1750 self.master.title('fetchmail configurator');
1751 self.master.iconname('fetchmail configurator');
1753 self.keepalive = [] # Use this to anchor the PhotoImage object
1754 make_icon_window(self, fetchmail_icon)
1756 Message(self, text="""
1757 Use `Novice Configuration' for basic fetchmail setup;
1758 with this, you can easily set up a single-drop connection
1759 to one remote mail server.
1760 """, width=600).pack(side=TOP)
1761 Button(self, text='Novice Configuration',
1762 fg='blue', command=self.novice).pack()
1764 Message(self, text="""
1765 Use `Expert Configuration' for advanced fetchmail setup,
1766 including multiple-site or multidrop connections.
1767 """, width=600).pack(side=TOP)
1768 Button(self, text='Expert Configuration',
1769 fg='blue', command=self.expert).pack()
1771 Message(self, text="""
1772 Or you can just select `Quit' to leave the configurator now and
1773 return to the main panel.
1774 """, width=600).pack(side=TOP)
1775 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1776 master.protocol("WM_DELETE_WINDOW", self.leave)
1779 self.master.destroy()
1780 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1783 self.master.destroy()
1784 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1787 self.master.destroy()
1790 # Run a command in a scrolling text widget, displaying its output
1792 class RunWindow(Frame):
1793 def __init__(self, command, master, parent):
1794 Frame.__init__(self, master)
1795 self.master = master
1796 self.master.title('fetchmail run window');
1797 self.master.iconname('fetchmail run window');
1800 text="Running "+command,
1801 bd=2).pack(side=TOP, pady=10)
1802 self.keepalive = [] # Use this to anchor the PhotoImage object
1803 make_icon_window(self, fetchmail_icon)
1805 # This is a scrolling text window
1806 textframe = Frame(self)
1807 scroll = Scrollbar(textframe)
1808 self.textwidget = Text(textframe, setgrid=TRUE)
1809 textframe.pack(side=TOP, expand=YES, fill=BOTH)
1810 self.textwidget.config(yscrollcommand=scroll.set)
1811 self.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
1812 scroll.config(command=self.textwidget.yview)
1813 scroll.pack(side=RIGHT, fill=BOTH)
1814 textframe.pack(side=TOP)
1816 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1818 self.update() # Draw widget before executing fetchmail
1820 # Always look for a runnable command in the directory we're running in
1821 # first. This avoids some obscure version-skew errors that can occur
1822 # if you pick up an old fetchmail from the standard system locations.
1823 os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"]
1824 child_stdout = os.popen(command + " 2>&1", "r")
1826 ch = child_stdout.read(1)
1829 self.textwidget.insert(END, ch)
1830 self.textwidget.insert(END, "Done.")
1831 self.textwidget.see(END);
1834 self.master.destroy()
1836 # Here's where we choose either configuration or launching
1838 class MainWindow(Frame):
1839 def __init__(self, outfile, master=None):
1840 Frame.__init__(self, master)
1841 self.outfile = outfile
1842 self.master.title('fetchmail launcher');
1843 self.master.iconname('fetchmail launcher');
1846 text='Fetchmailconf ' + version,
1847 bd=2).pack(side=TOP, pady=10)
1848 self.keepalive = [] # Use this to anchor the PhotoImage object
1849 make_icon_window(self, fetchmail_icon)
1852 ## Test icon display with the following:
1853 # icon_image = PhotoImage(data=fetchmail_icon)
1854 # Label(self, image=icon_image).pack(side=TOP, pady=10)
1855 # self.keepalive.append(icon_image)
1857 Message(self, text="""
1858 Use `Configure fetchmail' to tell fetchmail about the remote
1859 servers it should poll (the host name, your username there,
1860 whether to use POP or IMAP, and so forth).
1861 """, width=600).pack(side=TOP)
1862 self.configbutton = Button(self, text='Configure fetchmail',
1863 fg='blue', command=self.configure)
1864 self.configbutton.pack()
1866 Message(self, text="""
1867 Use `Run fetchmail' to run fetchmail with debugging enabled.
1868 This is a good way to test out a new configuration.
1869 """, width=600).pack(side=TOP)
1870 Button(self, text='Run fetchmail',fg='blue', command=self.test).pack()
1872 Message(self, text="""
1873 Use `Run fetchmail' to run fetchmail in foreground.
1874 Progress messages will be shown, but not debug messages.
1875 """, width=600).pack(side=TOP)
1876 Button(self, text='Run fetchmail', fg='blue', command=self.run).pack()
1878 Message(self, text="""
1879 Or you can just select `Quit' to exit the launcher now.
1880 """, width=600).pack(side=TOP)
1881 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1883 def configure(self):
1884 self.configbutton.configure(state=DISABLED)
1885 Configurator(self.outfile, Toplevel(),
1886 lambda self=self: self.configbutton.configure(state=NORMAL),
1889 RunWindow("fetchmail -d0 -v --nosyslog", Toplevel(), self)
1892 RunWindow("fetchmail -d0", Toplevel(), self)
1897 # Functions for turning a dictionary into an instantiated object tree.
1899 def intersect(list1, list2):
1900 # Compute set intersection of lists
1907 def setdiff(list1, list2):
1908 # Compute set difference of lists
1915 def copy_instance(toclass, fromdict):
1916 # Initialize a class object of given type from a conformant dictionary.
1917 for fld in fromdict.keys():
1918 if not fld in dictmembers:
1919 dictmembers.append(fld)
1920 # The `optional' fields are the ones we can ignore for purposes of
1921 # conformability checking; they'll still get copied if they are
1922 # present in the dictionary.
1923 optional = ('interface', 'monitor',
1924 'esmtpname', 'esmtppassword',
1925 'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
1926 'sslcertpath', 'sslfingerprint', 'showdots')
1927 class_sig = setdiff(toclass.__dict__.keys(), optional)
1929 dict_keys = setdiff(fromdict.keys(), optional)
1931 common = intersect(class_sig, dict_keys)
1932 if 'typemap' in class_sig:
1933 class_sig.remove('typemap')
1934 if tuple(class_sig) != tuple(dict_keys):
1935 print "Fields don't match what fetchmailconf expected:"
1936 # print "Class signature: " + `class_sig`
1937 # print "Dictionary keys: " + `dict_keys`
1938 diff = setdiff(class_sig, common)
1940 print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
1941 diff = setdiff(dict_keys, common)
1943 print "Not matched in dictionary keys: " + `diff`
1946 for x in fromdict.keys():
1947 setattr(toclass, x, fromdict[x])
1950 # And this is the main sequence. How it works:
1952 # First, call `fetchmail --configdump' and trap the output in a tempfile.
1953 # This should fill it with a Python initializer for a variable `fetchmailrc'.
1954 # Run execfile on the file to pull fetchmailrc into Python global space.
1955 # You don't want static data, though; you want, instead, a tree of objects
1956 # with the same data members and added appropriate methods.
1958 # This is what the copy_instance function() is for. It tries to copy a
1959 # dictionary field by field into a class, aborting if the class and dictionary
1960 # have different data members (except for any typemap member in the class;
1961 # that one is strictly for use by the MyWidget supperclass).
1963 # Once the object tree is set up, require user to choose novice or expert
1964 # mode and instantiate an edit object for the configuration. Class methods
1965 # will take it all from there.
1967 # Options (not documented because they're for fetchmailconf debuggers only):
1968 # -d: Read the configuration and dump it to stdout before editing. Dump
1969 # the edited result to stdout as well.
1970 # -f: specify the run control file to read.
1972 if __name__ == '__main__':
1974 if not os.environ.has_key("DISPLAY"):
1975 print "fetchmailconf must be run under X"
1978 fetchmail_icon = """
1979 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
1980 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
1981 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
1982 CJR7Y/+UMdbOxnNrY97Ove/Wvd7GrZyEa961jL2Ua9alc86ca7WEUntSKcaMSqVjGNZ7GGM5CNa1
1983 jPfOnN6tc3taMffeve/WtWtaQv/OjGtSMYRzWv/erda1hM6te7WUY62MWs61jP/vzv/ntda9jL2l
1984 czEhAO/n1oyEc//elDEpGEo5EOfexpyUe+/epefevffvxnNrQpyUStbWzsbGvZyclN7ezmNjWv//
1985 5/f33qWllNbWve/vzv//1ufnve/vvf//xvf3vefnrf//taWlc0pKMf//pbW1Y///jKWlWq2tWsbG
1986 Y///c97eUvf3Ut7nc+/3a87We8bOjOfv1u/37/f//621tb3Gxtbn52Nra87n53uUlJTv/6W9xuf3
1987 /8bW3iExOXu11tbv/5TW/4TO/63e/zmt/1KUxlK1/2u9/wCM/73GzrXG1gBKjACE/87e72NzhCkx
1988 OaXO92OMtUql/xCE/wApUtbe57W9xnN7hHut52Ot/xBSnABKnABavQB7/2ul7zF71gBr77XO73Oc
1989 1lqc9yFSlBApSimE/wAYOQApY0J7zlKM5wAxhABS1gBj/6W95wAhWgA5nAAYSgBS7wBS/wBK9wAp
1990 jABC5wBK/wApnABC/wApxgAhtYSMtQAQYwAp/3OE74SMxgAYxlpjvWNr70pS/wgQ3sbGzs7O1qWl
1991 3qWl70pKe0JC/yEhlCkp/wgI/wAAEAAAIQAAKQAAOQAASgAAUgAAYwAAawAAlAAAnAAApQAArQAA
1992 zgAA1gAA5wAA9wAA/0pC/xgQ52Na9ykhe4R7zikhYxgQSjEpQgAAACwAAAAAPAAoAAAI/wABCBxI
1993 sKDBgwgTKiRIYKHDhxARIvgXsaLFhGgEUBSYoKPHjyBDihxJkuS/kwNLqlzJcuTJjQBaypxpEiVH
1994 mjhxvkyZs2fLnTd9ehxAtKjRo0ZrwhTasUsENhYHKOUpk1E3j11mxCBiQVLEBlJd2owp9iVRjwUs
1995 zMCQ5IcLD4saPVxjIKxIoGTvvqSoyFEFGTBeqEhyxAoSFR/USGKVcEGBAwDshsSr1OYTEyhQpJiS
1996 ZcoUKWOQtJDRJFSaggzUGBgoGSTlsjahlPCRIkWVKT16THHRIoqIISBIEUgAYIGBhgRbf3ytFygU
1997 FZp9UDmxQkkMCRwyZKDBQy4aApABhP8XqNwj88l7BVpQYZtF5iArWgwAgGZBq24HU7OeGhQ90PVA
1998 aKZZCiiUMJ9ArSTEwGqR8ZeXfzbV0MIIMQTBwoUdxDDfAm8sZFyDZVEF4UYSKBEBD0+k6IEFPMxH
1999 3FzldXSea+kBgANJSOWIlIMhXZXAXv+c1WM3PuJEpH8iuhbAkv+MdENPRHaTRkdF/jiWSKCAwlKW
2000 VbbkY5Q0LgUSKExgoYBKCjCxARpdltQNKHaUoYAddnR53lVRnJLKBWh4RIEGCZx5FSOv1OLNDUVe
2001 deZHaWiZAB35fIOGNtbEUeV5oGAByzPOrBPFGt3kwEgxITACSg5oLGGLMg60oQAjaNz/oAAcN4Ai
2002 a0c3kHFDK3jYsw4g9sRzBgPLXdkRrBrQ8gsWQUxCCRZX9IJNBQ1s8IgCdeBCzBYN6IBIN2TUsQYd
2003 dXhDBxdzlAHOHHKEcocZdWwDjx8MTCmjsR2FMAstw1RyiSzHqPLALaOwk8QmzCzDCSi0xJKMMk4E
2004 Yw8389iTDT32GAKOPf7YY0Aa9tATyD3w/EGsefgmgEYUtPiChLKWQDMBJtEUgYkzH2RiTgGfTMCI
2005 Mlu0Yc85hNiDziH2tMqOGL72QY47gshLb7Fi4roELcjoQIsxWpDwQyfS2OCJMkLI4YUmyhgxSTVg
2006 CP2FHPZ80UDcieBjStNPD5LPOyZT/y0iHGiMwswexDSzRiRq6KIMJBc4M8skwKAyChia2KPH3P24
2007 YU8/lFhOTj152OPOHuXMU4g48vCRiN/9rZGLMdS4csUu1JzDgxuipOMDHMKsAwEnq/ByzTrrZMNO
2008 OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
2009 xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
2010 gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
2012 # The base64 data in the string above was generated by the following procedure:
2015 # print base64.encodestring(open("fetchmail.gif", "rb").read())
2019 (options, arguments) = getopt.getopt(sys.argv[1:], "df:h")
2020 dump = rcfile = None;
2021 for (switch, val) in options:
2022 if (switch == '-d'):
2024 elif (switch == '-f'):
2026 elif (switch == '-h'):
2028 Usage: fetchmailconf [-d] [-f fetchmailrc]
2029 -d - dump configuration (for debugging)
2030 -f fmrc - read alternate fetchmailrc file
2034 # Get client host's FQDN
2035 hostname = socket.gethostbyaddr(socket.gethostname())[0]
2038 ConfigurationDefaults = Configuration()
2039 ServerDefaults = Server()
2040 UserDefaults = User()
2042 # Read the existing configuration. We set the umask to 077 to make sure
2043 # that group & other read/write permissions are shut off -- we wouldn't
2044 # want crackers to snoop password information out of the tempfile.
2045 tmpfile = tempfile.mktemp()
2047 cmd = "umask 077 && fetchmail -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2049 cmd = "umask 077 && fetchmail --configdump --nosyslog >" + tmpfile
2054 print "`" + cmd + "' run failure, status " + `s`
2057 print "Unknown error while running fetchmail --configdump"
2064 print "Can't read configuration output of fetchmail --configdump."
2070 # The tricky part -- initializing objects from the configuration global
2071 # `Configuration' is the top level of the object tree we're going to mung.
2072 # The dictmembers list is used to track the set of fields the dictionary
2073 # contains; in particular, we can use it to tell whether things like the
2074 # monitor, interface, ssl, sslkey, or sslcert fields are present.
2076 Fetchmailrc = Configuration()
2077 copy_instance(Fetchmailrc, fetchmailrc)
2078 Fetchmailrc.servers = [];
2079 for server in fetchmailrc['servers']:
2081 copy_instance(Newsite, server)
2082 Fetchmailrc.servers.append(Newsite)
2084 for user in server['users']:
2086 copy_instance(Newuser, user)
2087 Newsite.users.append(Newuser)
2089 # We may want to display the configuration and quit
2091 print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2093 # The theory here is that -f alone sets the rcfile location,
2094 # but -d and -f together mean the new configuration should go to stdout.
2095 if not rcfile and not dump:
2096 rcfile = os.environ["HOME"] + "/.fetchmailrc"
2098 # OK, now run the configuration edit
2099 root = MainWindow(rcfile)
2102 # The following sets edit modes for GNU EMACS