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.softbounce = TRUE # Treat permanent error as temporary
26 self.properties = None # No exiguous properties
27 self.invisible = FALSE # Suppress Received line & spoof?
28 self.syslog = FALSE # Use syslogd for logging?
29 self.servers = [] # List of included sites
30 Configuration.typemap = (
31 ('poll_interval', 'Int'),
32 ('logfile', 'String'),
34 ('postmaster', 'String'),
35 ('bouncemail', 'Boolean'),
36 ('spambounce', 'Boolean'),
37 ('softbounce', 'Boolean'),
38 ('properties', 'String'),
39 ('syslog', 'Boolean'),
40 ('invisible', 'Boolean'))
44 if self.syslog != ConfigurationDefaults.syslog:
45 str = str + ("set syslog\n")
47 str = str + ("set logfile \"%s\"\n" % (self.logfile,));
48 if self.idfile != ConfigurationDefaults.idfile:
49 str = str + ("set idfile \"%s\"\n" % (self.idfile,));
50 if self.postmaster != ConfigurationDefaults.postmaster:
51 str = str + ("set postmaster \"%s\"\n" % (self.postmaster,));
53 str = str + ("set bouncemail\n")
55 str = str + ("set nobouncemail\n")
57 str = str + ("set spambounce\n")
59 str = str + ("set no spambounce\n")
61 str = str + ("set softbounce\n")
63 str = str + ("set no softbounce\n")
64 if self.properties != ConfigurationDefaults.properties:
65 str = str + ("set properties \"%s\"\n" % (self.properties,));
66 if self.poll_interval > 0:
67 str = str + "set daemon " + `self.poll_interval` + "\n"
69 str = str + ("set invisible\n")
70 for site in self.servers:
71 str = str + repr(site)
74 def __delitem__(self, name):
75 for si in range(len(self.servers)):
76 if self.servers[si].pollname == name:
81 return "[Configuration: " + repr(self) + "]"
85 self.pollname = None # Poll label
86 self.via = None # True name of host
87 self.active = TRUE # Poll status
88 self.interval = 0 # Skip interval
89 self.protocol = 'auto' # Default to auto protocol
90 self.service = None # Service name to use
91 self.uidl = FALSE # Don't use RFC1725 UIDLs by default
92 self.auth = 'any' # Default to password authentication
93 self.timeout = 300 # 5-minute timeout
94 self.envelope = 'Received' # Envelope-address header
95 self.envskip = 0 # Number of envelope headers to skip
96 self.qvirtual = None # Name prefix to strip
97 self.aka = [] # List of DNS aka names
98 self.dns = TRUE # Enable DNS lookup on multidrop
99 self.localdomains = [] # Domains to be considered local
100 self.interface = None # IP address and range
101 self.monitor = None # IP address and range
102 self.plugin = None # Plugin command for going to server
103 self.plugout = None # Plugin command for going to listener
104 self.principal = None # Kerberos principal
105 self.esmtpname = None # ESMTP 2554 name
106 self.esmtppassword = None # ESMTP 2554 password
107 self.tracepolls = FALSE # Add trace-poll info to headers
108 self.badheader = FALSE # Pass messages with bad headers on?
109 self.retrieveerror = 'abort' # Policy when message retrieval errors encountered
110 self.users = [] # List of user entries for site
112 ('pollname', 'String'),
114 ('active', 'Boolean'),
116 ('protocol', 'String'),
117 ('service', 'String'),
121 ('envelope', 'String'),
123 ('qvirtual', 'String'),
126 # leave localdomains out
127 ('interface', 'String'),
128 ('monitor', 'String'),
129 ('plugin', 'String'),
130 ('plugout', 'String'),
131 ('esmtpname', 'String'),
132 ('esmtppassword', 'String'),
133 ('principal', 'String'),
134 ('tracepolls','Boolean'),
135 ('badheader', 'Boolean'),
136 ('retrieveerror', 'String'))
138 def dump(self, folded):
140 if self.active: res = res + "poll"
141 else: res = res + "skip"
142 res = res + (" " + self.pollname)
144 res = res + (" via " + str(self.via) + "\n");
145 if self.protocol != ServerDefaults.protocol:
146 res = res + " with proto " + self.protocol
147 if self.service and self.protocol and self.service != defaultports[self.protocol] and defaultports[self.protocol] and self.service != ianaservices[defaultports[self.protocol]]:
148 res = res + " service " + self.service
149 if self.timeout != ServerDefaults.timeout:
150 res = res + " timeout " + `self.timeout`
151 if self.interval != ServerDefaults.interval:
152 res = res + " interval " + `self.interval`
153 if self.envelope != ServerDefaults.envelope or self.envskip != ServerDefaults.envskip:
155 res = res + " envelope " + `self.envskip` + " " + self.envelope
157 res = res + " envelope " + self.envelope
159 res = res + (" qvirtual " + str(self.qvirtual) + "\n");
160 if self.auth != ServerDefaults.auth:
161 res = res + " auth " + self.auth
162 if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl:
163 res = res + " and options"
164 if self.dns != ServerDefaults.dns:
165 res = res + flag2str(self.dns, 'dns')
166 if self.uidl != ServerDefaults.uidl:
167 res = res + flag2str(self.uidl, 'uidl')
168 if folded: res = res + "\n "
169 else: res = res + " "
175 if self.aka and self.localdomains: res = res + " "
176 if self.localdomains:
177 res = res + ("localdomains")
178 for x in self.localdomains:
180 if (self.aka or self.localdomains):
187 res = res + "tracepolls\n"
190 res = res + " interface " + str(self.interface)
192 res = res + " monitor " + str(self.monitor)
194 res = res + " plugin " + `self.plugin`
196 res = res + " plugout " + `self.plugout`
198 res = res + " principal " + `self.principal`
200 res = res + " esmtpname " + `self.esmtpname`
201 if self.esmtppassword:
202 res = res + " esmtppassword " + `self.esmtppassword`
203 if self.interface or self.monitor or self.principal or self.plugin or self.plugout:
207 res = res + "bad-header accept "
208 if self.retrieveerror == 'continue':
209 res = res + "retrieve-error continue "
210 if self.retrieveerror == 'markseen':
211 res = res + "retrieve-error markseen "
213 if res[-1] == " ": res = res[0:-1]
215 for user in self.users:
216 res = res + repr(user)
220 def __delitem__(self, name):
221 for ui in range(len(self.users)):
222 if self.users[ui].remote == name:
227 return self.dump(TRUE)
230 return "[Server: " + self.dump(FALSE) + "]"
234 if os.environ.has_key("USER"):
235 self.remote = os.environ["USER"] # Remote username
236 elif os.environ.has_key("LOGNAME"):
237 self.remote = os.environ["LOGNAME"]
239 print "Can't get your username!"
241 self.localnames = [self.remote,]# Local names
242 self.password = None # Password for mail account access
243 self.mailboxes = [] # Remote folders to retrieve from
244 self.smtphunt = [] # Hosts to forward to
245 self.fetchdomains = [] # Domains to fetch from
246 self.smtpaddress = None # Append this to MAIL FROM line
247 self.smtpname = None # Use this for RCPT TO
248 self.preconnect = None # Connection setup
249 self.postconnect = None # Connection wrapup
250 self.mda = None # Mail Delivery Agent
251 self.bsmtp = None # BSMTP output file
252 self.lmtp = FALSE # Use LMTP rather than SMTP?
253 self.antispam = "" # Listener's spam-block code
254 self.keep = FALSE # Keep messages
255 self.flush = FALSE # Flush messages
256 self.limitflush = FALSE # Flush oversized messages
257 self.fetchall = FALSE # Fetch old messages
258 self.rewrite = TRUE # Rewrite message headers
259 self.forcecr = FALSE # Force LF -> CR/LF
260 self.stripcr = FALSE # Strip CR
261 self.pass8bits = FALSE # Force BODY=7BIT
262 self.mimedecode = FALSE # Undo MIME armoring
263 self.dropstatus = FALSE # Drop incoming Status lines
264 self.dropdelivered = FALSE # Drop incoming Delivered-To lines
265 self.idle = FALSE # IDLE after poll
266 self.limit = 0 # Message size limit
267 self.warnings = 3600 # Size warning interval (see tunable.h)
268 self.fetchlimit = 0 # Max messages fetched per batch
269 self.fetchsizelimit = 100 # Max message sizes fetched per transaction
270 self.fastuidl = 4 # Do fast uidl 3 out of 4 times
271 self.batchlimit = 0 # Max message forwarded per batch
272 self.expunge = 0 # Interval between expunges (IMAP)
273 self.ssl = 0 # Enable Seccure Socket Layer
274 self.sslkey = None # SSL key filename
275 self.sslcert = None # SSL certificate filename
276 self.sslproto = None # Force SSL?
277 self.sslcertck = 0 # Enable strict SSL cert checking
278 self.sslcertpath = None # Path to trusted certificates
279 self.sslcommonname = None # SSL CommonName to expect
280 self.sslfingerprint = None # SSL key fingerprint to check
281 self.properties = None # Extension properties
283 ('remote', 'String'),
284 # leave out mailboxes and localnames
285 ('password', 'String'),
286 # Leave out smtphunt, fetchdomains
287 ('smtpaddress', 'String'),
288 ('smtpname', 'String'),
289 ('preconnect', 'String'),
290 ('postconnect', 'String'),
294 ('antispam', 'String'),
296 ('flush', 'Boolean'),
297 ('limitflush', 'Boolean'),
298 ('fetchall', 'Boolean'),
299 ('rewrite', 'Boolean'),
300 ('forcecr', 'Boolean'),
301 ('stripcr', 'Boolean'),
302 ('pass8bits', 'Boolean'),
303 ('mimedecode', 'Boolean'),
304 ('dropstatus', 'Boolean'),
305 ('dropdelivered', 'Boolean'),
309 ('fetchlimit', 'Int'),
310 ('fetchsizelimit', 'Int'),
312 ('batchlimit', 'Int'),
315 ('sslkey', 'String'),
316 ('sslcert', 'String'),
317 ('sslcertck', 'Boolean'),
318 ('sslcertpath', 'String'),
319 ('sslcommonname', 'String'),
320 ('sslfingerprint', 'String'),
321 ('properties', 'String'))
325 res = res + "user " + `self.remote` + " there ";
327 res = res + "with password " + `self.password` + " "
330 for x in self.localnames:
331 res = res + " " + `x`
333 if (self.keep != UserDefaults.keep
334 or self.flush != UserDefaults.flush
335 or self.limitflush != UserDefaults.limitflush
336 or self.fetchall != UserDefaults.fetchall
337 or self.rewrite != UserDefaults.rewrite
338 or self.forcecr != UserDefaults.forcecr
339 or self.stripcr != UserDefaults.stripcr
340 or self.pass8bits != UserDefaults.pass8bits
341 or self.mimedecode != UserDefaults.mimedecode
342 or self.dropstatus != UserDefaults.dropstatus
343 or self.dropdelivered != UserDefaults.dropdelivered
344 or self.idle != UserDefaults.idle):
345 res = res + " options"
346 if self.keep != UserDefaults.keep:
347 res = res + flag2str(self.keep, 'keep')
348 if self.flush != UserDefaults.flush:
349 res = res + flag2str(self.flush, 'flush')
350 if self.limitflush != UserDefaults.limitflush:
351 res = res + flag2str(self.limitflush, 'limitflush')
352 if self.fetchall != UserDefaults.fetchall:
353 res = res + flag2str(self.fetchall, 'fetchall')
354 if self.rewrite != UserDefaults.rewrite:
355 res = res + flag2str(self.rewrite, 'rewrite')
356 if self.forcecr != UserDefaults.forcecr:
357 res = res + flag2str(self.forcecr, 'forcecr')
358 if self.stripcr != UserDefaults.stripcr:
359 res = res + flag2str(self.stripcr, 'stripcr')
360 if self.pass8bits != UserDefaults.pass8bits:
361 res = res + flag2str(self.pass8bits, 'pass8bits')
362 if self.mimedecode != UserDefaults.mimedecode:
363 res = res + flag2str(self.mimedecode, 'mimedecode')
364 if self.dropstatus != UserDefaults.dropstatus:
365 res = res + flag2str(self.dropstatus, 'dropstatus')
366 if self.dropdelivered != UserDefaults.dropdelivered:
367 res = res + flag2str(self.dropdelivered, 'dropdelivered')
368 if self.idle != UserDefaults.idle:
369 res = res + flag2str(self.idle, 'idle')
370 if self.limit != UserDefaults.limit:
371 res = res + " limit " + `self.limit`
372 if self.warnings != UserDefaults.warnings:
373 res = res + " warnings " + `self.warnings`
374 if self.fetchlimit != UserDefaults.fetchlimit:
375 res = res + " fetchlimit " + `self.fetchlimit`
376 if self.fetchsizelimit != UserDefaults.fetchsizelimit:
377 res = res + " fetchsizelimit " + `self.fetchsizelimit`
378 if self.fastuidl != UserDefaults.fastuidl:
379 res = res + " fastuidl " + `self.fastuidl`
380 if self.batchlimit != UserDefaults.batchlimit:
381 res = res + " batchlimit " + `self.batchlimit`
382 if self.ssl and self.ssl != UserDefaults.ssl:
383 res = res + flag2str(self.ssl, 'ssl')
384 if self.sslkey and self.sslkey != UserDefaults.sslkey:
385 res = res + " sslkey " + `self.sslkey`
386 if self.sslcert and self.sslcert != UserDefaults.sslcert:
387 res = res + " sslcert " + `self.sslcert`
388 if self.sslproto and self.sslproto != UserDefaults.sslproto:
389 res = res + " sslproto " + `self.sslproto`
390 if self.sslcertck and self.sslcertck != UserDefaults.sslcertck:
391 res = res + flag2str(self.sslcertck, 'sslcertck')
392 if self.sslcertpath and self.sslcertpath != UserDefaults.sslcertpath:
393 res = res + " sslcertpath " + `self.sslcertpath`
394 if self.sslcommonname and self.sslcommonname != UserDefaults.sslcommonname:
395 res = res + " sslcommonname " + `self.sslcommonname`
396 if self.sslfingerprint and self.sslfingerprint != UserDefaults.sslfingerprint:
397 res = res + " sslfingerprint " + `self.sslfingerprint`
398 if self.expunge != UserDefaults.expunge:
399 res = res + " expunge " + `self.expunge`
401 trimmed = self.smtphunt;
402 if trimmed != [] and trimmed[len(trimmed) - 1] == "localhost":
403 trimmed = trimmed[0:len(trimmed) - 1]
404 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
405 trimmed = trimmed[0:len(trimmed) - 1]
407 res = res + " smtphost "
411 trimmed = self.fetchdomains
412 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
413 trimmed = trimmed[0:len(trimmed) - 1]
415 res = res + " fetchdomains "
420 res = res + " folder"
421 for x in self.mailboxes:
422 res = res + ' "%s"' % x
424 for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'):
425 if getattr(self, fld):
426 res = res + " %s %s\n" % (fld, `getattr(self, fld)`)
427 if self.lmtp != UserDefaults.lmtp:
428 res = res + flag2str(self.lmtp, 'lmtp')
429 if self.antispam != UserDefaults.antispam:
430 res = res + " antispam " + self.antispam + "\n"
434 return "[User: " + repr(self) + "]"
440 # IANA port assignments and bogus 1109 entry
441 ianaservices = {"pop2":109,
448 # fetchmail protocol to IANA service name
449 defaultports = {"auto":None,
458 authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp",
462 'title' : 'List Selection Help',
463 'banner': 'List Selection',
465 You must select an item in the list box (by clicking on it).
468 def flag2str(value, string):
469 # make a string representation of a .fetchmailrc flag or negated flag
473 if value == FALSE: str = str + ("no ")
477 class LabeledEntry(Frame):
478 # widget consisting of entry field with caption to left
479 def bind(self, key, action):
480 self.E.bind(key, action)
483 def __init__(self, Master, text, textvar, lwidth, ewidth=12):
484 Frame.__init__(self, Master)
485 self.L = Label(self, {'text':text, 'width':lwidth, 'anchor':'w'})
486 self.E = Entry(self, {'textvar':textvar, 'width':ewidth})
487 self.L.pack({'side':'left'})
488 self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
490 def ButtonBar(frame, legend, ref, alternatives, depth, command):
491 # array of radio buttons, caption to left, picking from a string list
493 width = (len(alternatives)+1) / depth;
494 Label(bar, text=legend).pack(side=LEFT)
495 for column in range(width):
496 subframe = Frame(bar)
497 for row in range(depth):
498 ind = width * row + column
499 if ind < len(alternatives):
500 Radiobutton(subframe,
501 {'text':alternatives[ind],
503 'value':alternatives[ind],
504 'command':command}).pack(side=TOP, anchor=W)
506 # This is just a spacer
507 Radiobutton(subframe,
508 {'text':" ",'state':DISABLED}).pack(side=TOP, anchor=W)
509 subframe.pack(side=LEFT)
513 def helpwin(helpdict):
514 # help message window with a self-destruct button
516 helpwin.title(helpdict['title'])
517 helpwin.iconname(helpdict['title'])
518 Label(helpwin, text=helpdict['banner']).pack()
519 textframe = Frame(helpwin)
520 scroll = Scrollbar(textframe)
521 helpwin.textwidget = Text(textframe, setgrid=TRUE)
522 textframe.pack(side=TOP, expand=YES, fill=BOTH)
523 helpwin.textwidget.config(yscrollcommand=scroll.set)
524 helpwin.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
525 scroll.config(command=helpwin.textwidget.yview)
526 scroll.pack(side=RIGHT, fill=BOTH)
527 helpwin.textwidget.insert(END, helpdict['text']);
528 Button(helpwin, text='Done',
529 command=lambda x=helpwin: x.destroy(), bd=2).pack()
530 textframe.pack(side=TOP)
532 def make_icon_window(base, image):
534 # Some older pythons will error out on this
535 icon_image = PhotoImage(data=image)
536 icon_window = Toplevel()
537 Label(icon_window, image=icon_image, bg='black').pack()
538 base.master.iconwindow(icon_window)
539 # Avoid TkInter brain death. PhotoImage objects go out of
540 # scope when the enclosing function returns. Therefore
541 # we have to explicitly link them to something.
542 base.keepalive.append(icon_image)
546 class ListEdit(Frame):
547 # edit a list of values (duplicates not allowed) with a supplied editor hook
548 def __init__(self, newlegend, list, editor, deletor, master, helptxt):
550 self.deletor = deletor
553 # Set up a widget to accept new elements
554 self.newval = StringVar(master)
555 newwin = LabeledEntry(master, newlegend, self.newval, '12')
556 newwin.bind('<Double-1>', self.handleNew)
557 newwin.bind('<Return>', self.handleNew)
558 newwin.pack(side=TOP, fill=X, anchor=E)
560 # Edit the existing list
561 listframe = Frame(master)
562 scroll = Scrollbar(listframe)
563 self.listwidget = Listbox(listframe, height=0, selectmode='browse')
566 self.listwidget.insert(END, x)
567 listframe.pack(side=TOP, expand=YES, fill=BOTH)
568 self.listwidget.config(yscrollcommand=scroll.set)
569 self.listwidget.pack(side=LEFT, expand=YES, fill=BOTH)
570 scroll.config(command=self.listwidget.yview)
571 scroll.pack(side=RIGHT, fill=BOTH)
572 self.listwidget.config(selectmode=SINGLE, setgrid=TRUE)
573 self.listwidget.bind('<Double-1>', self.handleList);
574 self.listwidget.bind('<Return>', self.handleList);
578 Button(bf, text='Edit', command=self.editItem).pack(side=LEFT)
579 Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT)
581 self.helptxt = helptxt
582 Button(bf, text='Help', fg='blue',
583 command=self.help).pack(side=RIGHT)
587 helpwin(self.helptxt)
589 def handleList(self, event):
592 def handleNew(self, event):
593 item = self.newval.get()
595 entire = self.listwidget.get(0, self.listwidget.index('end'));
596 if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
597 self.listwidget.insert('end', item)
598 if self.list != None: self.list.append(item)
600 apply(self.editor, (item,))
604 select = self.listwidget.curselection()
609 if index and self.editor:
610 label = self.listwidget.get(index);
612 apply(self.editor, (label,))
614 def deleteItem(self):
615 select = self.listwidget.curselection()
619 index = string.atoi(select[0])
620 label = self.listwidget.get(index);
621 self.listwidget.delete(index)
622 if self.list != None:
624 if self.deletor != None:
625 apply(self.deletor, (label,))
627 def ConfirmQuit(frame, context):
630 text = 'Really quit ' + context + ' without saving?',
632 strings = ('Yes', 'No'),
636 def dispose_window(master, legend, help, savelegend='OK'):
637 dispose = Frame(master, relief=RAISED, bd=5)
638 Label(dispose, text=legend).pack(side=TOP,pady=10)
639 Button(dispose, text=savelegend, fg='blue',
640 command=master.save).pack(side=LEFT)
641 Button(dispose, text='Quit', fg='blue',
642 command=master.nosave).pack(side=LEFT)
643 Button(dispose, text='Help', fg='blue',
644 command=lambda x=help: helpwin(x)).pack(side=RIGHT)
649 # Common methods for Tkinter widgets -- deals with Tkinter declaration
650 def post(self, widgetclass, field):
651 for x in widgetclass.typemap:
652 if x[1] == 'Boolean':
653 setattr(self, x[0], BooleanVar(self))
654 elif x[1] == 'String':
655 setattr(self, x[0], StringVar(self))
657 setattr(self, x[0], IntVar(self))
658 source = getattr(getattr(self, field), x[0])
660 getattr(self, x[0]).set(source)
662 def fetch(self, widgetclass, field):
663 for x in widgetclass.typemap:
664 setattr(getattr(self, field), x[0], getattr(self, x[0]).get())
667 # First, code to set the global fetchmail run controls.
670 configure_novice_help = {
671 'title' : 'Fetchmail novice configurator help',
672 'banner': 'Novice configurator help',
674 In the `Novice Configurator Controls' panel, you can:
676 Press `Save' to save the new fetchmail configuration you have created.
677 Press `Quit' to exit without saving.
678 Press `Help' to bring up this help message.
680 In the `Novice Configuration' panels, you will set up the basic data
681 needed to create a simple fetchmail setup. These include:
683 1. The name of the remote site you want to query.
685 2. Your login name on that site.
687 3. Your password on that site.
689 4. A protocol to use (POP, IMAP, ETRN, etc.)
691 5. A poll interval in seconds.
692 If 0, fetchmail will run in the foreground once when started.
693 If > 0, fetchmail will run in the background and start a new poll
694 cycle after the interval has elapsed.
696 6. Options to fetch old messages as well as new, or to suppress
697 deletion of fetched message.
699 The novice-configuration code will assume that you want to forward mail
700 to a local sendmail listener with no special options.
703 configure_expert_help = {
704 'title' : 'Fetchmail expert configurator help',
705 'banner': 'Expert configurator help',
707 In the `Expert Configurator Controls' panel, you can:
709 Press `Save' to save the new fetchmail configuration you have edited.
710 Press `Quit' to exit without saving.
711 Press `Help' to bring up this help message.
713 In the `Run Controls' panel, you can set the following options that
714 control how fetchmail runs:
717 Number of seconds to wait between polls in the background.
718 If zero, fetchmail will run in foreground.
721 If empty, emit progress and error messages to stderr.
722 Otherwise this gives the name of the files to write to.
723 This field is ignored if the "Log to syslog?" option is on.
726 If empty, store seen-message IDs in .fetchids under user's home
727 directory. If nonempty, use given file name.
730 Who to send multidrop mail to as a last resort if no address can
731 be matched. Normally empty; in this case, fetchmail treats the
732 invoking user as the address of last resort unless that user is
733 root. If that user is root, fetchmail sends to `postmaster'.
736 If this option is on (the default) error mail goes to the sender.
737 Otherwise it goes to the postmaster.
740 If this option is on, spam bounces are sent to the sender or
741 postmaster (depending on the "Bounces to sender?" option. Otherwise,
742 spam bounces are not sent (the default).
745 If this option is on, permanent delivery errors are treated as
746 temporary, i. e. mail is kept on the upstream server. Useful
747 during testing and after configuration changes, and on by
749 If this option is off, permanent delivery errors delete
750 undeliverable mail from the upstream.
753 If false (the default) fetchmail generates a Received line into
754 each message and generates a HELO from the machine it is running on.
755 If true, fetchmail generates no Received line and HELOs as if it were
758 In the `Remote Mail Configurations' panel, you can:
760 1. Enter the name of a new remote mail server you want fetchmail to query.
762 To do this, simply enter a label for the poll configuration in the
763 `New Server:' box. The label should be a DNS name of the server (unless
764 you are using ssh or some other tunneling method and will fill in the `via'
765 option on the site configuration screen).
767 2. Change the configuration of an existing site.
769 To do this, find the site's label in the listbox and double-click it.
770 This will take you to a site configuration dialogue.
774 class ConfigurationEdit(Frame, MyWidget):
775 def __init__(self, configuration, outfile, master, onexit):
777 self.configuration = configuration
778 self.outfile = outfile
779 self.container = master
781 ConfigurationEdit.mode_to_help = {
782 'novice':configure_novice_help, 'expert':configure_expert_help
785 def server_edit(self, sitename):
786 self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
788 def server_delete(self, sitename):
790 for user in self.subwidgets.keys():
792 del self.configuration[sitename]
796 def edit(self, mode):
798 Frame.__init__(self, self.container)
799 self.master.title('fetchmail ' + self.mode + ' configurator');
800 self.master.iconname('fetchmail ' + self.mode + ' configurator');
801 self.master.protocol('WM_DELETE_WINDOW', self.nosave)
802 self.keepalive = [] # Use this to anchor the PhotoImage object
803 make_icon_window(self, fetchmail_icon)
805 self.post(Configuration, 'configuration')
808 'Configurator ' + self.mode + ' Controls',
809 ConfigurationEdit.mode_to_help[self.mode],
812 gf = Frame(self, relief=RAISED, bd = 5)
814 text='Fetchmail Run Controls',
815 bd=2).pack(side=TOP, pady=10)
820 if self.mode != 'novice':
822 log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
823 log.pack(side=RIGHT, anchor=E)
825 # Set the poll interval
826 de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
827 de.pack(side=RIGHT, anchor=E)
832 if self.mode != 'novice':
835 {'text':'Bounces to sender?',
836 'variable':self.bouncemail,
837 'relief':GROOVE}).pack(side=LEFT, anchor=W)
842 {'text':'Send spam bounces?',
843 'variable':self.spambounce,
844 'relief':GROOVE}).pack(side=LEFT, anchor=W)
849 {'text':'Treat permanent errors as temporary?',
850 'variable':self.softbounce,
851 'relief':GROOVE}).pack(side=LEFT, anchor=W)
856 {'text':'Log to syslog?',
857 'variable':self.syslog,
858 'relief':GROOVE}).pack(side=LEFT, anchor=W)
859 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
860 log.pack(side=RIGHT, anchor=E)
864 {'text':'Invisible mode?',
865 'variable':self.invisible,
866 'relief':GROOVE}).pack(side=LEFT, anchor=W)
868 log = LabeledEntry(gf, ' Idfile:', self.idfile, '14')
869 log.pack(side=RIGHT, anchor=E)
873 # Expert mode allows us to edit multiple sites
874 lf = Frame(self, relief=RAISED, bd=5)
876 text='Remote Mail Server Configurations',
877 bd=2).pack(side=TOP, pady=10)
878 ListEdit('New Server:',
879 map(lambda x: x.pollname, self.configuration.servers),
880 lambda site, self=self: self.server_edit(site),
881 lambda site, self=self: self.server_delete(site),
886 for sitename in self.subwidgets.keys():
887 self.subwidgets[sitename].destruct()
888 self.master.destroy()
892 if ConfirmQuit(self, self.mode + " configuration editor"):
896 for sitename in self.subwidgets.keys():
897 self.subwidgets[sitename].save()
898 self.fetch(Configuration, 'configuration')
902 elif not os.path.isfile(self.outfile) or Dialog(self,
903 title = 'Overwrite existing run control file?',
904 text = 'Really overwrite existing run control file?',
906 strings = ('Yes', 'No'),
907 default = 1).num == 0:
909 os.rename(self.outfile, self.outfile + "~")
910 # Pre-1.5.2 compatibility...
913 oldumask = os.umask(077)
914 fm = open(self.outfile, 'w')
919 os.chmod(self.outfile, 0600)
920 fm.write("# Configuration created %s by fetchmailconf %s\n" % (time.ctime(time.time()), version))
921 fm.write(`self.configuration`)
927 # Server editing stuff.
930 'title' : 'Remote site help',
931 'banner': 'Remote sites',
933 When you add a site name to the list here,
934 you initialize an entry telling fetchmail
935 how to poll a new site.
937 When you select a sitename (by double-
938 clicking it, or by single-clicking to
939 select and then clicking the Edit button),
940 you will open a window to configure that
945 'title' : 'Server options help',
946 'banner': 'Server Options',
948 The server options screen controls fetchmail
949 options that apply to one of your mailservers.
951 Once you have a mailserver configuration set
952 up as you like it, you can select `OK' to
953 store it in the server list maintained in
954 the main configuration window.
956 If you wish to discard changes to a server
957 configuration, select `Quit'.
961 'title' : 'Run Control help',
962 'banner': 'Run Controls',
964 If the `Poll normally' checkbox is on, the host is polled as part of
965 the normal operation of fetchmail when it is run with no arguments.
966 If it is off, fetchmail will only query this host when it is given as
967 a command-line argument.
969 The `Retrieve Error Policy' specifies how server errors during
970 message retrieval are handled. The default behaviour is to abort the
971 current session. Both the continue and markseen options will skip
972 the message with the error, but continue the session allowing for
973 downloading of subsequent messages. Additionally, the markseen
974 option will mark the skipped message as seen.
976 The `True name of server' box should specify the actual DNS name
977 to query. By default this is the same as the poll name.
979 Normally each host described in the file is queried once each
980 poll cycle. If `Cycles to skip between polls' is greater than 0,
981 that's the number of poll cycles that are skipped between the
982 times this post is actually polled.
984 The `Server timeout' is the number of seconds fetchmail will wait
985 for a reply from the mailserver before concluding it is hung and
990 'title' : 'Protocol and Port help',
991 'banner': 'Protocol and Port',
993 These options control the remote-mail protocol
994 and TCP/IP service port used to query this
997 If you click the `Probe for supported protocols'
998 button, fetchmail will try to find you the most
999 capable server on the selected host (this will
1000 only work if you're conncted to the Internet).
1001 The probe only checks for ordinary IMAP and POP
1002 protocols; fortunately these are the most
1003 frequently supported.
1005 The `Protocol' button bar offers you a choice of
1006 all the different protocols available. The `auto'
1007 protocol is the default mode; it probes the host
1008 ports for POP3 and IMAP to see if either is
1011 Normally the TCP/IP service port to use is
1012 dictated by the protocol choice. The `Service'
1013 field (only present in expert mode) lets you
1014 set a non-standard service (port).
1018 'title' : 'Security option help',
1019 'banner': 'Security',
1021 The 'authorization mode' allows you to choose the
1022 mode that fetchmail uses to log in to your server. You
1023 can usually leave this at 'any', but you will have to pick
1024 'NTLM' and 'MSN' manually for the nonce.
1026 The 'interface' option allows you to specify a range
1027 of IP addresses to monitor for activity. If these
1028 addresses are not active, fetchmail will not poll.
1029 Specifying this may protect you from a spoofing attack
1030 if your client machine has more than one IP gateway
1031 address and some of the gateways are to insecure nets.
1033 The `monitor' option, if given, specifies the only
1034 device through which fetchmail is permitted to connect
1035 to servers. This option may be used to prevent
1036 fetchmail from triggering an expensive dial-out if the
1037 interface is not already active.
1039 The `interface' and `monitor' options are available
1040 only for Linux and freeBSD systems. See the fetchmail
1041 manual page for details on these.
1043 The ssl option enables SSL communication with a mailserver
1044 supporting Secure Sockets Layer. The sslkey and sslcert options
1045 declare key and certificate files for use with SSL.
1046 The sslcertck option enables strict checking of SSL server
1047 certificates (and sslcertpath gives the trusted certificate
1048 directory). The sslcommonname option helps if the server is
1049 misconfigured and returning "Server CommonName mismatch"
1050 warnings. With sslfingerprint, you can specify a finger-
1051 print the server's key is checked against.
1055 'title' : 'Multidrop option help',
1056 'banner': 'Multidrop',
1058 These options are only useful with multidrop mode.
1059 See the manual page for extended discussion.
1063 'title' : 'User list help',
1064 'banner': 'User list',
1066 When you add a user name to the list here,
1067 you initialize an entry telling fetchmail
1068 to poll the site on behalf of the new user.
1070 When you select a username (by double-
1071 clicking it, or by single-clicking to
1072 select and then clicking the Edit button),
1073 you will open a window to configure the
1074 user's options on that site.
1077 class ServerEdit(Frame, MyWidget):
1078 def __init__(self, host, parent):
1079 self.parent = parent
1081 self.subwidgets = {}
1082 for site in parent.configuration.servers:
1083 if site.pollname == host:
1085 if (self.server == None):
1086 self.server = Server()
1087 self.server.pollname = host
1088 self.server.via = None
1089 parent.configuration.servers.append(self.server)
1091 def edit(self, mode, master=None):
1092 Frame.__init__(self, master)
1094 self.master.title('Fetchmail host ' + self.server.pollname);
1095 self.master.iconname('Fetchmail host ' + self.server.pollname);
1096 self.post(Server, 'server')
1097 self.makeWidgets(self.server.pollname, mode)
1098 self.keepalive = [] # Use this to anchor the PhotoImage object
1099 make_icon_window(self, fetchmail_icon)
1102 # self.wait_window()
1106 for username in self.subwidgets.keys():
1107 self.subwidgets[username].destruct()
1108 del self.parent.subwidgets[self.server.pollname]
1109 self.master.destroy()
1112 if ConfirmQuit(self, 'server option editing'):
1116 self.fetch(Server, 'server')
1117 for username in self.subwidgets.keys():
1118 self.subwidgets[username].save()
1121 def defaultPort(self):
1122 proto = self.protocol.get()
1123 # Callback to reset the port number whenever the protocol type changes.
1124 # We used to only reset the port if it had a default (zero) value.
1125 # This turns out to be a bad idea especially in Novice mode -- if
1126 # you set POP3 and then set IMAP, the port invisibly remained 110.
1127 # Now we reset unconditionally on the theory that if you're setting
1128 # a custom port number you should be in expert mode and playing
1129 # close enough attention to notice this...
1130 self.service.set(defaultports[proto])
1131 if not proto in ("POP3", "APOP", "KPOP"): self.uidl.state = DISABLED
1133 def user_edit(self, username, mode):
1134 self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1136 def user_delete(self, username):
1137 if self.subwidgets.has_key(username):
1138 self.subwidgets[username].destruct()
1139 del self.server[username]
1141 def makeWidgets(self, host, mode):
1142 topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1144 leftwin = Frame(self);
1147 if mode != 'novice':
1148 ctlwin = Frame(leftwin, relief=RAISED, bd=5)
1149 Label(ctlwin, text="Run Controls").pack(side=TOP)
1150 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
1151 Checkbutton(ctlwin, text='Pass messages with bad headers?',
1152 variable=self.badheader).pack(side=TOP)
1153 retrieveerrorlist = ['abort', 'continue', 'markseen']
1154 Label(ctlwin, text="Retrieve Error Policy").pack(side=TOP)
1155 ButtonBar(ctlwin, '', self.retrieveerror, retrieveerrorlist, 1, None)
1156 LabeledEntry(ctlwin, 'True name of ' + host + ':',
1157 self.via, leftwidth).pack(side=TOP, fill=X)
1158 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
1159 self.interval, leftwidth).pack(side=TOP, fill=X)
1160 LabeledEntry(ctlwin, 'Server timeout (seconds):',
1161 self.timeout, leftwidth).pack(side=TOP, fill=X)
1162 Button(ctlwin, text='Help', fg='blue',
1163 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
1166 # Compute the available protocols from the compile-time options
1167 protolist = ['auto']
1168 if 'pop2' in feature_options:
1169 protolist.append("POP2")
1170 if 'pop3' in feature_options:
1171 protolist = protolist + ["POP3", "APOP", "KPOP"]
1172 if 'sdps' in feature_options:
1173 protolist.append("SDPS")
1174 if 'imap' in feature_options:
1175 protolist.append("IMAP")
1176 if 'etrn' in feature_options:
1177 protolist.append("ETRN")
1178 if 'odmr' in feature_options:
1179 protolist.append("ODMR")
1181 protwin = Frame(leftwin, relief=RAISED, bd=5)
1182 Label(protwin, text="Protocol").pack(side=TOP)
1183 ButtonBar(protwin, '',
1184 self.protocol, protolist, 2,
1186 if mode != 'novice':
1187 LabeledEntry(protwin, 'On server TCP/IP service:',
1188 self.service, leftwidth).pack(side=TOP, fill=X)
1190 Checkbutton(protwin,
1191 text="POP3: track `seen' with client-side UIDLs?",
1192 variable=self.uidl).pack(side=TOP)
1193 Button(protwin, text='Probe for supported protocols', fg='blue',
1194 command=self.autoprobe).pack(side=LEFT)
1195 Button(protwin, text='Help', fg='blue',
1196 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
1197 protwin.pack(fill=X)
1199 userwin = Frame(leftwin, relief=RAISED, bd=5)
1200 Label(userwin, text="User entries for " + host).pack(side=TOP)
1201 ListEdit("New user: ",
1202 map(lambda x: x.remote, self.server.users),
1203 lambda u, m=mode, s=self: s.user_edit(u, m),
1204 lambda u, s=self: s.user_delete(u),
1206 userwin.pack(fill=X)
1208 leftwin.pack(side=LEFT, anchor=N, fill=X);
1210 if mode != 'novice':
1211 rightwin = Frame(self);
1213 mdropwin = Frame(rightwin, relief=RAISED, bd=5)
1214 Label(mdropwin, text="Multidrop options").pack(side=TOP)
1215 LabeledEntry(mdropwin, 'Envelope address header:',
1216 self.envelope, '22').pack(side=TOP, fill=X)
1217 LabeledEntry(mdropwin, 'Envelope headers to skip:',
1218 self.envskip, '22').pack(side=TOP, fill=X)
1219 LabeledEntry(mdropwin, 'Name prefix to strip:',
1220 self.qvirtual, '22').pack(side=TOP, fill=X)
1221 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
1222 variable=self.dns).pack(side=TOP)
1223 Label(mdropwin, text="DNS aliases").pack(side=TOP)
1224 ListEdit("New alias: ", self.server.aka, None, None, mdropwin, None)
1225 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
1226 ListEdit("New domain: ",
1227 self.server.localdomains, None, None, mdropwin, multihelp)
1228 mdropwin.pack(fill=X)
1230 if os_type in ('linux', 'freebsd'):
1231 secwin = Frame(rightwin, relief=RAISED, bd=5)
1232 Label(secwin, text="Security").pack(side=TOP)
1233 # Don't actually let users set this. KPOP sets it implicitly
1234 ButtonBar(secwin, 'Authorization mode:',
1235 self.auth, authlist, 2, None).pack(side=TOP)
1236 if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
1237 LabeledEntry(secwin, 'IP range to check before poll:',
1238 self.interface, leftwidth).pack(side=TOP, fill=X)
1239 if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
1240 LabeledEntry(secwin, 'Interface to monitor:',
1241 self.monitor, leftwidth).pack(side=TOP, fill=X)
1242 # Someday this should handle Kerberos 5 too
1243 if 'kerberos' in feature_options:
1244 LabeledEntry(secwin, 'Principal:',
1245 self.principal, '12').pack(side=TOP, fill=X)
1246 # ESMTP authentication
1247 LabeledEntry(secwin, 'ESMTP name:',
1248 self.esmtpname, '12').pack(side=TOP, fill=X)
1249 LabeledEntry(secwin, 'ESMTP password:',
1250 self.esmtppassword, '12').pack(side=TOP, fill=X)
1251 Button(secwin, text='Help', fg='blue',
1252 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
1255 rightwin.pack(side=LEFT, anchor=N);
1257 def autoprobe(self):
1258 # Note: this only handles case (1) near fetchmail.c:1032
1259 # We're assuming people smart enough to set up ssh tunneling
1260 # won't need autoprobing.
1262 realhost = self.server.via
1264 realhost = self.server.pollname
1266 for protocol in ("IMAP","POP3","POP2"):
1267 service = defaultports[protocol]
1268 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1270 sock.connect((realhost, ianaservices[service]))
1271 greetline = sock.recv(1024)
1277 confwin = Toplevel()
1278 if greetline == None:
1279 title = "Autoprobe of " + realhost + " failed"
1281 Fetchmailconf didn't find any mailservers active.
1282 This could mean the host doesn't support any,
1283 or that your Internet connection is down, or
1284 that the host is so slow that the probe timed
1285 out before getting a response.
1289 # OK, now try to recognize potential problems
1291 if protocol == "POP2":
1292 warnings = warnings + """
1293 It appears you have somehow found a mailserver running only POP2.
1294 Congratulations. Have you considered a career in archaeology?
1296 Unfortunately, stock fetchmail binaries don't include POP2 support anymore.
1297 Unless the first line of your fetchmail -V output includes the string "POP2",
1298 you'll have to build it from sources yourself with the configure
1299 switch --enable-POP2.
1303 ### POP3 servers start here
1305 if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
1306 warnings = warnings + """
1307 This appears to be an old version of the UC Davis POP server. These are
1308 dangerously unreliable (among other problems, they may drop your mailbox
1309 on the floor if your connection is interrupted during the session).
1311 It is strongly recommended that you find a better POP3 server. The fetchmail
1312 FAQ includes pointers to good ones.
1315 if string.find(greetline, "comcast.net") > 0:
1316 warnings = warnings + """
1317 The Comcast Maillennium POP3 server only returns the first 80K of a long
1318 message retrieved with TOP. Its response to RETR is normal, so use the
1322 # Steve VanDevender <stevev@efn.org> writes:
1323 # The only system I have seen this happen with is cucipop-1.31
1324 # under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
1325 # 2.x and probably quite a few other systems. It appears to be a
1326 # bug or bad interaction with the SunOS realloc() -- it turns out
1327 # that internally cucipop does allocate a certain data structure in
1328 # multiples of 16, using realloc() to bump it up to the next
1329 # multiple if it needs more.
1331 # The distinctive symptom is that when there are 16 messages in the
1332 # inbox, you can RETR and DELE all 16 messages successfully, but on
1333 # QUIT cucipop returns something like "-ERR Error locking your
1334 # mailbox" and aborts without updating it.
1336 # The cucipop banner looks like:
1338 # +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1340 if string.find(greetline, "Cubic Circle") > 0:
1341 warnings = warnings + """
1342 I see your server is running cucipop. Better make sure the server box
1343 isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
1344 under that version, and doesn't cope with the result gracefully. Newer
1345 SunOS and Solaris machines run cucipop OK.
1347 Also, some versions of cucipop don't assert an exclusive lock on your
1348 mailbox when it's being queried. This means that if you have more than
1349 one fetchmail query running against the same mailbox, bad things can happen.
1351 if string.find(greetline, "David POP3 Server") > 0:
1352 warnings = warnings + """
1353 This POP3 server is badly broken. You should get rid of it -- and the
1354 brain-dead Microsoft operating system it rode in on.
1357 # The greeting line on the server known to be buggy is:
1358 # +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
1360 if string.find(greetline, "FTGate") > 0:
1361 warnings = warnings + """
1362 This POP server has a weird bug; it says OK twice in response to TOP.
1363 Its response to RETR is normal, so use the `fetchall' option.
1366 if string.find(greetline, " geonet.de") > 0:
1367 warnings = warnings + """
1368 You appear to be using geonet. As of late 2002, the TOP command on
1369 geonet's POP3 is broken. Use the fetchall option.
1372 if string.find(greetline, "OpenMail") > 0:
1373 warnings = warnings + """
1374 You appear to be using some version of HP OpenMail. Many versions of
1375 OpenMail do not process the "TOP" command correctly; the symptom is that
1376 only the header and first line of each message is retrieved. To work
1377 around this bug, turn on `fetchall' on all user entries associated with
1381 if string.find(greetline, "Escape character is") > 0:
1382 warnings = warnings + """
1383 Your greeting line looks like it was written by a fetid pile of
1384 camel dung identified to me as `popa3d written by Solar Designer'.
1385 Beware! The UIDL support in this thing is known to be completely broken,
1386 and other things probably are too.
1389 if string.find(greetline, "MercuryP/NLM v1.48") > 0:
1390 warnings = warnings + """
1391 This is not a POP3 server. It has delusions of being one, but after
1392 RETR all messages are automatically marked to be deleted. The only
1393 way to prevent this is to issue an RSET before leaving the server.
1394 Fetchmail does this, but we suspect this is probably broken in lots
1398 if string.find(greetline, "POP-Max") > 0:
1399 warnings = warnings + """
1400 The Mail Max POP3 server screws up on mail with attachments. It
1401 reports the message size with attachments included, but doesn't
1402 download them on a RETR or TOP (this violates the IMAP RFCs). It also
1403 doesn't implement TOP correctly. You should get rid of it -- and the
1404 brain-dead NT server it rode in on.
1407 if string.find(greetline, "POP3 Server Ready") > 0:
1408 warnings = warnings + """
1409 Some server that uses this greeting line has been observed to choke on
1410 TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
1413 if string.find(greetline, "QPOP") > 0:
1414 warnings = warnings + """
1415 This appears to be a version of Eudora qpopper. That's good. Fetchmail
1416 knows all about qpopper. However, be aware that the 2.53 version of
1417 qpopper does something odd that causes fetchmail to hang with a socket
1418 error on very large messages. This is probably not a fetchmail bug, as
1419 it has been observed with fetchpop. The fix is to upgrade to qpopper
1420 3.0beta or a more recent version. Better yet, switch to IMAP.
1423 if string.find(greetline, " sprynet.com") > 0:
1424 warnings = warnings + """
1425 You appear to be using a SpryNet server. In mid-1999 it was reported that
1426 the SpryNet TOP command marks messages seen. Therefore, for proper error
1427 recovery in the event of a line drop, it is strongly recommended that you
1428 turn on `fetchall' on all user entries associated with this server.
1431 if string.find(greetline, "TEMS POP3") > 0:
1432 warnings = warnings + """
1433 Your POP3 server has "TEMS" in its header line. At least one such
1434 server does not process the "TOP" command correctly; the symptom is
1435 that fetchmail hangs when trying to retrieve mail. To work around
1436 this bug, turn on `fetchall' on all user entries associated with this
1440 if string.find(greetline, " spray.se") > 0:
1441 warnings = warnings + """
1442 Your POP3 server has "spray.se" in its header line. In May 2000 at
1443 least one such server did not process the "TOP" command correctly; the
1444 symptom is that messages are treated as headerless. To work around
1445 this bug, turn on `fetchall' on all user entries associated with this
1449 if string.find(greetline, " usa.net") > 0:
1450 warnings = warnings + """
1451 You appear to be using USA.NET's free mail service. Their POP3 servers
1452 (at least as of the 2.2 version in use mid-1998) are quite flaky, but
1453 fetchmail can compensate. They seem to require that fetchall be switched on
1454 (otherwise you won't necessarily see all your mail, not even new mail).
1455 They also botch the TOP command the fetchmail normally uses for retrieval
1456 (it only retrieves about 10 lines rather than the number specified).
1457 Turning on fetchall will disable the use of TOP.
1459 Therefore, it is strongly recommended that you turn on `fetchall' on all
1460 user entries associated with this server.
1463 if string.find(greetline, " Novonyx POP3") > 0:
1464 warnings = warnings + """
1465 Your mailserver is running Novonyx POP3. This server, at least as of
1466 version 2.17, seems to have problems handling and reporting seen bits.
1467 You may have to use the fetchall option.
1470 if string.find(greetline, " IMS POP3") > 0:
1471 warnings = warnings + """
1472 Some servers issuing the greeting line 'IMS POP3' have been known to
1473 do byte-stuffing incorrectly. This means that if a message you receive
1474 has a . (period) at start of line, fetchmail will become confused and
1475 probably wedge itself. (This bug was recorded on IMS POP3 0.86.)
1479 ### IMAP servers start here
1481 if string.find(greetline, "GroupWise") > 0:
1482 warnings = warnings + """
1483 The Novell GroupWise IMAP server would be better named GroupFoolish;
1484 it is (according to the designer of IMAP) unusably broken. Among
1485 other things, it doesn't include a required content length in its
1486 BODY[TEXT] response.<p>
1488 Fetchmail works around this problem, but we strongly recommend voting
1489 with your dollars for a server that isn't brain-dead. If you stick
1490 with code as shoddy as GroupWise seems to be, you will probably pay
1491 for it with other problems.<p>
1494 if string.find(greetline, "InterChange") > 0:
1495 warnings = warnings + """
1497 The InterChange IMAP server at release levels below 3.61.08 screws up
1498 on mail with attachments. It doesn't fetch them if you give it a
1499 BODY[TEXT] request, though it does if you request RFC822.TEXT.
1500 According to the IMAP RFCs and their maintainer these should be
1501 equivalent -- and we can't drop the BODY[TEXT] form because M$
1502 Exchange (quite legally under RFC2062) rejectsit. The InterChange
1503 folks claim to have fixed this bug in 3.61.08.
1506 if string.find(greetline, "Imail") > 0:
1507 warnings = warnings + """
1508 We've seen a bug report indicating that this IMAP server (at least as of
1509 version 5.0.7) returns an invalid body size for messages with MIME
1510 attachments; the effect is to drop the attachments on the floor. We
1511 recommend you upgrade to a non-broken IMAP server.
1514 if string.find(greetline, "Domino IMAP4") > 0:
1515 warnings = warnings + """
1516 Your IMAP server appears to be Lotus Domino. This server, at least up
1517 to version 4.6.2a, has a bug in its generation of MIME boundaries (see
1518 the details in the fetchmail FAQ). As a result, even MIME aware MUAs
1519 will see attachments as part of the message text. If your Domino server's
1520 POP3 facility is enabled, we recommend you fall back on it.
1524 ### Checks for protocol variants start here
1526 closebrak = string.find(greetline, ">")
1527 if closebrak > 0 and greetline[closebrak+1] == "\r":
1528 warnings = warnings + """
1529 It looks like you could use APOP on this server and avoid sending it your
1530 password in clear. You should talk to the mailserver administrator about
1534 if string.find(greetline, "IMAP2bis") > 0:
1535 warnings = warnings + """
1536 IMAP2bis servers have a minor problem; they can't peek at messages without
1537 marking them seen. If you take a line hit during the retrieval, the
1538 interrupted message may get left on the server, marked seen.
1540 To work around this, it is recommended that you set the `fetchall'
1541 option on all user entries associated with this server, so any stuck
1542 mail will be retrieved next time around.
1544 To fix this bug, upgrade to an IMAP4 server. The fetchmail FAQ includes
1545 a pointer to an open-source implementation.
1548 if string.find(greetline, "IMAP4rev1") > 0:
1549 warnings = warnings + """
1550 I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
1551 remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
1552 has therefore been extremely well tested with this class of server.
1556 warnings = warnings + """
1557 Fetchmail doesn't know anything special about this server type.
1561 # Display success window with warnings
1562 title = "Autoprobe of " + realhost + " succeeded"
1563 confirm = "The " + protocol + " server said:\n\n" + greetline + warnings
1564 self.protocol.set(protocol)
1565 self.service.set(defaultports[protocol])
1566 confwin.title(title)
1567 confwin.iconname(title)
1568 Label(confwin, text=title).pack()
1569 Message(confwin, text=confirm, width=600).pack()
1570 Button(confwin, text='Done',
1571 command=lambda x=confwin: x.destroy(), bd=2).pack()
1574 # User editing stuff
1578 'title' : 'User option help',
1579 'banner': 'User options',
1581 You may use this panel to set options
1582 that may differ between individual
1585 Once you have a user configuration set
1586 up as you like it, you can select `OK' to
1587 store it in the user list maintained in
1588 the site configuration window.
1590 If you wish to discard the changes you have
1591 made to user options, select `Quit'.
1595 'title' : 'Local name help',
1596 'banner': 'Local names',
1598 The local name(s) in a user entry are the
1599 people on the client machine who should
1600 receive mail from the poll described.
1602 Note: if a user entry has more than one
1603 local name, messages will be retrieved
1604 in multidrop mode. This complicates
1605 the configuration issues; see the manual
1606 page section on multidrop mode.
1608 Warning: Be careful with local names
1609 such as foo@bar.com, as that can cause
1610 the mail to be sent to foo@bar.com instead
1611 of sending it to your local system.
1614 class UserEdit(Frame, MyWidget):
1615 def __init__(self, username, parent):
1616 self.parent = parent
1618 for user in parent.server.users:
1619 if user.remote == username:
1621 if self.user == None:
1623 self.user.remote = username
1624 self.user.localnames = [username]
1625 parent.server.users.append(self.user)
1627 def edit(self, mode, master=None):
1628 Frame.__init__(self, master)
1630 self.master.title('Fetchmail user ' + self.user.remote
1631 + ' querying ' + self.parent.server.pollname);
1632 self.master.iconname('Fetchmail user ' + self.user.remote);
1633 self.post(User, 'user')
1634 self.makeWidgets(mode, self.parent.server.pollname)
1635 self.keepalive = [] # Use this to anchor the PhotoImage object
1636 make_icon_window(self, fetchmail_icon)
1639 # self.wait_window()
1643 # Yes, this test can fail -- if you delete the parent window.
1644 if self.parent.subwidgets.has_key(self.user.remote):
1645 del self.parent.subwidgets[self.user.remote]
1646 self.master.destroy()
1649 if ConfirmQuit(self, 'user option editing'):
1654 for x in self.user.localnames: ok = ok + (string.find(x, '@') != -1)
1655 if ok == 0 or Dialog(self,
1656 title = "Really accept an embedded '@' ?",
1657 text = "Local names with an embedded '@', such as in foo@bar "
1658 "might result in your mail being sent to foo@bar.com "
1659 "instead of your local system.\n Are you sure you want "
1660 "a local user name with an '@' in it?",
1661 bitmap = 'question',
1662 strings = ('Yes', 'No'),
1663 default = 1).num == 0:
1664 self.fetch(User, 'user')
1667 def makeWidgets(self, mode, servername):
1668 dispose_window(self,
1669 "User options for " + self.user.remote + " querying " + servername,
1672 if mode != 'novice':
1673 leftwin = Frame(self);
1677 secwin = Frame(leftwin, relief=RAISED, bd=5)
1678 Label(secwin, text="Authentication").pack(side=TOP)
1679 LabeledEntry(secwin, 'Password:',
1680 self.password, '12').pack(side=TOP, fill=X)
1681 secwin.pack(fill=X, anchor=N)
1683 if 'ssl' in feature_options or 'ssl' in dictmembers:
1684 sslwin = Frame(leftwin, relief=RAISED, bd=5)
1685 Checkbutton(sslwin, text="Use SSL?",
1686 variable=self.ssl).pack(side=TOP, fill=X)
1687 LabeledEntry(sslwin, 'SSL key:',
1688 self.sslkey, '14').pack(side=TOP, fill=X)
1689 LabeledEntry(sslwin, 'SSL certificate:',
1690 self.sslcert, '14').pack(side=TOP, fill=X)
1691 Checkbutton(sslwin, text="Check server SSL certificate?",
1692 variable=self.sslcertck).pack(side=TOP, fill=X)
1693 LabeledEntry(sslwin, 'SSL trusted certificate directory:',
1694 self.sslcertpath, '14').pack(side=TOP, fill=X)
1695 LabeledEntry(sslwin, 'SSL CommonName:',
1696 self.sslcommonname, '14').pack(side=TOP, fill=X)
1697 LabeledEntry(sslwin, 'SSL key fingerprint:',
1698 self.sslfingerprint, '14').pack(side=TOP, fill=X)
1699 sslwin.pack(fill=X, anchor=N)
1701 names = Frame(leftwin, relief=RAISED, bd=5)
1702 Label(names, text="Local names").pack(side=TOP)
1703 ListEdit("New name: ",
1704 self.user.localnames, None, None, names, localhelp)
1705 names.pack(fill=X, anchor=N)
1707 if mode != 'novice':
1708 targwin = Frame(leftwin, relief=RAISED, bd=5)
1709 Label(targwin, text="Forwarding Options").pack(side=TOP)
1710 Label(targwin, text="Listeners to forward to").pack(side=TOP)
1711 ListEdit("New listener:",
1712 self.user.smtphunt, None, None, targwin, None)
1713 Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
1714 ListEdit("Domains:",
1715 self.user.fetchdomains, None, None, targwin, None)
1716 LabeledEntry(targwin, 'Use domain on RCPT TO line:',
1717 self.smtpaddress, '26').pack(side=TOP, fill=X)
1718 LabeledEntry(targwin, 'Set fixed RCPT TO address:',
1719 self.smtpname, '26').pack(side=TOP, fill=X)
1720 LabeledEntry(targwin, 'Connection setup command:',
1721 self.preconnect, '26').pack(side=TOP, fill=X)
1722 LabeledEntry(targwin, 'Connection wrapup command:',
1723 self.postconnect, '26').pack(side=TOP, fill=X)
1724 LabeledEntry(targwin, 'Local delivery agent:',
1725 self.mda, '26').pack(side=TOP, fill=X)
1726 LabeledEntry(targwin, 'BSMTP output file:',
1727 self.bsmtp, '26').pack(side=TOP, fill=X)
1728 LabeledEntry(targwin, 'Listener spam-block codes:',
1729 self.antispam, '26').pack(side=TOP, fill=X)
1730 LabeledEntry(targwin, 'Pass-through properties:',
1731 self.properties, '26').pack(side=TOP, fill=X)
1732 Checkbutton(targwin, text="Use LMTP?",
1733 variable=self.lmtp).pack(side=TOP, fill=X)
1734 targwin.pack(fill=X, anchor=N)
1736 if mode != 'novice':
1737 leftwin.pack(side=LEFT, fill=X, anchor=N)
1738 rightwin = Frame(self)
1742 optwin = Frame(rightwin, relief=RAISED, bd=5)
1743 Label(optwin, text="Processing Options").pack(side=TOP)
1744 Checkbutton(optwin, text="Suppress deletion of messages after reading",
1745 variable=self.keep).pack(side=TOP, anchor=W)
1746 Checkbutton(optwin, text="Fetch old messages as well as new",
1747 variable=self.fetchall).pack(side=TOP, anchor=W)
1748 if mode != 'novice':
1749 Checkbutton(optwin, text="Flush seen messages before retrieval",
1750 variable=self.flush).pack(side=TOP, anchor=W)
1751 Checkbutton(optwin, text="Flush oversized messages before retrieval",
1752 variable=self.limitflush).pack(side=TOP, anchor=W)
1753 Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply",
1754 variable=self.rewrite).pack(side=TOP, anchor=W)
1755 Checkbutton(optwin, text="Force CR/LF at end of each line",
1756 variable=self.forcecr).pack(side=TOP, anchor=W)
1757 Checkbutton(optwin, text="Strip CR from end of each line",
1758 variable=self.stripcr).pack(side=TOP, anchor=W)
1759 Checkbutton(optwin, text="Pass 8 bits even though SMTP says 7BIT",
1760 variable=self.pass8bits).pack(side=TOP, anchor=W)
1761 Checkbutton(optwin, text="Undo MIME armoring on header and body",
1762 variable=self.mimedecode).pack(side=TOP, anchor=W)
1763 Checkbutton(optwin, text="Drop Status lines from forwarded messages",
1764 variable=self.dropstatus).pack(side=TOP, anchor=W)
1765 Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
1766 variable=self.dropdelivered).pack(side=TOP, anchor=W)
1769 if mode != 'novice':
1770 limwin = Frame(rightwin, relief=RAISED, bd=5)
1771 Label(limwin, text="Resource Limits").pack(side=TOP)
1772 LabeledEntry(limwin, 'Message size limit:',
1773 self.limit, '30').pack(side=TOP, fill=X)
1774 LabeledEntry(limwin, 'Size warning interval:',
1775 self.warnings, '30').pack(side=TOP, fill=X)
1776 LabeledEntry(limwin, 'Max messages to fetch per poll:',
1777 self.fetchlimit, '30').pack(side=TOP, fill=X)
1778 LabeledEntry(limwin, 'Max message sizes to fetch per transaction:',
1779 self.fetchsizelimit, '30').pack(side=TOP, fill=X)
1780 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1781 LabeledEntry(limwin, 'Use fast UIDL:',
1782 self.fastuidl, '30').pack(side=TOP, fill=X)
1783 LabeledEntry(limwin, 'Max messages to forward per poll:',
1784 self.batchlimit, '30').pack(side=TOP, fill=X)
1785 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1786 LabeledEntry(limwin, 'Interval between expunges:',
1787 self.expunge, '30').pack(side=TOP, fill=X)
1788 Checkbutton(limwin, text="Idle after each poll (IMAP only)",
1789 variable=self.idle).pack(side=TOP, anchor=W)
1792 if self.parent.server.protocol == 'IMAP':
1793 foldwin = Frame(rightwin, relief=RAISED, bd=5)
1794 Label(foldwin, text="Remote folders (IMAP only)").pack(side=TOP)
1795 ListEdit("New folder:", self.user.mailboxes,
1796 None, None, foldwin, None)
1797 foldwin.pack(fill=X, anchor=N)
1799 if mode != 'novice':
1800 rightwin.pack(side=LEFT)
1806 # Top-level window that offers either novice or expert mode
1807 # (but not both at once; it disappears when one is selected).
1810 class Configurator(Frame):
1811 def __init__(self, outfile, master, onexit, parent):
1812 Frame.__init__(self, master)
1813 self.outfile = outfile
1814 self.onexit = onexit
1815 self.parent = parent
1816 self.master.title('fetchmail configurator');
1817 self.master.iconname('fetchmail configurator');
1819 self.keepalive = [] # Use this to anchor the PhotoImage object
1820 make_icon_window(self, fetchmail_icon)
1822 Message(self, text="""
1823 Use `Novice Configuration' for basic fetchmail setup;
1824 with this, you can easily set up a single-drop connection
1825 to one remote mail server.
1826 """, width=600).pack(side=TOP)
1827 Button(self, text='Novice Configuration',
1828 fg='blue', command=self.novice).pack()
1830 Message(self, text="""
1831 Use `Expert Configuration' for advanced fetchmail setup,
1832 including multiple-site or multidrop connections.
1833 """, width=600).pack(side=TOP)
1834 Button(self, text='Expert Configuration',
1835 fg='blue', command=self.expert).pack()
1837 Message(self, text="""
1838 Or you can just select `Quit' to leave the configurator now and
1839 return to the main panel.
1840 """, width=600).pack(side=TOP)
1841 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1842 master.protocol("WM_DELETE_WINDOW", self.leave)
1845 self.master.destroy()
1846 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1849 self.master.destroy()
1850 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1853 self.master.destroy()
1856 # Run a command in a scrolling text widget, displaying its output
1858 class RunWindow(Frame):
1859 def __init__(self, command, master, parent):
1860 Frame.__init__(self, master)
1861 self.master = master
1862 self.master.title('fetchmail run window');
1863 self.master.iconname('fetchmail run window');
1866 text="Running "+command,
1867 bd=2).pack(side=TOP, pady=10)
1868 self.keepalive = [] # Use this to anchor the PhotoImage object
1869 make_icon_window(self, fetchmail_icon)
1871 # This is a scrolling text window
1872 textframe = Frame(self)
1873 scroll = Scrollbar(textframe)
1874 self.textwidget = Text(textframe, setgrid=TRUE)
1875 textframe.pack(side=TOP, expand=YES, fill=BOTH)
1876 self.textwidget.config(yscrollcommand=scroll.set)
1877 self.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
1878 scroll.config(command=self.textwidget.yview)
1879 scroll.pack(side=RIGHT, fill=BOTH)
1880 textframe.pack(side=TOP)
1882 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1884 self.update() # Draw widget before executing fetchmail
1886 # Always look for a runnable command in the directory we're running in
1887 # first. This avoids some obscure version-skew errors that can occur
1888 # if you pick up an old fetchmail from the standard system locations.
1889 os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"]
1890 child_stdout = os.popen(command + " 2>&1 </dev/null", "r")
1892 ch = child_stdout.read(1)
1895 self.textwidget.insert(END, ch)
1896 self.textwidget.insert(END, "Done.")
1897 self.textwidget.see(END);
1900 self.master.destroy()
1902 # Here's where we choose either configuration or launching
1904 class MainWindow(Frame):
1905 def __init__(self, outfile, master=None):
1906 Frame.__init__(self, master)
1907 self.outfile = outfile
1908 self.master.title('fetchmail launcher');
1909 self.master.iconname('fetchmail launcher');
1912 text='Fetchmailconf ' + version,
1913 bd=2).pack(side=TOP, pady=10)
1914 self.keepalive = [] # Use this to anchor the PhotoImage object
1915 make_icon_window(self, fetchmail_icon)
1918 ## Test icon display with the following:
1919 # icon_image = PhotoImage(data=fetchmail_icon)
1920 # Label(self, image=icon_image).pack(side=TOP, pady=10)
1921 # self.keepalive.append(icon_image)
1923 Message(self, text="""
1924 Use `Configure fetchmail' to tell fetchmail about the remote
1925 servers it should poll (the host name, your username there,
1926 whether to use POP or IMAP, and so forth).
1927 """, width=600).pack(side=TOP)
1928 self.configbutton = Button(self, text='Configure fetchmail',
1929 fg='blue', command=self.configure)
1930 self.configbutton.pack()
1932 Message(self, text="""
1933 Use `Run fetchmail' to run fetchmail with debugging enabled.
1934 This is a good way to test out a new configuration.
1935 """, width=600).pack(side=TOP)
1936 Button(self, text='Run fetchmail',fg='blue', command=self.test).pack()
1938 Message(self, text="""
1939 Use `Run fetchmail' to run fetchmail in foreground.
1940 Progress messages will be shown, but not debug messages.
1941 """, width=600).pack(side=TOP)
1942 Button(self, text='Run fetchmail', fg='blue', command=self.run).pack()
1944 Message(self, text="""
1945 Or you can just select `Quit' to exit the launcher now.
1946 """, width=600).pack(side=TOP)
1947 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1949 def configure(self):
1950 self.configbutton.configure(state=DISABLED)
1951 Configurator(self.outfile, Toplevel(),
1952 lambda self=self: self.configbutton.configure(state=NORMAL),
1955 cmd = "fetchmail -N -d0 --nosyslog -v"
1957 cmd = cmd + " -f " + rcfile
1958 RunWindow(cmd, Toplevel(), self)
1961 cmd = "fetchmail -N -d0"
1963 cmd = cmd + " -f " + rcfile
1964 RunWindow(cmd, Toplevel(), self)
1969 # Functions for turning a dictionary into an instantiated object tree.
1971 def intersect(list1, list2):
1972 # Compute set intersection of lists
1979 def setdiff(list1, list2):
1980 # Compute set difference of lists
1987 def copy_instance(toclass, fromdict):
1988 # Initialize a class object of given type from a conformant dictionary.
1989 for fld in fromdict.keys():
1990 if not fld in dictmembers:
1991 dictmembers.append(fld)
1992 # The `optional' fields are the ones we can ignore for purposes of
1993 # conformability checking; they'll still get copied if they are
1994 # present in the dictionary.
1995 optional = ('interface', 'monitor',
1996 'esmtpname', 'esmtppassword',
1997 'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
1998 'sslcertpath', 'sslcommonname', 'sslfingerprint', 'showdots')
1999 class_sig = setdiff(toclass.__dict__.keys(), optional)
2001 dict_keys = setdiff(fromdict.keys(), optional)
2003 common = intersect(class_sig, dict_keys)
2004 if 'typemap' in class_sig:
2005 class_sig.remove('typemap')
2006 if tuple(class_sig) != tuple(dict_keys):
2007 print "Fields don't match what fetchmailconf expected:"
2008 # print "Class signature: " + `class_sig`
2009 # print "Dictionary keys: " + `dict_keys`
2010 diff = setdiff(class_sig, common)
2012 print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
2013 diff = setdiff(dict_keys, common)
2015 print "Not matched in dictionary keys: " + `diff`
2018 for x in fromdict.keys():
2019 setattr(toclass, x, fromdict[x])
2022 # And this is the main sequence. How it works:
2024 # First, call `fetchmail --configdump' and trap the output in a tempfile.
2025 # This should fill it with a Python initializer for a variable `fetchmailrc'.
2026 # Run execfile on the file to pull fetchmailrc into Python global space.
2027 # You don't want static data, though; you want, instead, a tree of objects
2028 # with the same data members and added appropriate methods.
2030 # This is what the copy_instance function() is for. It tries to copy a
2031 # dictionary field by field into a class, aborting if the class and dictionary
2032 # have different data members (except for any typemap member in the class;
2033 # that one is strictly for use by the MyWidget supperclass).
2035 # Once the object tree is set up, require user to choose novice or expert
2036 # mode and instantiate an edit object for the configuration. Class methods
2037 # will take it all from there.
2039 # Options (not documented because they're for fetchmailconf debuggers only):
2040 # -d: Read the configuration and dump it to stdout before editing. Dump
2041 # the edited result to stdout as well.
2042 # -f: specify the run control file to read.
2044 if __name__ == '__main__':
2046 if not os.environ.has_key("DISPLAY"):
2047 print "fetchmailconf must be run under X"
2050 fetchmail_icon = """
2051 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
2052 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
2053 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
2054 CJR7Y/+UMdbOxnNrY97Ove/Wvd7GrZyEa961jL2Ua9alc86ca7WEUntSKcaMSqVjGNZ7GGM5CNa1
2055 jPfOnN6tc3taMffeve/WtWtaQv/OjGtSMYRzWv/erda1hM6te7WUY62MWs61jP/vzv/ntda9jL2l
2056 czEhAO/n1oyEc//elDEpGEo5EOfexpyUe+/epefevffvxnNrQpyUStbWzsbGvZyclN7ezmNjWv//
2057 5/f33qWllNbWve/vzv//1ufnve/vvf//xvf3vefnrf//taWlc0pKMf//pbW1Y///jKWlWq2tWsbG
2058 Y///c97eUvf3Ut7nc+/3a87We8bOjOfv1u/37/f//621tb3Gxtbn52Nra87n53uUlJTv/6W9xuf3
2059 /8bW3iExOXu11tbv/5TW/4TO/63e/zmt/1KUxlK1/2u9/wCM/73GzrXG1gBKjACE/87e72NzhCkx
2060 OaXO92OMtUql/xCE/wApUtbe57W9xnN7hHut52Ot/xBSnABKnABavQB7/2ul7zF71gBr77XO73Oc
2061 1lqc9yFSlBApSimE/wAYOQApY0J7zlKM5wAxhABS1gBj/6W95wAhWgA5nAAYSgBS7wBS/wBK9wAp
2062 jABC5wBK/wApnABC/wApxgAhtYSMtQAQYwAp/3OE74SMxgAYxlpjvWNr70pS/wgQ3sbGzs7O1qWl
2063 3qWl70pKe0JC/yEhlCkp/wgI/wAAEAAAIQAAKQAAOQAASgAAUgAAYwAAawAAlAAAnAAApQAArQAA
2064 zgAA1gAA5wAA9wAA/0pC/xgQ52Na9ykhe4R7zikhYxgQSjEpQgAAACwAAAAAPAAoAAAI/wABCBxI
2065 sKDBgwgTKiRIYKHDhxARIvgXsaLFhGgEUBSYoKPHjyBDihxJkuS/kwNLqlzJcuTJjQBaypxpEiVH
2066 mjhxvkyZs2fLnTd9ehxAtKjRo0ZrwhTasUsENhYHKOUpk1E3j11mxCBiQVLEBlJd2owp9iVRjwUs
2067 zMCQ5IcLD4saPVxjIKxIoGTvvqSoyFEFGTBeqEhyxAoSFR/USGKVcEGBAwDshsSr1OYTEyhQpJiS
2068 ZcoUKWOQtJDRJFSaggzUGBgoGSTlsjahlPCRIkWVKT16THHRIoqIISBIEUgAYIGBhgRbf3ytFygU
2069 FZp9UDmxQkkMCRwyZKDBQy4aApABhP8XqNwj88l7BVpQYZtF5iArWgwAgGZBq24HU7OeGhQ90PVA
2070 aKZZCiiUMJ9ArSTEwGqR8ZeXfzbV0MIIMQTBwoUdxDDfAm8sZFyDZVEF4UYSKBEBD0+k6IEFPMxH
2071 3FzldXSea+kBgANJSOWIlIMhXZXAXv+c1WM3PuJEpH8iuhbAkv+MdENPRHaTRkdF/jiWSKCAwlKW
2072 VbbkY5Q0LgUSKExgoYBKCjCxARpdltQNKHaUoYAddnR53lVRnJLKBWh4RIEGCZx5FSOv1OLNDUVe
2073 deZHaWiZAB35fIOGNtbEUeV5oGAByzPOrBPFGt3kwEgxITACSg5oLGGLMg60oQAjaNz/oAAcN4Ai
2074 a0c3kHFDK3jYsw4g9sRzBgPLXdkRrBrQ8gsWQUxCCRZX9IJNBQ1s8IgCdeBCzBYN6IBIN2TUsQYd
2075 dXhDBxdzlAHOHHKEcocZdWwDjx8MTCmjsR2FMAstw1RyiSzHqPLALaOwk8QmzCzDCSi0xJKMMk4E
2076 Yw8389iTDT32GAKOPf7YY0Aa9tATyD3w/EGsefgmgEYUtPiChLKWQDMBJtEUgYkzH2RiTgGfTMCI
2077 Mlu0Yc85hNiDziH2tMqOGL72QY47gshLb7Fi4roELcjoQIsxWpDwQyfS2OCJMkLI4YUmyhgxSTVg
2078 CP2FHPZ80UDcieBjStNPD5LPOyZT/y0iHGiMwswexDSzRiRq6KIMJBc4M8skwKAyChia2KPH3P24
2079 YU8/lFhOTj152OPOHuXMU4g48vCRiN/9rZGLMdS4csUu1JzDgxuipOMDHMKsAwEnq/ByzTrrZMNO
2080 OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
2081 xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
2082 gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
2084 # The base64 data in the string above was generated by the following procedure:
2087 # print base64.encodestring(open("fetchmail.gif", "rb").read())
2091 (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV", ["help",
2093 dump = rcfile = None;
2094 for (switch, val) in options:
2095 if (switch == '-d'):
2097 elif (switch == '-f'):
2099 elif (switch == '-h' or switch == '--help'):
2101 Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|--help|-V|--version}
2102 -d - dump configuration (for debugging)
2103 -f fmrc - read alternate fetchmailrc file
2104 --help, -h - print this help text and quit
2105 --version, -V - print fetchmailconf version and quit
2108 elif (switch == '-V' or switch == '--version'):
2109 print "fetchmailconf %s" % version
2111 Copyright (C) 1997 - 2003 Eric S. Raymond
2112 Copyright (C) 2005, 2006, 2008, 2009 Matthias Andree
2113 fetchmailconf comes with ABSOLUTELY NO WARRANTY. This is free software, you are
2114 welcome to redistribute it under certain conditions. Please see the file
2115 COPYING in the source or documentation directory for details."""
2118 # Get client host's FQDN
2119 hostname = socket.gethostbyaddr(socket.gethostname())[0]
2122 ConfigurationDefaults = Configuration()
2123 ServerDefaults = Server()
2124 UserDefaults = User()
2126 # Read the existing configuration. We set the umask to 077 to make sure
2127 # that group & other read/write permissions are shut off -- we wouldn't
2128 # want crackers to snoop password information out of the tempfile.
2129 tmpfile = tempfile.mktemp()
2131 cmd = "umask 077 && fetchmail </dev/null -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2133 cmd = "umask 077 && fetchmail </dev/null --configdump --nosyslog >" + tmpfile
2138 print "`" + cmd + "' run failure, status " + `s`
2141 print "Unknown error while running fetchmail --configdump"
2148 print "Can't read configuration output of fetchmail --configdump."
2154 # The tricky part -- initializing objects from the configuration global
2155 # `Configuration' is the top level of the object tree we're going to mung.
2156 # The dictmembers list is used to track the set of fields the dictionary
2157 # contains; in particular, we can use it to tell whether things like the
2158 # monitor, interface, ssl, sslkey, or sslcert fields are present.
2160 Fetchmailrc = Configuration()
2161 copy_instance(Fetchmailrc, fetchmailrc)
2162 Fetchmailrc.servers = [];
2163 for server in fetchmailrc['servers']:
2165 copy_instance(Newsite, server)
2166 Fetchmailrc.servers.append(Newsite)
2168 for user in server['users']:
2170 copy_instance(Newuser, user)
2171 Newsite.users.append(Newuser)
2173 # We may want to display the configuration and quit
2175 print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2177 # The theory here is that -f alone sets the rcfile location,
2178 # but -d and -f together mean the new configuration should go to stdout.
2179 if not rcfile and not dump:
2180 rcfile = os.environ["HOME"] + "/.fetchmailrc"
2182 # OK, now run the configuration edit
2183 root = MainWindow(rcfile)
2186 # The following sets edit modes for GNU EMACS