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.users = [] # List of user entries for site
111 ('pollname', 'String'),
113 ('active', 'Boolean'),
115 ('protocol', 'String'),
116 ('service', 'String'),
120 ('envelope', 'String'),
122 ('qvirtual', 'String'),
125 # leave localdomains out
126 ('interface', 'String'),
127 ('monitor', 'String'),
128 ('plugin', 'String'),
129 ('plugout', 'String'),
130 ('esmtpname', 'String'),
131 ('esmtppassword', 'String'),
132 ('principal', 'String'),
133 ('tracepolls','Boolean'),
134 ('badheader', 'Boolean'))
136 def dump(self, folded):
138 if self.active: res = res + "poll"
139 else: res = res + "skip"
140 res = res + (" " + self.pollname)
142 res = res + (" via " + str(self.via) + "\n");
143 if self.protocol != ServerDefaults.protocol:
144 res = res + " with proto " + self.protocol
145 if self.service and self.protocol and self.service != defaultports[self.protocol] and defaultports[self.protocol] and self.service != ianaservices[defaultports[self.protocol]]:
146 res = res + " service " + self.service
147 if self.timeout != ServerDefaults.timeout:
148 res = res + " timeout " + `self.timeout`
149 if self.interval != ServerDefaults.interval:
150 res = res + " interval " + `self.interval`
151 if self.envelope != ServerDefaults.envelope or self.envskip != ServerDefaults.envskip:
153 res = res + " envelope " + `self.envskip` + " " + self.envelope
155 res = res + " envelope " + self.envelope
157 res = res + (" qvirtual " + str(self.qvirtual) + "\n");
158 if self.auth != ServerDefaults.auth:
159 res = res + " auth " + self.auth
160 if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl:
161 res = res + " and options"
162 if self.dns != ServerDefaults.dns:
163 res = res + flag2str(self.dns, 'dns')
164 if self.uidl != ServerDefaults.uidl:
165 res = res + flag2str(self.uidl, 'uidl')
166 if folded: res = res + "\n "
167 else: res = res + " "
173 if self.aka and self.localdomains: res = res + " "
174 if self.localdomains:
175 res = res + ("localdomains")
176 for x in self.localdomains:
178 if (self.aka or self.localdomains):
185 res = res + "tracepolls\n"
188 res = res + " interface " + str(self.interface)
190 res = res + " monitor " + str(self.monitor)
192 res = res + " plugin " + `self.plugin`
194 res = res + " plugout " + `self.plugout`
196 res = res + " principal " + `self.principal`
198 res = res + " esmtpname " + `self.esmtpname`
199 if self.esmtppassword:
200 res = res + " esmtppassword " + `self.esmtppassword`
201 if self.interface or self.monitor or self.principal or self.plugin or self.plugout:
205 res = res + "bad-header accept "
207 if res[-1] == " ": res = res[0:-1]
209 for user in self.users:
210 res = res + repr(user)
214 def __delitem__(self, name):
215 for ui in range(len(self.users)):
216 if self.users[ui].remote == name:
221 return self.dump(TRUE)
224 return "[Server: " + self.dump(FALSE) + "]"
228 if os.environ.has_key("USER"):
229 self.remote = os.environ["USER"] # Remote username
230 elif os.environ.has_key("LOGNAME"):
231 self.remote = os.environ["LOGNAME"]
233 print "Can't get your username!"
235 self.localnames = [self.remote,]# Local names
236 self.password = None # Password for mail account access
237 self.mailboxes = [] # Remote folders to retrieve from
238 self.smtphunt = [] # Hosts to forward to
239 self.fetchdomains = [] # Domains to fetch from
240 self.smtpaddress = None # Append this to MAIL FROM line
241 self.smtpname = None # Use this for RCPT TO
242 self.preconnect = None # Connection setup
243 self.postconnect = None # Connection wrapup
244 self.mda = None # Mail Delivery Agent
245 self.bsmtp = None # BSMTP output file
246 self.lmtp = FALSE # Use LMTP rather than SMTP?
247 self.antispam = "" # Listener's spam-block code
248 self.keep = FALSE # Keep messages
249 self.flush = FALSE # Flush messages
250 self.limitflush = FALSE # Flush oversized messages
251 self.fetchall = FALSE # Fetch old messages
252 self.rewrite = TRUE # Rewrite message headers
253 self.forcecr = FALSE # Force LF -> CR/LF
254 self.stripcr = FALSE # Strip CR
255 self.pass8bits = FALSE # Force BODY=7BIT
256 self.mimedecode = FALSE # Undo MIME armoring
257 self.dropstatus = FALSE # Drop incoming Status lines
258 self.dropdelivered = FALSE # Drop incoming Delivered-To lines
259 self.idle = FALSE # IDLE after poll
260 self.limit = 0 # Message size limit
261 self.warnings = 3600 # Size warning interval (see tunable.h)
262 self.fetchlimit = 0 # Max messages fetched per batch
263 self.fetchsizelimit = 100 # Max message sizes fetched per transaction
264 self.fastuidl = 4 # Do fast uidl 3 out of 4 times
265 self.batchlimit = 0 # Max message forwarded per batch
266 self.expunge = 0 # Interval between expunges (IMAP)
267 self.ssl = 0 # Enable Seccure Socket Layer
268 self.sslkey = None # SSL key filename
269 self.sslcert = None # SSL certificate filename
270 self.sslproto = None # Force SSL?
271 self.sslcertck = 0 # Enable strict SSL cert checking
272 self.sslcertpath = None # Path to trusted certificates
273 self.sslcommonname = None # SSL CommonName to expect
274 self.sslfingerprint = None # SSL key fingerprint to check
275 self.properties = None # Extension properties
277 ('remote', 'String'),
278 # leave out mailboxes and localnames
279 ('password', 'String'),
280 # Leave out smtphunt, fetchdomains
281 ('smtpaddress', 'String'),
282 ('smtpname', 'String'),
283 ('preconnect', 'String'),
284 ('postconnect', 'String'),
288 ('antispam', 'String'),
290 ('flush', 'Boolean'),
291 ('limitflush', 'Boolean'),
292 ('fetchall', 'Boolean'),
293 ('rewrite', 'Boolean'),
294 ('forcecr', 'Boolean'),
295 ('stripcr', 'Boolean'),
296 ('pass8bits', 'Boolean'),
297 ('mimedecode', 'Boolean'),
298 ('dropstatus', 'Boolean'),
299 ('dropdelivered', 'Boolean'),
303 ('fetchlimit', 'Int'),
304 ('fetchsizelimit', 'Int'),
306 ('batchlimit', 'Int'),
309 ('sslkey', 'String'),
310 ('sslcert', 'String'),
311 ('sslcertck', 'Boolean'),
312 ('sslcertpath', 'String'),
313 ('sslcommonname', 'String'),
314 ('sslfingerprint', 'String'),
315 ('properties', 'String'))
319 res = res + "user " + `self.remote` + " there ";
321 res = res + "with password " + `self.password` + " "
324 for x in self.localnames:
325 res = res + " " + `x`
327 if (self.keep != UserDefaults.keep
328 or self.flush != UserDefaults.flush
329 or self.limitflush != UserDefaults.limitflush
330 or self.fetchall != UserDefaults.fetchall
331 or self.rewrite != UserDefaults.rewrite
332 or self.forcecr != UserDefaults.forcecr
333 or self.stripcr != UserDefaults.stripcr
334 or self.pass8bits != UserDefaults.pass8bits
335 or self.mimedecode != UserDefaults.mimedecode
336 or self.dropstatus != UserDefaults.dropstatus
337 or self.dropdelivered != UserDefaults.dropdelivered
338 or self.idle != UserDefaults.idle):
339 res = res + " options"
340 if self.keep != UserDefaults.keep:
341 res = res + flag2str(self.keep, 'keep')
342 if self.flush != UserDefaults.flush:
343 res = res + flag2str(self.flush, 'flush')
344 if self.limitflush != UserDefaults.limitflush:
345 res = res + flag2str(self.limitflush, 'limitflush')
346 if self.fetchall != UserDefaults.fetchall:
347 res = res + flag2str(self.fetchall, 'fetchall')
348 if self.rewrite != UserDefaults.rewrite:
349 res = res + flag2str(self.rewrite, 'rewrite')
350 if self.forcecr != UserDefaults.forcecr:
351 res = res + flag2str(self.forcecr, 'forcecr')
352 if self.stripcr != UserDefaults.stripcr:
353 res = res + flag2str(self.stripcr, 'stripcr')
354 if self.pass8bits != UserDefaults.pass8bits:
355 res = res + flag2str(self.pass8bits, 'pass8bits')
356 if self.mimedecode != UserDefaults.mimedecode:
357 res = res + flag2str(self.mimedecode, 'mimedecode')
358 if self.dropstatus != UserDefaults.dropstatus:
359 res = res + flag2str(self.dropstatus, 'dropstatus')
360 if self.dropdelivered != UserDefaults.dropdelivered:
361 res = res + flag2str(self.dropdelivered, 'dropdelivered')
362 if self.idle != UserDefaults.idle:
363 res = res + flag2str(self.idle, 'idle')
364 if self.limit != UserDefaults.limit:
365 res = res + " limit " + `self.limit`
366 if self.warnings != UserDefaults.warnings:
367 res = res + " warnings " + `self.warnings`
368 if self.fetchlimit != UserDefaults.fetchlimit:
369 res = res + " fetchlimit " + `self.fetchlimit`
370 if self.fetchsizelimit != UserDefaults.fetchsizelimit:
371 res = res + " fetchsizelimit " + `self.fetchsizelimit`
372 if self.fastuidl != UserDefaults.fastuidl:
373 res = res + " fastuidl " + `self.fastuidl`
374 if self.batchlimit != UserDefaults.batchlimit:
375 res = res + " batchlimit " + `self.batchlimit`
376 if self.ssl and self.ssl != UserDefaults.ssl:
377 res = res + flag2str(self.ssl, 'ssl')
378 if self.sslkey and self.sslkey != UserDefaults.sslkey:
379 res = res + " sslkey " + `self.sslkey`
380 if self.sslcert and self.sslcert != UserDefaults.sslcert:
381 res = res + " sslcert " + `self.sslcert`
382 if self.sslproto and self.sslproto != UserDefaults.sslproto:
383 res = res + " sslproto " + `self.sslproto`
384 if self.sslcertck and self.sslcertck != UserDefaults.sslcertck:
385 res = res + flag2str(self.sslcertck, 'sslcertck')
386 if self.sslcertpath and self.sslcertpath != UserDefaults.sslcertpath:
387 res = res + " sslcertpath " + `self.sslcertpath`
388 if self.sslcommonname and self.sslcommonname != UserDefaults.sslcommonname:
389 res = res + " sslcommonname " + `self.sslcommonname`
390 if self.sslfingerprint and self.sslfingerprint != UserDefaults.sslfingerprint:
391 res = res + " sslfingerprint " + `self.sslfingerprint`
392 if self.expunge != UserDefaults.expunge:
393 res = res + " expunge " + `self.expunge`
395 trimmed = self.smtphunt;
396 if trimmed != [] and trimmed[len(trimmed) - 1] == "localhost":
397 trimmed = trimmed[0:len(trimmed) - 1]
398 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
399 trimmed = trimmed[0:len(trimmed) - 1]
401 res = res + " smtphost "
405 trimmed = self.fetchdomains
406 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
407 trimmed = trimmed[0:len(trimmed) - 1]
409 res = res + " fetchdomains "
414 res = res + " folder"
415 for x in self.mailboxes:
416 res = res + ' "%s"' % x
418 for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'):
419 if getattr(self, fld):
420 res = res + " %s %s\n" % (fld, `getattr(self, fld)`)
421 if self.lmtp != UserDefaults.lmtp:
422 res = res + flag2str(self.lmtp, 'lmtp')
423 if self.antispam != UserDefaults.antispam:
424 res = res + " antispam " + self.antispam + "\n"
428 return "[User: " + repr(self) + "]"
434 # IANA port assignments and bogus 1109 entry
435 ianaservices = {"pop2":109,
442 # fetchmail protocol to IANA service name
443 defaultports = {"auto":None,
452 authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp",
456 'title' : 'List Selection Help',
457 'banner': 'List Selection',
459 You must select an item in the list box (by clicking on it).
462 def flag2str(value, string):
463 # make a string representation of a .fetchmailrc flag or negated flag
467 if value == FALSE: str = str + ("no ")
471 class LabeledEntry(Frame):
472 # widget consisting of entry field with caption to left
473 def bind(self, key, action):
474 self.E.bind(key, action)
477 def __init__(self, Master, text, textvar, lwidth, ewidth=12):
478 Frame.__init__(self, Master)
479 self.L = Label(self, {'text':text, 'width':lwidth, 'anchor':'w'})
480 self.E = Entry(self, {'textvar':textvar, 'width':ewidth})
481 self.L.pack({'side':'left'})
482 self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
484 def ButtonBar(frame, legend, ref, alternatives, depth, command):
485 # array of radio buttons, caption to left, picking from a string list
487 width = (len(alternatives)+1) / depth;
488 Label(bar, text=legend).pack(side=LEFT)
489 for column in range(width):
490 subframe = Frame(bar)
491 for row in range(depth):
492 ind = width * row + column
493 if ind < len(alternatives):
494 Radiobutton(subframe,
495 {'text':alternatives[ind],
497 'value':alternatives[ind],
498 'command':command}).pack(side=TOP, anchor=W)
500 # This is just a spacer
501 Radiobutton(subframe,
502 {'text':" ",'state':DISABLED}).pack(side=TOP, anchor=W)
503 subframe.pack(side=LEFT)
507 def helpwin(helpdict):
508 # help message window with a self-destruct button
510 helpwin.title(helpdict['title'])
511 helpwin.iconname(helpdict['title'])
512 Label(helpwin, text=helpdict['banner']).pack()
513 textframe = Frame(helpwin)
514 scroll = Scrollbar(textframe)
515 helpwin.textwidget = Text(textframe, setgrid=TRUE)
516 textframe.pack(side=TOP, expand=YES, fill=BOTH)
517 helpwin.textwidget.config(yscrollcommand=scroll.set)
518 helpwin.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
519 scroll.config(command=helpwin.textwidget.yview)
520 scroll.pack(side=RIGHT, fill=BOTH)
521 helpwin.textwidget.insert(END, helpdict['text']);
522 Button(helpwin, text='Done',
523 command=lambda x=helpwin: x.destroy(), bd=2).pack()
524 textframe.pack(side=TOP)
526 def make_icon_window(base, image):
528 # Some older pythons will error out on this
529 icon_image = PhotoImage(data=image)
530 icon_window = Toplevel()
531 Label(icon_window, image=icon_image, bg='black').pack()
532 base.master.iconwindow(icon_window)
533 # Avoid TkInter brain death. PhotoImage objects go out of
534 # scope when the enclosing function returns. Therefore
535 # we have to explicitly link them to something.
536 base.keepalive.append(icon_image)
540 class ListEdit(Frame):
541 # edit a list of values (duplicates not allowed) with a supplied editor hook
542 def __init__(self, newlegend, list, editor, deletor, master, helptxt):
544 self.deletor = deletor
547 # Set up a widget to accept new elements
548 self.newval = StringVar(master)
549 newwin = LabeledEntry(master, newlegend, self.newval, '12')
550 newwin.bind('<Double-1>', self.handleNew)
551 newwin.bind('<Return>', self.handleNew)
552 newwin.pack(side=TOP, fill=X, anchor=E)
554 # Edit the existing list
555 listframe = Frame(master)
556 scroll = Scrollbar(listframe)
557 self.listwidget = Listbox(listframe, height=0, selectmode='browse')
560 self.listwidget.insert(END, x)
561 listframe.pack(side=TOP, expand=YES, fill=BOTH)
562 self.listwidget.config(yscrollcommand=scroll.set)
563 self.listwidget.pack(side=LEFT, expand=YES, fill=BOTH)
564 scroll.config(command=self.listwidget.yview)
565 scroll.pack(side=RIGHT, fill=BOTH)
566 self.listwidget.config(selectmode=SINGLE, setgrid=TRUE)
567 self.listwidget.bind('<Double-1>', self.handleList);
568 self.listwidget.bind('<Return>', self.handleList);
572 Button(bf, text='Edit', command=self.editItem).pack(side=LEFT)
573 Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT)
575 self.helptxt = helptxt
576 Button(bf, text='Help', fg='blue',
577 command=self.help).pack(side=RIGHT)
581 helpwin(self.helptxt)
583 def handleList(self, event):
586 def handleNew(self, event):
587 item = self.newval.get()
589 entire = self.listwidget.get(0, self.listwidget.index('end'));
590 if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
591 self.listwidget.insert('end', item)
592 if self.list != None: self.list.append(item)
594 apply(self.editor, (item,))
598 select = self.listwidget.curselection()
603 if index and self.editor:
604 label = self.listwidget.get(index);
606 apply(self.editor, (label,))
608 def deleteItem(self):
609 select = self.listwidget.curselection()
613 index = string.atoi(select[0])
614 label = self.listwidget.get(index);
615 self.listwidget.delete(index)
616 if self.list != None:
618 if self.deletor != None:
619 apply(self.deletor, (label,))
621 def ConfirmQuit(frame, context):
624 text = 'Really quit ' + context + ' without saving?',
626 strings = ('Yes', 'No'),
630 def dispose_window(master, legend, help, savelegend='OK'):
631 dispose = Frame(master, relief=RAISED, bd=5)
632 Label(dispose, text=legend).pack(side=TOP,pady=10)
633 Button(dispose, text=savelegend, fg='blue',
634 command=master.save).pack(side=LEFT)
635 Button(dispose, text='Quit', fg='blue',
636 command=master.nosave).pack(side=LEFT)
637 Button(dispose, text='Help', fg='blue',
638 command=lambda x=help: helpwin(x)).pack(side=RIGHT)
643 # Common methods for Tkinter widgets -- deals with Tkinter declaration
644 def post(self, widgetclass, field):
645 for x in widgetclass.typemap:
646 if x[1] == 'Boolean':
647 setattr(self, x[0], BooleanVar(self))
648 elif x[1] == 'String':
649 setattr(self, x[0], StringVar(self))
651 setattr(self, x[0], IntVar(self))
652 source = getattr(getattr(self, field), x[0])
654 getattr(self, x[0]).set(source)
656 def fetch(self, widgetclass, field):
657 for x in widgetclass.typemap:
658 setattr(getattr(self, field), x[0], getattr(self, x[0]).get())
661 # First, code to set the global fetchmail run controls.
664 configure_novice_help = {
665 'title' : 'Fetchmail novice configurator help',
666 'banner': 'Novice configurator help',
668 In the `Novice Configurator Controls' panel, you can:
670 Press `Save' to save the new fetchmail configuration you have created.
671 Press `Quit' to exit without saving.
672 Press `Help' to bring up this help message.
674 In the `Novice Configuration' panels, you will set up the basic data
675 needed to create a simple fetchmail setup. These include:
677 1. The name of the remote site you want to query.
679 2. Your login name on that site.
681 3. Your password on that site.
683 4. A protocol to use (POP, IMAP, ETRN, etc.)
685 5. A poll interval in seconds.
686 If 0, fetchmail will run in the foreground once when started.
687 If > 0, fetchmail will run in the background and start a new poll
688 cycle after the interval has elapsed.
690 6. Options to fetch old messages as well as new, or to suppress
691 deletion of fetched message.
693 The novice-configuration code will assume that you want to forward mail
694 to a local sendmail listener with no special options.
697 configure_expert_help = {
698 'title' : 'Fetchmail expert configurator help',
699 'banner': 'Expert configurator help',
701 In the `Expert Configurator Controls' panel, you can:
703 Press `Save' to save the new fetchmail configuration you have edited.
704 Press `Quit' to exit without saving.
705 Press `Help' to bring up this help message.
707 In the `Run Controls' panel, you can set the following options that
708 control how fetchmail runs:
711 Number of seconds to wait between polls in the background.
712 If zero, fetchmail will run in foreground.
715 If empty, emit progress and error messages to stderr.
716 Otherwise this gives the name of the files to write to.
717 This field is ignored if the "Log to syslog?" option is on.
720 If empty, store seen-message IDs in .fetchids under user's home
721 directory. If nonempty, use given file name.
724 Who to send multidrop mail to as a last resort if no address can
725 be matched. Normally empty; in this case, fetchmail treats the
726 invoking user as the address of last resort unless that user is
727 root. If that user is root, fetchmail sends to `postmaster'.
730 If this option is on (the default) error mail goes to the sender.
731 Otherwise it goes to the postmaster.
734 If this option is on, spam bounces are sent to the sender or
735 postmaster (depending on the "Bounces to sender?" option. Otherwise,
736 spam bounces are not sent (the default).
739 If this option is on, permanent delivery errors are treated as
740 temporary, i. e. mail is kept on the upstream server. Useful
741 during testing and after configuration changes, and on by
743 If this option is off, permanent delivery errors delete
744 undeliverable mail from the upstream.
747 If false (the default) fetchmail generates a Received line into
748 each message and generates a HELO from the machine it is running on.
749 If true, fetchmail generates no Received line and HELOs as if it were
752 In the `Remote Mail Configurations' panel, you can:
754 1. Enter the name of a new remote mail server you want fetchmail to query.
756 To do this, simply enter a label for the poll configuration in the
757 `New Server:' box. The label should be a DNS name of the server (unless
758 you are using ssh or some other tunneling method and will fill in the `via'
759 option on the site configuration screen).
761 2. Change the configuration of an existing site.
763 To do this, find the site's label in the listbox and double-click it.
764 This will take you to a site configuration dialogue.
768 class ConfigurationEdit(Frame, MyWidget):
769 def __init__(self, configuration, outfile, master, onexit):
771 self.configuration = configuration
772 self.outfile = outfile
773 self.container = master
775 ConfigurationEdit.mode_to_help = {
776 'novice':configure_novice_help, 'expert':configure_expert_help
779 def server_edit(self, sitename):
780 self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
782 def server_delete(self, sitename):
784 for user in self.subwidgets.keys():
786 del self.configuration[sitename]
790 def edit(self, mode):
792 Frame.__init__(self, self.container)
793 self.master.title('fetchmail ' + self.mode + ' configurator');
794 self.master.iconname('fetchmail ' + self.mode + ' configurator');
795 self.master.protocol('WM_DELETE_WINDOW', self.nosave)
796 self.keepalive = [] # Use this to anchor the PhotoImage object
797 make_icon_window(self, fetchmail_icon)
799 self.post(Configuration, 'configuration')
802 'Configurator ' + self.mode + ' Controls',
803 ConfigurationEdit.mode_to_help[self.mode],
806 gf = Frame(self, relief=RAISED, bd = 5)
808 text='Fetchmail Run Controls',
809 bd=2).pack(side=TOP, pady=10)
814 if self.mode != 'novice':
816 log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
817 log.pack(side=RIGHT, anchor=E)
819 # Set the poll interval
820 de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
821 de.pack(side=RIGHT, anchor=E)
826 if self.mode != 'novice':
829 {'text':'Bounces to sender?',
830 'variable':self.bouncemail,
831 'relief':GROOVE}).pack(side=LEFT, anchor=W)
836 {'text':'Send spam bounces?',
837 'variable':self.spambounce,
838 'relief':GROOVE}).pack(side=LEFT, anchor=W)
843 {'text':'Treat permanent errors as temporary?',
844 'variable':self.softbounce,
845 'relief':GROOVE}).pack(side=LEFT, anchor=W)
850 {'text':'Log to syslog?',
851 'variable':self.syslog,
852 'relief':GROOVE}).pack(side=LEFT, anchor=W)
853 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
854 log.pack(side=RIGHT, anchor=E)
858 {'text':'Invisible mode?',
859 'variable':self.invisible,
860 'relief':GROOVE}).pack(side=LEFT, anchor=W)
862 log = LabeledEntry(gf, ' Idfile:', self.idfile, '14')
863 log.pack(side=RIGHT, anchor=E)
867 # Expert mode allows us to edit multiple sites
868 lf = Frame(self, relief=RAISED, bd=5)
870 text='Remote Mail Server Configurations',
871 bd=2).pack(side=TOP, pady=10)
872 ListEdit('New Server:',
873 map(lambda x: x.pollname, self.configuration.servers),
874 lambda site, self=self: self.server_edit(site),
875 lambda site, self=self: self.server_delete(site),
880 for sitename in self.subwidgets.keys():
881 self.subwidgets[sitename].destruct()
882 self.master.destroy()
886 if ConfirmQuit(self, self.mode + " configuration editor"):
890 for sitename in self.subwidgets.keys():
891 self.subwidgets[sitename].save()
892 self.fetch(Configuration, 'configuration')
896 elif not os.path.isfile(self.outfile) or Dialog(self,
897 title = 'Overwrite existing run control file?',
898 text = 'Really overwrite existing run control file?',
900 strings = ('Yes', 'No'),
901 default = 1).num == 0:
903 os.rename(self.outfile, self.outfile + "~")
904 # Pre-1.5.2 compatibility...
907 oldumask = os.umask(077)
908 fm = open(self.outfile, 'w')
913 os.chmod(self.outfile, 0600)
914 fm.write("# Configuration created %s by fetchmailconf %s\n" % (time.ctime(time.time()), version))
915 fm.write(`self.configuration`)
921 # Server editing stuff.
924 'title' : 'Remote site help',
925 'banner': 'Remote sites',
927 When you add a site name to the list here,
928 you initialize an entry telling fetchmail
929 how to poll a new site.
931 When you select a sitename (by double-
932 clicking it, or by single-clicking to
933 select and then clicking the Edit button),
934 you will open a window to configure that
939 'title' : 'Server options help',
940 'banner': 'Server Options',
942 The server options screen controls fetchmail
943 options that apply to one of your mailservers.
945 Once you have a mailserver configuration set
946 up as you like it, you can select `OK' to
947 store it in the server list maintained in
948 the main configuration window.
950 If you wish to discard changes to a server
951 configuration, select `Quit'.
955 'title' : 'Run Control help',
956 'banner': 'Run Controls',
958 If the `Poll normally' checkbox is on, the host is polled as part of
959 the normal operation of fetchmail when it is run with no arguments.
960 If it is off, fetchmail will only query this host when it is given as
961 a command-line argument.
963 The `True name of server' box should specify the actual DNS name
964 to query. By default this is the same as the poll name.
966 Normally each host described in the file is queried once each
967 poll cycle. If `Cycles to skip between polls' is greater than 0,
968 that's the number of poll cycles that are skipped between the
969 times this post is actually polled.
971 The `Server timeout' is the number of seconds fetchmail will wait
972 for a reply from the mailserver before concluding it is hung and
977 'title' : 'Protocol and Port help',
978 'banner': 'Protocol and Port',
980 These options control the remote-mail protocol
981 and TCP/IP service port used to query this
984 If you click the `Probe for supported protocols'
985 button, fetchmail will try to find you the most
986 capable server on the selected host (this will
987 only work if you're conncted to the Internet).
988 The probe only checks for ordinary IMAP and POP
989 protocols; fortunately these are the most
990 frequently supported.
992 The `Protocol' button bar offers you a choice of
993 all the different protocols available. The `auto'
994 protocol is the default mode; it probes the host
995 ports for POP3 and IMAP to see if either is
998 Normally the TCP/IP service port to use is
999 dictated by the protocol choice. The `Service'
1000 field (only present in expert mode) lets you
1001 set a non-standard service (port).
1005 'title' : 'Security option help',
1006 'banner': 'Security',
1008 The 'authorization mode' allows you to choose the
1009 mode that fetchmail uses to log in to your server. You
1010 can usually leave this at 'any', but you will have to pick
1011 'NTLM' and 'MSN' manually for the nonce.
1013 The 'interface' option allows you to specify a range
1014 of IP addresses to monitor for activity. If these
1015 addresses are not active, fetchmail will not poll.
1016 Specifying this may protect you from a spoofing attack
1017 if your client machine has more than one IP gateway
1018 address and some of the gateways are to insecure nets.
1020 The `monitor' option, if given, specifies the only
1021 device through which fetchmail is permitted to connect
1022 to servers. This option may be used to prevent
1023 fetchmail from triggering an expensive dial-out if the
1024 interface is not already active.
1026 The `interface' and `monitor' options are available
1027 only for Linux and freeBSD systems. See the fetchmail
1028 manual page for details on these.
1030 The ssl option enables SSL communication with a mailserver
1031 supporting Secure Sockets Layer. The sslkey and sslcert options
1032 declare key and certificate files for use with SSL.
1033 The sslcertck option enables strict checking of SSL server
1034 certificates (and sslcertpath gives the trusted certificate
1035 directory). The sslcommonname option helps if the server is
1036 misconfigured and returning "Server CommonName mismatch"
1037 warnings. With sslfingerprint, you can specify a finger-
1038 print the server's key is checked against.
1042 'title' : 'Multidrop option help',
1043 'banner': 'Multidrop',
1045 These options are only useful with multidrop mode.
1046 See the manual page for extended discussion.
1050 'title' : 'User list help',
1051 'banner': 'User list',
1053 When you add a user name to the list here,
1054 you initialize an entry telling fetchmail
1055 to poll the site on behalf of the new user.
1057 When you select a username (by double-
1058 clicking it, or by single-clicking to
1059 select and then clicking the Edit button),
1060 you will open a window to configure the
1061 user's options on that site.
1064 class ServerEdit(Frame, MyWidget):
1065 def __init__(self, host, parent):
1066 self.parent = parent
1068 self.subwidgets = {}
1069 for site in parent.configuration.servers:
1070 if site.pollname == host:
1072 if (self.server == None):
1073 self.server = Server()
1074 self.server.pollname = host
1075 self.server.via = None
1076 parent.configuration.servers.append(self.server)
1078 def edit(self, mode, master=None):
1079 Frame.__init__(self, master)
1081 self.master.title('Fetchmail host ' + self.server.pollname);
1082 self.master.iconname('Fetchmail host ' + self.server.pollname);
1083 self.post(Server, 'server')
1084 self.makeWidgets(self.server.pollname, mode)
1085 self.keepalive = [] # Use this to anchor the PhotoImage object
1086 make_icon_window(self, fetchmail_icon)
1089 # self.wait_window()
1093 for username in self.subwidgets.keys():
1094 self.subwidgets[username].destruct()
1095 del self.parent.subwidgets[self.server.pollname]
1096 self.master.destroy()
1099 if ConfirmQuit(self, 'server option editing'):
1103 self.fetch(Server, 'server')
1104 for username in self.subwidgets.keys():
1105 self.subwidgets[username].save()
1108 def defaultPort(self):
1109 proto = self.protocol.get()
1110 # Callback to reset the port number whenever the protocol type changes.
1111 # We used to only reset the port if it had a default (zero) value.
1112 # This turns out to be a bad idea especially in Novice mode -- if
1113 # you set POP3 and then set IMAP, the port invisibly remained 110.
1114 # Now we reset unconditionally on the theory that if you're setting
1115 # a custom port number you should be in expert mode and playing
1116 # close enough attention to notice this...
1117 self.service.set(defaultports[proto])
1118 if not proto in ("POP3", "APOP", "KPOP"): self.uidl.state = DISABLED
1120 def user_edit(self, username, mode):
1121 self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1123 def user_delete(self, username):
1124 if self.subwidgets.has_key(username):
1125 self.subwidgets[username].destruct()
1126 del self.server[username]
1128 def makeWidgets(self, host, mode):
1129 topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1131 leftwin = Frame(self);
1134 if mode != 'novice':
1135 ctlwin = Frame(leftwin, relief=RAISED, bd=5)
1136 Label(ctlwin, text="Run Controls").pack(side=TOP)
1137 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
1138 Checkbutton(ctlwin, text='Pass messages with bad headers?',
1139 variable=self.badheader).pack(side=TOP)
1140 LabeledEntry(ctlwin, 'True name of ' + host + ':',
1141 self.via, leftwidth).pack(side=TOP, fill=X)
1142 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
1143 self.interval, leftwidth).pack(side=TOP, fill=X)
1144 LabeledEntry(ctlwin, 'Server timeout (seconds):',
1145 self.timeout, leftwidth).pack(side=TOP, fill=X)
1146 Button(ctlwin, text='Help', fg='blue',
1147 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
1150 # Compute the available protocols from the compile-time options
1151 protolist = ['auto']
1152 if 'pop2' in feature_options:
1153 protolist.append("POP2")
1154 if 'pop3' in feature_options:
1155 protolist = protolist + ["POP3", "APOP", "KPOP"]
1156 if 'sdps' in feature_options:
1157 protolist.append("SDPS")
1158 if 'imap' in feature_options:
1159 protolist.append("IMAP")
1160 if 'etrn' in feature_options:
1161 protolist.append("ETRN")
1162 if 'odmr' in feature_options:
1163 protolist.append("ODMR")
1165 protwin = Frame(leftwin, relief=RAISED, bd=5)
1166 Label(protwin, text="Protocol").pack(side=TOP)
1167 ButtonBar(protwin, '',
1168 self.protocol, protolist, 2,
1170 if mode != 'novice':
1171 LabeledEntry(protwin, 'On server TCP/IP service:',
1172 self.service, leftwidth).pack(side=TOP, fill=X)
1174 Checkbutton(protwin,
1175 text="POP3: track `seen' with client-side UIDLs?",
1176 variable=self.uidl).pack(side=TOP)
1177 Button(protwin, text='Probe for supported protocols', fg='blue',
1178 command=self.autoprobe).pack(side=LEFT)
1179 Button(protwin, text='Help', fg='blue',
1180 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
1181 protwin.pack(fill=X)
1183 userwin = Frame(leftwin, relief=RAISED, bd=5)
1184 Label(userwin, text="User entries for " + host).pack(side=TOP)
1185 ListEdit("New user: ",
1186 map(lambda x: x.remote, self.server.users),
1187 lambda u, m=mode, s=self: s.user_edit(u, m),
1188 lambda u, s=self: s.user_delete(u),
1190 userwin.pack(fill=X)
1192 leftwin.pack(side=LEFT, anchor=N, fill=X);
1194 if mode != 'novice':
1195 rightwin = Frame(self);
1197 mdropwin = Frame(rightwin, relief=RAISED, bd=5)
1198 Label(mdropwin, text="Multidrop options").pack(side=TOP)
1199 LabeledEntry(mdropwin, 'Envelope address header:',
1200 self.envelope, '22').pack(side=TOP, fill=X)
1201 LabeledEntry(mdropwin, 'Envelope headers to skip:',
1202 self.envskip, '22').pack(side=TOP, fill=X)
1203 LabeledEntry(mdropwin, 'Name prefix to strip:',
1204 self.qvirtual, '22').pack(side=TOP, fill=X)
1205 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
1206 variable=self.dns).pack(side=TOP)
1207 Label(mdropwin, text="DNS aliases").pack(side=TOP)
1208 ListEdit("New alias: ", self.server.aka, None, None, mdropwin, None)
1209 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
1210 ListEdit("New domain: ",
1211 self.server.localdomains, None, None, mdropwin, multihelp)
1212 mdropwin.pack(fill=X)
1214 if os_type in ('linux', 'freebsd'):
1215 secwin = Frame(rightwin, relief=RAISED, bd=5)
1216 Label(secwin, text="Security").pack(side=TOP)
1217 # Don't actually let users set this. KPOP sets it implicitly
1218 ButtonBar(secwin, 'Authorization mode:',
1219 self.auth, authlist, 2, None).pack(side=TOP)
1220 if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
1221 LabeledEntry(secwin, 'IP range to check before poll:',
1222 self.interface, leftwidth).pack(side=TOP, fill=X)
1223 if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
1224 LabeledEntry(secwin, 'Interface to monitor:',
1225 self.monitor, leftwidth).pack(side=TOP, fill=X)
1226 # Someday this should handle Kerberos 5 too
1227 if 'kerberos' in feature_options:
1228 LabeledEntry(secwin, 'Principal:',
1229 self.principal, '12').pack(side=TOP, fill=X)
1230 # ESMTP authentication
1231 LabeledEntry(secwin, 'ESMTP name:',
1232 self.esmtpname, '12').pack(side=TOP, fill=X)
1233 LabeledEntry(secwin, 'ESMTP password:',
1234 self.esmtppassword, '12').pack(side=TOP, fill=X)
1235 Button(secwin, text='Help', fg='blue',
1236 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
1239 rightwin.pack(side=LEFT, anchor=N);
1241 def autoprobe(self):
1242 # Note: this only handles case (1) near fetchmail.c:1032
1243 # We're assuming people smart enough to set up ssh tunneling
1244 # won't need autoprobing.
1246 realhost = self.server.via
1248 realhost = self.server.pollname
1250 for protocol in ("IMAP","POP3","POP2"):
1251 service = defaultports[protocol]
1252 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1254 sock.connect((realhost, ianaservices[service]))
1255 greetline = sock.recv(1024)
1261 confwin = Toplevel()
1262 if greetline == None:
1263 title = "Autoprobe of " + realhost + " failed"
1265 Fetchmailconf didn't find any mailservers active.
1266 This could mean the host doesn't support any,
1267 or that your Internet connection is down, or
1268 that the host is so slow that the probe timed
1269 out before getting a response.
1273 # OK, now try to recognize potential problems
1275 if protocol == "POP2":
1276 warnings = warnings + """
1277 It appears you have somehow found a mailserver running only POP2.
1278 Congratulations. Have you considered a career in archaeology?
1280 Unfortunately, stock fetchmail binaries don't include POP2 support anymore.
1281 Unless the first line of your fetchmail -V output includes the string "POP2",
1282 you'll have to build it from sources yourself with the configure
1283 switch --enable-POP2.
1287 ### POP3 servers start here
1289 if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
1290 warnings = warnings + """
1291 This appears to be an old version of the UC Davis POP server. These are
1292 dangerously unreliable (among other problems, they may drop your mailbox
1293 on the floor if your connection is interrupted during the session).
1295 It is strongly recommended that you find a better POP3 server. The fetchmail
1296 FAQ includes pointers to good ones.
1299 if string.find(greetline, "comcast.net") > 0:
1300 warnings = warnings + """
1301 The Comcast Maillennium POP3 server only returns the first 80K of a long
1302 message retrieved with TOP. Its response to RETR is normal, so use the
1306 # Steve VanDevender <stevev@efn.org> writes:
1307 # The only system I have seen this happen with is cucipop-1.31
1308 # under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
1309 # 2.x and probably quite a few other systems. It appears to be a
1310 # bug or bad interaction with the SunOS realloc() -- it turns out
1311 # that internally cucipop does allocate a certain data structure in
1312 # multiples of 16, using realloc() to bump it up to the next
1313 # multiple if it needs more.
1315 # The distinctive symptom is that when there are 16 messages in the
1316 # inbox, you can RETR and DELE all 16 messages successfully, but on
1317 # QUIT cucipop returns something like "-ERR Error locking your
1318 # mailbox" and aborts without updating it.
1320 # The cucipop banner looks like:
1322 # +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1324 if string.find(greetline, "Cubic Circle") > 0:
1325 warnings = warnings + """
1326 I see your server is running cucipop. Better make sure the server box
1327 isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
1328 under that version, and doesn't cope with the result gracefully. Newer
1329 SunOS and Solaris machines run cucipop OK.
1331 Also, some versions of cucipop don't assert an exclusive lock on your
1332 mailbox when it's being queried. This means that if you have more than
1333 one fetchmail query running against the same mailbox, bad things can happen.
1335 if string.find(greetline, "David POP3 Server") > 0:
1336 warnings = warnings + """
1337 This POP3 server is badly broken. You should get rid of it -- and the
1338 brain-dead Microsoft operating system it rode in on.
1341 # The greeting line on the server known to be buggy is:
1342 # +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
1344 if string.find(greetline, "FTGate") > 0:
1345 warnings = warnings + """
1346 This POP server has a weird bug; it says OK twice in response to TOP.
1347 Its response to RETR is normal, so use the `fetchall' option.
1350 if string.find(greetline, " geonet.de") > 0:
1351 warnings = warnings + """
1352 You appear to be using geonet. As of late 2002, the TOP command on
1353 geonet's POP3 is broken. Use the fetchall option.
1356 if string.find(greetline, "OpenMail") > 0:
1357 warnings = warnings + """
1358 You appear to be using some version of HP OpenMail. Many versions of
1359 OpenMail do not process the "TOP" command correctly; the symptom is that
1360 only the header and first line of each message is retrieved. To work
1361 around this bug, turn on `fetchall' on all user entries associated with
1365 if string.find(greetline, "Escape character is") > 0:
1366 warnings = warnings + """
1367 Your greeting line looks like it was written by a fetid pile of
1368 camel dung identified to me as `popa3d written by Solar Designer'.
1369 Beware! The UIDL support in this thing is known to be completely broken,
1370 and other things probably are too.
1373 if string.find(greetline, "MercuryP/NLM v1.48") > 0:
1374 warnings = warnings + """
1375 This is not a POP3 server. It has delusions of being one, but after
1376 RETR all messages are automatically marked to be deleted. The only
1377 way to prevent this is to issue an RSET before leaving the server.
1378 Fetchmail does this, but we suspect this is probably broken in lots
1382 if string.find(greetline, "POP-Max") > 0:
1383 warnings = warnings + """
1384 The Mail Max POP3 server screws up on mail with attachments. It
1385 reports the message size with attachments included, but doesn't
1386 download them on a RETR or TOP (this violates the IMAP RFCs). It also
1387 doesn't implement TOP correctly. You should get rid of it -- and the
1388 brain-dead NT server it rode in on.
1391 if string.find(greetline, "POP3 Server Ready") > 0:
1392 warnings = warnings + """
1393 Some server that uses this greeting line has been observed to choke on
1394 TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
1397 if string.find(greetline, "QPOP") > 0:
1398 warnings = warnings + """
1399 This appears to be a version of Eudora qpopper. That's good. Fetchmail
1400 knows all about qpopper. However, be aware that the 2.53 version of
1401 qpopper does something odd that causes fetchmail to hang with a socket
1402 error on very large messages. This is probably not a fetchmail bug, as
1403 it has been observed with fetchpop. The fix is to upgrade to qpopper
1404 3.0beta or a more recent version. Better yet, switch to IMAP.
1407 if string.find(greetline, " sprynet.com") > 0:
1408 warnings = warnings + """
1409 You appear to be using a SpryNet server. In mid-1999 it was reported that
1410 the SpryNet TOP command marks messages seen. Therefore, for proper error
1411 recovery in the event of a line drop, it is strongly recommended that you
1412 turn on `fetchall' on all user entries associated with this server.
1415 if string.find(greetline, "TEMS POP3") > 0:
1416 warnings = warnings + """
1417 Your POP3 server has "TEMS" in its header line. At least one such
1418 server does not process the "TOP" command correctly; the symptom is
1419 that fetchmail hangs when trying to retrieve mail. To work around
1420 this bug, turn on `fetchall' on all user entries associated with this
1424 if string.find(greetline, " spray.se") > 0:
1425 warnings = warnings + """
1426 Your POP3 server has "spray.se" in its header line. In May 2000 at
1427 least one such server did not process the "TOP" command correctly; the
1428 symptom is that messages are treated as headerless. To work around
1429 this bug, turn on `fetchall' on all user entries associated with this
1433 if string.find(greetline, " usa.net") > 0:
1434 warnings = warnings + """
1435 You appear to be using USA.NET's free mail service. Their POP3 servers
1436 (at least as of the 2.2 version in use mid-1998) are quite flaky, but
1437 fetchmail can compensate. They seem to require that fetchall be switched on
1438 (otherwise you won't necessarily see all your mail, not even new mail).
1439 They also botch the TOP command the fetchmail normally uses for retrieval
1440 (it only retrieves about 10 lines rather than the number specified).
1441 Turning on fetchall will disable the use of TOP.
1443 Therefore, it is strongly recommended that you turn on `fetchall' on all
1444 user entries associated with this server.
1447 if string.find(greetline, " Novonyx POP3") > 0:
1448 warnings = warnings + """
1449 Your mailserver is running Novonyx POP3. This server, at least as of
1450 version 2.17, seems to have problems handling and reporting seen bits.
1451 You may have to use the fetchall option.
1454 if string.find(greetline, " IMS POP3") > 0:
1455 warnings = warnings + """
1456 Some servers issuing the greeting line 'IMS POP3' have been known to
1457 do byte-stuffing incorrectly. This means that if a message you receive
1458 has a . (period) at start of line, fetchmail will become confused and
1459 probably wedge itself. (This bug was recorded on IMS POP3 0.86.)
1463 ### IMAP servers start here
1465 if string.find(greetline, "GroupWise") > 0:
1466 warnings = warnings + """
1467 The Novell GroupWise IMAP server would be better named GroupFoolish;
1468 it is (according to the designer of IMAP) unusably broken. Among
1469 other things, it doesn't include a required content length in its
1470 BODY[TEXT] response.<p>
1472 Fetchmail works around this problem, but we strongly recommend voting
1473 with your dollars for a server that isn't brain-dead. If you stick
1474 with code as shoddy as GroupWise seems to be, you will probably pay
1475 for it with other problems.<p>
1478 if string.find(greetline, "InterChange") > 0:
1479 warnings = warnings + """
1481 The InterChange IMAP server at release levels below 3.61.08 screws up
1482 on mail with attachments. It doesn't fetch them if you give it a
1483 BODY[TEXT] request, though it does if you request RFC822.TEXT.
1484 According to the IMAP RFCs and their maintainer these should be
1485 equivalent -- and we can't drop the BODY[TEXT] form because M$
1486 Exchange (quite legally under RFC2062) rejectsit. The InterChange
1487 folks claim to have fixed this bug in 3.61.08.
1490 if string.find(greetline, "Imail") > 0:
1491 warnings = warnings + """
1492 We've seen a bug report indicating that this IMAP server (at least as of
1493 version 5.0.7) returns an invalid body size for messages with MIME
1494 attachments; the effect is to drop the attachments on the floor. We
1495 recommend you upgrade to a non-broken IMAP server.
1498 if string.find(greetline, "Domino IMAP4") > 0:
1499 warnings = warnings + """
1500 Your IMAP server appears to be Lotus Domino. This server, at least up
1501 to version 4.6.2a, has a bug in its generation of MIME boundaries (see
1502 the details in the fetchmail FAQ). As a result, even MIME aware MUAs
1503 will see attachments as part of the message text. If your Domino server's
1504 POP3 facility is enabled, we recommend you fall back on it.
1508 ### Checks for protocol variants start here
1510 closebrak = string.find(greetline, ">")
1511 if closebrak > 0 and greetline[closebrak+1] == "\r":
1512 warnings = warnings + """
1513 It looks like you could use APOP on this server and avoid sending it your
1514 password in clear. You should talk to the mailserver administrator about
1518 if string.find(greetline, "IMAP2bis") > 0:
1519 warnings = warnings + """
1520 IMAP2bis servers have a minor problem; they can't peek at messages without
1521 marking them seen. If you take a line hit during the retrieval, the
1522 interrupted message may get left on the server, marked seen.
1524 To work around this, it is recommended that you set the `fetchall'
1525 option on all user entries associated with this server, so any stuck
1526 mail will be retrieved next time around.
1528 To fix this bug, upgrade to an IMAP4 server. The fetchmail FAQ includes
1529 a pointer to an open-source implementation.
1532 if string.find(greetline, "IMAP4rev1") > 0:
1533 warnings = warnings + """
1534 I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
1535 remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
1536 has therefore been extremely well tested with this class of server.
1540 warnings = warnings + """
1541 Fetchmail doesn't know anything special about this server type.
1545 # Display success window with warnings
1546 title = "Autoprobe of " + realhost + " succeeded"
1547 confirm = "The " + protocol + " server said:\n\n" + greetline + warnings
1548 self.protocol.set(protocol)
1549 self.service.set(defaultports[protocol])
1550 confwin.title(title)
1551 confwin.iconname(title)
1552 Label(confwin, text=title).pack()
1553 Message(confwin, text=confirm, width=600).pack()
1554 Button(confwin, text='Done',
1555 command=lambda x=confwin: x.destroy(), bd=2).pack()
1558 # User editing stuff
1562 'title' : 'User option help',
1563 'banner': 'User options',
1565 You may use this panel to set options
1566 that may differ between individual
1569 Once you have a user configuration set
1570 up as you like it, you can select `OK' to
1571 store it in the user list maintained in
1572 the site configuration window.
1574 If you wish to discard the changes you have
1575 made to user options, select `Quit'.
1579 'title' : 'Local name help',
1580 'banner': 'Local names',
1582 The local name(s) in a user entry are the
1583 people on the client machine who should
1584 receive mail from the poll described.
1586 Note: if a user entry has more than one
1587 local name, messages will be retrieved
1588 in multidrop mode. This complicates
1589 the configuration issues; see the manual
1590 page section on multidrop mode.
1592 Warning: Be careful with local names
1593 such as foo@bar.com, as that can cause
1594 the mail to be sent to foo@bar.com instead
1595 of sending it to your local system.
1598 class UserEdit(Frame, MyWidget):
1599 def __init__(self, username, parent):
1600 self.parent = parent
1602 for user in parent.server.users:
1603 if user.remote == username:
1605 if self.user == None:
1607 self.user.remote = username
1608 self.user.localnames = [username]
1609 parent.server.users.append(self.user)
1611 def edit(self, mode, master=None):
1612 Frame.__init__(self, master)
1614 self.master.title('Fetchmail user ' + self.user.remote
1615 + ' querying ' + self.parent.server.pollname);
1616 self.master.iconname('Fetchmail user ' + self.user.remote);
1617 self.post(User, 'user')
1618 self.makeWidgets(mode, self.parent.server.pollname)
1619 self.keepalive = [] # Use this to anchor the PhotoImage object
1620 make_icon_window(self, fetchmail_icon)
1623 # self.wait_window()
1627 # Yes, this test can fail -- if you delete the parent window.
1628 if self.parent.subwidgets.has_key(self.user.remote):
1629 del self.parent.subwidgets[self.user.remote]
1630 self.master.destroy()
1633 if ConfirmQuit(self, 'user option editing'):
1638 for x in self.user.localnames: ok = ok + (string.find(x, '@') != -1)
1639 if ok == 0 or Dialog(self,
1640 title = "Really accept an embedded '@' ?",
1641 text = "Local names with an embedded '@', such as in foo@bar "
1642 "might result in your mail being sent to foo@bar.com "
1643 "instead of your local system.\n Are you sure you want "
1644 "a local user name with an '@' in it?",
1645 bitmap = 'question',
1646 strings = ('Yes', 'No'),
1647 default = 1).num == 0:
1648 self.fetch(User, 'user')
1651 def makeWidgets(self, mode, servername):
1652 dispose_window(self,
1653 "User options for " + self.user.remote + " querying " + servername,
1656 if mode != 'novice':
1657 leftwin = Frame(self);
1661 secwin = Frame(leftwin, relief=RAISED, bd=5)
1662 Label(secwin, text="Authentication").pack(side=TOP)
1663 LabeledEntry(secwin, 'Password:',
1664 self.password, '12').pack(side=TOP, fill=X)
1665 secwin.pack(fill=X, anchor=N)
1667 if 'ssl' in feature_options or 'ssl' in dictmembers:
1668 sslwin = Frame(leftwin, relief=RAISED, bd=5)
1669 Checkbutton(sslwin, text="Use SSL?",
1670 variable=self.ssl).pack(side=TOP, fill=X)
1671 LabeledEntry(sslwin, 'SSL key:',
1672 self.sslkey, '14').pack(side=TOP, fill=X)
1673 LabeledEntry(sslwin, 'SSL certificate:',
1674 self.sslcert, '14').pack(side=TOP, fill=X)
1675 Checkbutton(sslwin, text="Check server SSL certificate?",
1676 variable=self.sslcertck).pack(side=TOP, fill=X)
1677 LabeledEntry(sslwin, 'SSL trusted certificate directory:',
1678 self.sslcertpath, '14').pack(side=TOP, fill=X)
1679 LabeledEntry(sslwin, 'SSL CommonName:',
1680 self.sslcommonname, '14').pack(side=TOP, fill=X)
1681 LabeledEntry(sslwin, 'SSL key fingerprint:',
1682 self.sslfingerprint, '14').pack(side=TOP, fill=X)
1683 sslwin.pack(fill=X, anchor=N)
1685 names = Frame(leftwin, relief=RAISED, bd=5)
1686 Label(names, text="Local names").pack(side=TOP)
1687 ListEdit("New name: ",
1688 self.user.localnames, None, None, names, localhelp)
1689 names.pack(fill=X, anchor=N)
1691 if mode != 'novice':
1692 targwin = Frame(leftwin, relief=RAISED, bd=5)
1693 Label(targwin, text="Forwarding Options").pack(side=TOP)
1694 Label(targwin, text="Listeners to forward to").pack(side=TOP)
1695 ListEdit("New listener:",
1696 self.user.smtphunt, None, None, targwin, None)
1697 Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
1698 ListEdit("Domains:",
1699 self.user.fetchdomains, None, None, targwin, None)
1700 LabeledEntry(targwin, 'Use domain on RCPT TO line:',
1701 self.smtpaddress, '26').pack(side=TOP, fill=X)
1702 LabeledEntry(targwin, 'Set fixed RCPT TO address:',
1703 self.smtpname, '26').pack(side=TOP, fill=X)
1704 LabeledEntry(targwin, 'Connection setup command:',
1705 self.preconnect, '26').pack(side=TOP, fill=X)
1706 LabeledEntry(targwin, 'Connection wrapup command:',
1707 self.postconnect, '26').pack(side=TOP, fill=X)
1708 LabeledEntry(targwin, 'Local delivery agent:',
1709 self.mda, '26').pack(side=TOP, fill=X)
1710 LabeledEntry(targwin, 'BSMTP output file:',
1711 self.bsmtp, '26').pack(side=TOP, fill=X)
1712 LabeledEntry(targwin, 'Listener spam-block codes:',
1713 self.antispam, '26').pack(side=TOP, fill=X)
1714 LabeledEntry(targwin, 'Pass-through properties:',
1715 self.properties, '26').pack(side=TOP, fill=X)
1716 Checkbutton(targwin, text="Use LMTP?",
1717 variable=self.lmtp).pack(side=TOP, fill=X)
1718 targwin.pack(fill=X, anchor=N)
1720 if mode != 'novice':
1721 leftwin.pack(side=LEFT, fill=X, anchor=N)
1722 rightwin = Frame(self)
1726 optwin = Frame(rightwin, relief=RAISED, bd=5)
1727 Label(optwin, text="Processing Options").pack(side=TOP)
1728 Checkbutton(optwin, text="Suppress deletion of messages after reading",
1729 variable=self.keep).pack(side=TOP, anchor=W)
1730 Checkbutton(optwin, text="Fetch old messages as well as new",
1731 variable=self.fetchall).pack(side=TOP, anchor=W)
1732 if mode != 'novice':
1733 Checkbutton(optwin, text="Flush seen messages before retrieval",
1734 variable=self.flush).pack(side=TOP, anchor=W)
1735 Checkbutton(optwin, text="Flush oversized messages before retrieval",
1736 variable=self.limitflush).pack(side=TOP, anchor=W)
1737 Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply",
1738 variable=self.rewrite).pack(side=TOP, anchor=W)
1739 Checkbutton(optwin, text="Force CR/LF at end of each line",
1740 variable=self.forcecr).pack(side=TOP, anchor=W)
1741 Checkbutton(optwin, text="Strip CR from end of each line",
1742 variable=self.stripcr).pack(side=TOP, anchor=W)
1743 Checkbutton(optwin, text="Pass 8 bits even though SMTP says 7BIT",
1744 variable=self.pass8bits).pack(side=TOP, anchor=W)
1745 Checkbutton(optwin, text="Undo MIME armoring on header and body",
1746 variable=self.mimedecode).pack(side=TOP, anchor=W)
1747 Checkbutton(optwin, text="Drop Status lines from forwarded messages",
1748 variable=self.dropstatus).pack(side=TOP, anchor=W)
1749 Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
1750 variable=self.dropdelivered).pack(side=TOP, anchor=W)
1753 if mode != 'novice':
1754 limwin = Frame(rightwin, relief=RAISED, bd=5)
1755 Label(limwin, text="Resource Limits").pack(side=TOP)
1756 LabeledEntry(limwin, 'Message size limit:',
1757 self.limit, '30').pack(side=TOP, fill=X)
1758 LabeledEntry(limwin, 'Size warning interval:',
1759 self.warnings, '30').pack(side=TOP, fill=X)
1760 LabeledEntry(limwin, 'Max messages to fetch per poll:',
1761 self.fetchlimit, '30').pack(side=TOP, fill=X)
1762 LabeledEntry(limwin, 'Max message sizes to fetch per transaction:',
1763 self.fetchsizelimit, '30').pack(side=TOP, fill=X)
1764 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1765 LabeledEntry(limwin, 'Use fast UIDL:',
1766 self.fastuidl, '30').pack(side=TOP, fill=X)
1767 LabeledEntry(limwin, 'Max messages to forward per poll:',
1768 self.batchlimit, '30').pack(side=TOP, fill=X)
1769 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1770 LabeledEntry(limwin, 'Interval between expunges:',
1771 self.expunge, '30').pack(side=TOP, fill=X)
1772 Checkbutton(limwin, text="Idle after each poll (IMAP only)",
1773 variable=self.idle).pack(side=TOP, anchor=W)
1776 if self.parent.server.protocol == 'IMAP':
1777 foldwin = Frame(rightwin, relief=RAISED, bd=5)
1778 Label(foldwin, text="Remote folders (IMAP only)").pack(side=TOP)
1779 ListEdit("New folder:", self.user.mailboxes,
1780 None, None, foldwin, None)
1781 foldwin.pack(fill=X, anchor=N)
1783 if mode != 'novice':
1784 rightwin.pack(side=LEFT)
1790 # Top-level window that offers either novice or expert mode
1791 # (but not both at once; it disappears when one is selected).
1794 class Configurator(Frame):
1795 def __init__(self, outfile, master, onexit, parent):
1796 Frame.__init__(self, master)
1797 self.outfile = outfile
1798 self.onexit = onexit
1799 self.parent = parent
1800 self.master.title('fetchmail configurator');
1801 self.master.iconname('fetchmail configurator');
1803 self.keepalive = [] # Use this to anchor the PhotoImage object
1804 make_icon_window(self, fetchmail_icon)
1806 Message(self, text="""
1807 Use `Novice Configuration' for basic fetchmail setup;
1808 with this, you can easily set up a single-drop connection
1809 to one remote mail server.
1810 """, width=600).pack(side=TOP)
1811 Button(self, text='Novice Configuration',
1812 fg='blue', command=self.novice).pack()
1814 Message(self, text="""
1815 Use `Expert Configuration' for advanced fetchmail setup,
1816 including multiple-site or multidrop connections.
1817 """, width=600).pack(side=TOP)
1818 Button(self, text='Expert Configuration',
1819 fg='blue', command=self.expert).pack()
1821 Message(self, text="""
1822 Or you can just select `Quit' to leave the configurator now and
1823 return to the main panel.
1824 """, width=600).pack(side=TOP)
1825 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1826 master.protocol("WM_DELETE_WINDOW", self.leave)
1829 self.master.destroy()
1830 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1833 self.master.destroy()
1834 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1837 self.master.destroy()
1840 # Run a command in a scrolling text widget, displaying its output
1842 class RunWindow(Frame):
1843 def __init__(self, command, master, parent):
1844 Frame.__init__(self, master)
1845 self.master = master
1846 self.master.title('fetchmail run window');
1847 self.master.iconname('fetchmail run window');
1850 text="Running "+command,
1851 bd=2).pack(side=TOP, pady=10)
1852 self.keepalive = [] # Use this to anchor the PhotoImage object
1853 make_icon_window(self, fetchmail_icon)
1855 # This is a scrolling text window
1856 textframe = Frame(self)
1857 scroll = Scrollbar(textframe)
1858 self.textwidget = Text(textframe, setgrid=TRUE)
1859 textframe.pack(side=TOP, expand=YES, fill=BOTH)
1860 self.textwidget.config(yscrollcommand=scroll.set)
1861 self.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
1862 scroll.config(command=self.textwidget.yview)
1863 scroll.pack(side=RIGHT, fill=BOTH)
1864 textframe.pack(side=TOP)
1866 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1868 self.update() # Draw widget before executing fetchmail
1870 # Always look for a runnable command in the directory we're running in
1871 # first. This avoids some obscure version-skew errors that can occur
1872 # if you pick up an old fetchmail from the standard system locations.
1873 os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"]
1874 child_stdout = os.popen(command + " 2>&1 </dev/null", "r")
1876 ch = child_stdout.read(1)
1879 self.textwidget.insert(END, ch)
1880 self.textwidget.insert(END, "Done.")
1881 self.textwidget.see(END);
1884 self.master.destroy()
1886 # Here's where we choose either configuration or launching
1888 class MainWindow(Frame):
1889 def __init__(self, outfile, master=None):
1890 Frame.__init__(self, master)
1891 self.outfile = outfile
1892 self.master.title('fetchmail launcher');
1893 self.master.iconname('fetchmail launcher');
1896 text='Fetchmailconf ' + version,
1897 bd=2).pack(side=TOP, pady=10)
1898 self.keepalive = [] # Use this to anchor the PhotoImage object
1899 make_icon_window(self, fetchmail_icon)
1902 ## Test icon display with the following:
1903 # icon_image = PhotoImage(data=fetchmail_icon)
1904 # Label(self, image=icon_image).pack(side=TOP, pady=10)
1905 # self.keepalive.append(icon_image)
1907 Message(self, text="""
1908 Use `Configure fetchmail' to tell fetchmail about the remote
1909 servers it should poll (the host name, your username there,
1910 whether to use POP or IMAP, and so forth).
1911 """, width=600).pack(side=TOP)
1912 self.configbutton = Button(self, text='Configure fetchmail',
1913 fg='blue', command=self.configure)
1914 self.configbutton.pack()
1916 Message(self, text="""
1917 Use `Run fetchmail' to run fetchmail with debugging enabled.
1918 This is a good way to test out a new configuration.
1919 """, width=600).pack(side=TOP)
1920 Button(self, text='Run fetchmail',fg='blue', command=self.test).pack()
1922 Message(self, text="""
1923 Use `Run fetchmail' to run fetchmail in foreground.
1924 Progress messages will be shown, but not debug messages.
1925 """, width=600).pack(side=TOP)
1926 Button(self, text='Run fetchmail', fg='blue', command=self.run).pack()
1928 Message(self, text="""
1929 Or you can just select `Quit' to exit the launcher now.
1930 """, width=600).pack(side=TOP)
1931 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1933 def configure(self):
1934 self.configbutton.configure(state=DISABLED)
1935 Configurator(self.outfile, Toplevel(),
1936 lambda self=self: self.configbutton.configure(state=NORMAL),
1939 cmd = "fetchmail -N -d0 --nosyslog -v"
1941 cmd = cmd + " -f " + rcfile
1942 RunWindow(cmd, Toplevel(), self)
1945 cmd = "fetchmail -N -d0"
1947 cmd = cmd + " -f " + rcfile
1948 RunWindow(cmd, Toplevel(), self)
1953 # Functions for turning a dictionary into an instantiated object tree.
1955 def intersect(list1, list2):
1956 # Compute set intersection of lists
1963 def setdiff(list1, list2):
1964 # Compute set difference of lists
1971 def copy_instance(toclass, fromdict):
1972 # Initialize a class object of given type from a conformant dictionary.
1973 for fld in fromdict.keys():
1974 if not fld in dictmembers:
1975 dictmembers.append(fld)
1976 # The `optional' fields are the ones we can ignore for purposes of
1977 # conformability checking; they'll still get copied if they are
1978 # present in the dictionary.
1979 optional = ('interface', 'monitor',
1980 'esmtpname', 'esmtppassword',
1981 'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
1982 'sslcertpath', 'sslcommonname', 'sslfingerprint', 'showdots')
1983 class_sig = setdiff(toclass.__dict__.keys(), optional)
1985 dict_keys = setdiff(fromdict.keys(), optional)
1987 common = intersect(class_sig, dict_keys)
1988 if 'typemap' in class_sig:
1989 class_sig.remove('typemap')
1990 if tuple(class_sig) != tuple(dict_keys):
1991 print "Fields don't match what fetchmailconf expected:"
1992 # print "Class signature: " + `class_sig`
1993 # print "Dictionary keys: " + `dict_keys`
1994 diff = setdiff(class_sig, common)
1996 print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
1997 diff = setdiff(dict_keys, common)
1999 print "Not matched in dictionary keys: " + `diff`
2002 for x in fromdict.keys():
2003 setattr(toclass, x, fromdict[x])
2006 # And this is the main sequence. How it works:
2008 # First, call `fetchmail --configdump' and trap the output in a tempfile.
2009 # This should fill it with a Python initializer for a variable `fetchmailrc'.
2010 # Run execfile on the file to pull fetchmailrc into Python global space.
2011 # You don't want static data, though; you want, instead, a tree of objects
2012 # with the same data members and added appropriate methods.
2014 # This is what the copy_instance function() is for. It tries to copy a
2015 # dictionary field by field into a class, aborting if the class and dictionary
2016 # have different data members (except for any typemap member in the class;
2017 # that one is strictly for use by the MyWidget supperclass).
2019 # Once the object tree is set up, require user to choose novice or expert
2020 # mode and instantiate an edit object for the configuration. Class methods
2021 # will take it all from there.
2023 # Options (not documented because they're for fetchmailconf debuggers only):
2024 # -d: Read the configuration and dump it to stdout before editing. Dump
2025 # the edited result to stdout as well.
2026 # -f: specify the run control file to read.
2028 if __name__ == '__main__':
2030 if not os.environ.has_key("DISPLAY"):
2031 print "fetchmailconf must be run under X"
2034 fetchmail_icon = """
2035 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
2036 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
2037 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
2038 CJR7Y/+UMdbOxnNrY97Ove/Wvd7GrZyEa961jL2Ua9alc86ca7WEUntSKcaMSqVjGNZ7GGM5CNa1
2039 jPfOnN6tc3taMffeve/WtWtaQv/OjGtSMYRzWv/erda1hM6te7WUY62MWs61jP/vzv/ntda9jL2l
2040 czEhAO/n1oyEc//elDEpGEo5EOfexpyUe+/epefevffvxnNrQpyUStbWzsbGvZyclN7ezmNjWv//
2041 5/f33qWllNbWve/vzv//1ufnve/vvf//xvf3vefnrf//taWlc0pKMf//pbW1Y///jKWlWq2tWsbG
2042 Y///c97eUvf3Ut7nc+/3a87We8bOjOfv1u/37/f//621tb3Gxtbn52Nra87n53uUlJTv/6W9xuf3
2043 /8bW3iExOXu11tbv/5TW/4TO/63e/zmt/1KUxlK1/2u9/wCM/73GzrXG1gBKjACE/87e72NzhCkx
2044 OaXO92OMtUql/xCE/wApUtbe57W9xnN7hHut52Ot/xBSnABKnABavQB7/2ul7zF71gBr77XO73Oc
2045 1lqc9yFSlBApSimE/wAYOQApY0J7zlKM5wAxhABS1gBj/6W95wAhWgA5nAAYSgBS7wBS/wBK9wAp
2046 jABC5wBK/wApnABC/wApxgAhtYSMtQAQYwAp/3OE74SMxgAYxlpjvWNr70pS/wgQ3sbGzs7O1qWl
2047 3qWl70pKe0JC/yEhlCkp/wgI/wAAEAAAIQAAKQAAOQAASgAAUgAAYwAAawAAlAAAnAAApQAArQAA
2048 zgAA1gAA5wAA9wAA/0pC/xgQ52Na9ykhe4R7zikhYxgQSjEpQgAAACwAAAAAPAAoAAAI/wABCBxI
2049 sKDBgwgTKiRIYKHDhxARIvgXsaLFhGgEUBSYoKPHjyBDihxJkuS/kwNLqlzJcuTJjQBaypxpEiVH
2050 mjhxvkyZs2fLnTd9ehxAtKjRo0ZrwhTasUsENhYHKOUpk1E3j11mxCBiQVLEBlJd2owp9iVRjwUs
2051 zMCQ5IcLD4saPVxjIKxIoGTvvqSoyFEFGTBeqEhyxAoSFR/USGKVcEGBAwDshsSr1OYTEyhQpJiS
2052 ZcoUKWOQtJDRJFSaggzUGBgoGSTlsjahlPCRIkWVKT16THHRIoqIISBIEUgAYIGBhgRbf3ytFygU
2053 FZp9UDmxQkkMCRwyZKDBQy4aApABhP8XqNwj88l7BVpQYZtF5iArWgwAgGZBq24HU7OeGhQ90PVA
2054 aKZZCiiUMJ9ArSTEwGqR8ZeXfzbV0MIIMQTBwoUdxDDfAm8sZFyDZVEF4UYSKBEBD0+k6IEFPMxH
2055 3FzldXSea+kBgANJSOWIlIMhXZXAXv+c1WM3PuJEpH8iuhbAkv+MdENPRHaTRkdF/jiWSKCAwlKW
2056 VbbkY5Q0LgUSKExgoYBKCjCxARpdltQNKHaUoYAddnR53lVRnJLKBWh4RIEGCZx5FSOv1OLNDUVe
2057 deZHaWiZAB35fIOGNtbEUeV5oGAByzPOrBPFGt3kwEgxITACSg5oLGGLMg60oQAjaNz/oAAcN4Ai
2058 a0c3kHFDK3jYsw4g9sRzBgPLXdkRrBrQ8gsWQUxCCRZX9IJNBQ1s8IgCdeBCzBYN6IBIN2TUsQYd
2059 dXhDBxdzlAHOHHKEcocZdWwDjx8MTCmjsR2FMAstw1RyiSzHqPLALaOwk8QmzCzDCSi0xJKMMk4E
2060 Yw8389iTDT32GAKOPf7YY0Aa9tATyD3w/EGsefgmgEYUtPiChLKWQDMBJtEUgYkzH2RiTgGfTMCI
2061 Mlu0Yc85hNiDziH2tMqOGL72QY47gshLb7Fi4roELcjoQIsxWpDwQyfS2OCJMkLI4YUmyhgxSTVg
2062 CP2FHPZ80UDcieBjStNPD5LPOyZT/y0iHGiMwswexDSzRiRq6KIMJBc4M8skwKAyChia2KPH3P24
2063 YU8/lFhOTj152OPOHuXMU4g48vCRiN/9rZGLMdS4csUu1JzDgxuipOMDHMKsAwEnq/ByzTrrZMNO
2064 OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
2065 xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
2066 gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
2068 # The base64 data in the string above was generated by the following procedure:
2071 # print base64.encodestring(open("fetchmail.gif", "rb").read())
2075 (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV", ["help",
2077 dump = rcfile = None;
2078 for (switch, val) in options:
2079 if (switch == '-d'):
2081 elif (switch == '-f'):
2083 elif (switch == '-h' or switch == '--help'):
2085 Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|--help|-V|--version}
2086 -d - dump configuration (for debugging)
2087 -f fmrc - read alternate fetchmailrc file
2088 --help, -h - print this help text and quit
2089 --version, -V - print fetchmailconf version and quit
2092 elif (switch == '-V' or switch == '--version'):
2093 print "fetchmailconf %s" % version
2095 Copyright (C) 1997 - 2003 Eric S. Raymond
2096 Copyright (C) 2005, 2006, 2008, 2009 Matthias Andree
2097 fetchmailconf comes with ABSOLUTELY NO WARRANTY. This is free software, you are
2098 welcome to redistribute it under certain conditions. Please see the file
2099 COPYING in the source or documentation directory for details."""
2102 # Get client host's FQDN
2103 hostname = socket.gethostbyaddr(socket.gethostname())[0]
2106 ConfigurationDefaults = Configuration()
2107 ServerDefaults = Server()
2108 UserDefaults = User()
2110 # Read the existing configuration. We set the umask to 077 to make sure
2111 # that group & other read/write permissions are shut off -- we wouldn't
2112 # want crackers to snoop password information out of the tempfile.
2113 tmpfile = tempfile.mktemp()
2115 cmd = "umask 077 && fetchmail </dev/null -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2117 cmd = "umask 077 && fetchmail </dev/null --configdump --nosyslog >" + tmpfile
2122 print "`" + cmd + "' run failure, status " + `s`
2125 print "Unknown error while running fetchmail --configdump"
2132 print "Can't read configuration output of fetchmail --configdump."
2138 # The tricky part -- initializing objects from the configuration global
2139 # `Configuration' is the top level of the object tree we're going to mung.
2140 # The dictmembers list is used to track the set of fields the dictionary
2141 # contains; in particular, we can use it to tell whether things like the
2142 # monitor, interface, ssl, sslkey, or sslcert fields are present.
2144 Fetchmailrc = Configuration()
2145 copy_instance(Fetchmailrc, fetchmailrc)
2146 Fetchmailrc.servers = [];
2147 for server in fetchmailrc['servers']:
2149 copy_instance(Newsite, server)
2150 Fetchmailrc.servers.append(Newsite)
2152 for user in server['users']:
2154 copy_instance(Newuser, user)
2155 Newsite.users.append(Newuser)
2157 # We may want to display the configuration and quit
2159 print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2161 # The theory here is that -f alone sets the rcfile location,
2162 # but -d and -f together mean the new configuration should go to stdout.
2163 if not rcfile and not dump:
2164 rcfile = os.environ["HOME"] + "/.fetchmailrc"
2166 # OK, now run the configuration edit
2167 root = MainWindow(rcfile)
2170 # The following sets edit modes for GNU EMACS