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.auth = 'any' # Default to password authentication
92 self.timeout = 300 # 5-minute timeout
93 self.envelope = 'Received' # Envelope-address header
94 self.envskip = 0 # Number of envelope headers to skip
95 self.qvirtual = None # Name prefix to strip
96 self.aka = [] # List of DNS aka names
97 self.dns = TRUE # Enable DNS lookup on multidrop
98 self.localdomains = [] # Domains to be considered local
99 self.interface = None # IP address and range
100 self.monitor = None # IP address and range
101 self.plugin = None # Plugin command for going to server
102 self.plugout = None # Plugin command for going to listener
103 self.principal = None # Kerberos principal
104 self.esmtpname = None # ESMTP 2554 name
105 self.esmtppassword = None # ESMTP 2554 password
106 self.tracepolls = FALSE # Add trace-poll info to headers
107 self.badheader = FALSE # Pass messages with bad headers on?
108 self.retrieveerror = 'abort' # Policy when message retrieval errors encountered
109 self.users = [] # List of user entries for site
111 ('pollname', 'String'),
113 ('active', 'Boolean'),
115 ('protocol', 'String'),
116 ('service', 'String'),
119 ('envelope', 'String'),
121 ('qvirtual', 'String'),
124 # leave localdomains out
125 ('interface', 'String'),
126 ('monitor', 'String'),
127 ('plugin', 'String'),
128 ('plugout', 'String'),
129 ('esmtpname', 'String'),
130 ('esmtppassword', 'String'),
131 ('principal', 'String'),
132 ('tracepolls','Boolean'),
133 ('badheader', 'Boolean'),
134 ('retrieveerror', 'String'))
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
161 res = res + " and options"
162 if self.dns != ServerDefaults.dns:
163 res = res + flag2str(self.dns, 'dns')
164 if folded: res = res + "\n "
165 else: res = res + " "
171 if self.aka and self.localdomains: res = res + " "
172 if self.localdomains:
173 res = res + ("localdomains")
174 for x in self.localdomains:
176 if (self.aka or self.localdomains):
183 res = res + "tracepolls\n"
186 res = res + " interface " + str(self.interface)
188 res = res + " monitor " + str(self.monitor)
190 res = res + " plugin " + `self.plugin`
192 res = res + " plugout " + `self.plugout`
194 res = res + " principal " + `self.principal`
196 res = res + " esmtpname " + `self.esmtpname`
197 if self.esmtppassword:
198 res = res + " esmtppassword " + `self.esmtppassword`
199 if self.interface or self.monitor or self.principal or self.plugin or self.plugout:
204 res = res + "bad-header accept "
205 if self.retrieveerror == 'continue':
206 res = res + "retrieve-error continue "
207 if self.retrieveerror == 'markseen':
208 res = res + "retrieve-error markseen "
209 if self.badheader or self.retrieveerror != ServerDefaults.retrieveerror:
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 = {"pop3":110,
447 # fetchmail protocol to IANA service name
448 defaultports = {"auto":None,
456 authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp",
460 'title' : 'List Selection Help',
461 'banner': 'List Selection',
463 You must select an item in the list box (by clicking on it).
466 def flag2str(value, string):
467 # make a string representation of a .fetchmailrc flag or negated flag
471 if value == FALSE: str = str + ("no ")
475 class LabeledEntry(Frame):
476 # widget consisting of entry field with caption to left
477 def bind(self, key, action):
478 self.E.bind(key, action)
481 def __init__(self, Master, text, textvar, lwidth, ewidth=12):
482 Frame.__init__(self, Master)
483 self.L = Label(self, {'text':text, 'width':lwidth, 'anchor':'w'})
484 self.E = Entry(self, {'textvar':textvar, 'width':ewidth})
485 self.L.pack({'side':'left'})
486 self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
488 def ButtonBar(frame, legend, ref, alternatives, depth, command):
489 # array of radio buttons, caption to left, picking from a string list
491 width = (len(alternatives)+1) / depth;
492 Label(bar, text=legend).pack(side=LEFT)
493 for column in range(width):
494 subframe = Frame(bar)
495 for row in range(depth):
496 ind = width * row + column
497 if ind < len(alternatives):
498 Radiobutton(subframe,
499 {'text':alternatives[ind],
501 'value':alternatives[ind],
502 'command':command}).pack(side=TOP, anchor=W)
504 # This is just a spacer
505 Radiobutton(subframe,
506 {'text':" ",'state':DISABLED}).pack(side=TOP, anchor=W)
507 subframe.pack(side=LEFT)
511 def helpwin(helpdict):
512 # help message window with a self-destruct button
514 helpwin.title(helpdict['title'])
515 helpwin.iconname(helpdict['title'])
516 Label(helpwin, text=helpdict['banner']).pack()
517 textframe = Frame(helpwin)
518 scroll = Scrollbar(textframe)
519 helpwin.textwidget = Text(textframe, setgrid=TRUE)
520 textframe.pack(side=TOP, expand=YES, fill=BOTH)
521 helpwin.textwidget.config(yscrollcommand=scroll.set)
522 helpwin.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
523 scroll.config(command=helpwin.textwidget.yview)
524 scroll.pack(side=RIGHT, fill=BOTH)
525 helpwin.textwidget.insert(END, helpdict['text']);
526 Button(helpwin, text='Done',
527 command=lambda x=helpwin: x.destroy(), bd=2).pack()
528 textframe.pack(side=TOP)
530 def make_icon_window(base, image):
532 # Some older pythons will error out on this
533 icon_image = PhotoImage(data=image)
534 icon_window = Toplevel()
535 Label(icon_window, image=icon_image, bg='black').pack()
536 base.master.iconwindow(icon_window)
537 # Avoid TkInter brain death. PhotoImage objects go out of
538 # scope when the enclosing function returns. Therefore
539 # we have to explicitly link them to something.
540 base.keepalive.append(icon_image)
544 class ListEdit(Frame):
545 # edit a list of values (duplicates not allowed) with a supplied editor hook
546 def __init__(self, newlegend, list, editor, deletor, master, helptxt):
548 self.deletor = deletor
551 # Set up a widget to accept new elements
552 self.newval = StringVar(master)
553 newwin = LabeledEntry(master, newlegend, self.newval, '12')
554 newwin.bind('<Double-1>', self.handleNew)
555 newwin.bind('<Return>', self.handleNew)
556 newwin.pack(side=TOP, fill=X, anchor=E)
558 # Edit the existing list
559 listframe = Frame(master)
560 scroll = Scrollbar(listframe)
561 self.listwidget = Listbox(listframe, height=0, selectmode='browse')
564 self.listwidget.insert(END, x)
565 listframe.pack(side=TOP, expand=YES, fill=BOTH)
566 self.listwidget.config(yscrollcommand=scroll.set)
567 self.listwidget.pack(side=LEFT, expand=YES, fill=BOTH)
568 scroll.config(command=self.listwidget.yview)
569 scroll.pack(side=RIGHT, fill=BOTH)
570 self.listwidget.config(selectmode=SINGLE, setgrid=TRUE)
571 self.listwidget.bind('<Double-1>', self.handleList);
572 self.listwidget.bind('<Return>', self.handleList);
576 Button(bf, text='Edit', command=self.editItem).pack(side=LEFT)
577 Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT)
579 self.helptxt = helptxt
580 Button(bf, text='Help', fg='blue',
581 command=self.help).pack(side=RIGHT)
585 helpwin(self.helptxt)
587 def handleList(self, event):
590 def handleNew(self, event):
591 item = self.newval.get()
593 entire = self.listwidget.get(0, self.listwidget.index('end'));
594 if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
595 self.listwidget.insert('end', item)
596 if self.list != None: self.list.append(item)
598 apply(self.editor, (item,))
602 select = self.listwidget.curselection()
607 if index and self.editor:
608 label = self.listwidget.get(index);
610 apply(self.editor, (label,))
612 def deleteItem(self):
613 select = self.listwidget.curselection()
617 index = string.atoi(select[0])
618 label = self.listwidget.get(index);
619 self.listwidget.delete(index)
620 if self.list != None:
622 if self.deletor != None:
623 apply(self.deletor, (label,))
625 def ConfirmQuit(frame, context):
628 text = 'Really quit ' + context + ' without saving?',
630 strings = ('Yes', 'No'),
634 def dispose_window(master, legend, help, savelegend='OK'):
635 dispose = Frame(master, relief=RAISED, bd=5)
636 Label(dispose, text=legend).pack(side=TOP,pady=10)
637 Button(dispose, text=savelegend, fg='blue',
638 command=master.save).pack(side=LEFT)
639 Button(dispose, text='Quit', fg='blue',
640 command=master.nosave).pack(side=LEFT)
641 Button(dispose, text='Help', fg='blue',
642 command=lambda x=help: helpwin(x)).pack(side=RIGHT)
647 # Common methods for Tkinter widgets -- deals with Tkinter declaration
648 def post(self, widgetclass, field):
649 for x in widgetclass.typemap:
650 if x[1] == 'Boolean':
651 setattr(self, x[0], BooleanVar(self))
652 elif x[1] == 'String':
653 setattr(self, x[0], StringVar(self))
655 setattr(self, x[0], IntVar(self))
656 source = getattr(getattr(self, field), x[0])
658 getattr(self, x[0]).set(source)
660 def fetch(self, widgetclass, field):
661 for x in widgetclass.typemap:
662 setattr(getattr(self, field), x[0], getattr(self, x[0]).get())
665 # First, code to set the global fetchmail run controls.
668 configure_novice_help = {
669 'title' : 'Fetchmail novice configurator help',
670 'banner': 'Novice configurator help',
672 In the `Novice Configurator Controls' panel, you can:
674 Press `Save' to save the new fetchmail configuration you have created.
675 Press `Quit' to exit without saving.
676 Press `Help' to bring up this help message.
678 In the `Novice Configuration' panels, you will set up the basic data
679 needed to create a simple fetchmail setup. These include:
681 1. The name of the remote site you want to query.
683 2. Your login name on that site.
685 3. Your password on that site.
687 4. A protocol to use (POP, IMAP, ETRN, etc.)
689 5. A poll interval in seconds.
690 If 0, fetchmail will run in the foreground once when started.
691 If > 0, fetchmail will run in the background and start a new poll
692 cycle after the interval has elapsed.
694 6. Options to fetch old messages as well as new, or to suppress
695 deletion of fetched message.
697 The novice-configuration code will assume that you want to forward mail
698 to a local sendmail listener with no special options.
701 configure_expert_help = {
702 'title' : 'Fetchmail expert configurator help',
703 'banner': 'Expert configurator help',
705 In the `Expert Configurator Controls' panel, you can:
707 Press `Save' to save the new fetchmail configuration you have edited.
708 Press `Quit' to exit without saving.
709 Press `Help' to bring up this help message.
711 In the `Run Controls' panel, you can set the following options that
712 control how fetchmail runs:
715 Number of seconds to wait between polls in the background.
716 If zero, fetchmail will run in foreground.
719 If empty, emit progress and error messages to stderr.
720 Otherwise this gives the name of the files to write to.
721 This field is ignored if the "Log to syslog?" option is on.
724 If empty, store seen-message IDs in .fetchids under user's home
725 directory. If nonempty, use given file name.
728 Who to send multidrop mail to as a last resort if no address can
729 be matched. Normally empty; in this case, fetchmail treats the
730 invoking user as the address of last resort unless that user is
731 root. If that user is root, fetchmail sends to `postmaster'.
734 If this option is on (the default) error mail goes to the sender.
735 Otherwise it goes to the postmaster.
738 If this option is on, spam bounces are sent to the sender or
739 postmaster (depending on the "Bounces to sender?" option. Otherwise,
740 spam bounces are not sent (the default).
743 If this option is on, permanent delivery errors are treated as
744 temporary, i. e. mail is kept on the upstream server. Useful
745 during testing and after configuration changes, and on by
747 If this option is off, permanent delivery errors delete
748 undeliverable mail from the upstream.
751 If false (the default) fetchmail generates a Received line into
752 each message and generates a HELO from the machine it is running on.
753 If true, fetchmail generates no Received line and HELOs as if it were
756 In the `Remote Mail Configurations' panel, you can:
758 1. Enter the name of a new remote mail server you want fetchmail to query.
760 To do this, simply enter a label for the poll configuration in the
761 `New Server:' box. The label should be a DNS name of the server (unless
762 you are using ssh or some other tunneling method and will fill in the `via'
763 option on the site configuration screen).
765 2. Change the configuration of an existing site.
767 To do this, find the site's label in the listbox and double-click it.
768 This will take you to a site configuration dialogue.
772 class ConfigurationEdit(Frame, MyWidget):
773 def __init__(self, configuration, outfile, master, onexit):
775 self.configuration = configuration
776 self.outfile = outfile
777 self.container = master
779 ConfigurationEdit.mode_to_help = {
780 'novice':configure_novice_help, 'expert':configure_expert_help
783 def server_edit(self, sitename):
784 self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
786 def server_delete(self, sitename):
788 for user in self.subwidgets.keys():
790 del self.configuration[sitename]
794 def edit(self, mode):
796 Frame.__init__(self, self.container)
797 self.master.title('fetchmail ' + self.mode + ' configurator');
798 self.master.iconname('fetchmail ' + self.mode + ' configurator');
799 self.master.protocol('WM_DELETE_WINDOW', self.nosave)
800 self.keepalive = [] # Use this to anchor the PhotoImage object
801 make_icon_window(self, fetchmail_icon)
803 self.post(Configuration, 'configuration')
806 'Configurator ' + self.mode + ' Controls',
807 ConfigurationEdit.mode_to_help[self.mode],
810 gf = Frame(self, relief=RAISED, bd = 5)
812 text='Fetchmail Run Controls',
813 bd=2).pack(side=TOP, pady=10)
818 if self.mode != 'novice':
820 log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
821 log.pack(side=RIGHT, anchor=E)
823 # Set the poll interval
824 de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
825 de.pack(side=RIGHT, anchor=E)
830 if self.mode != 'novice':
833 {'text':'Bounces to sender?',
834 'variable':self.bouncemail,
835 'relief':GROOVE}).pack(side=LEFT, anchor=W)
840 {'text':'Send spam bounces?',
841 'variable':self.spambounce,
842 'relief':GROOVE}).pack(side=LEFT, anchor=W)
847 {'text':'Treat permanent errors as temporary?',
848 'variable':self.softbounce,
849 'relief':GROOVE}).pack(side=LEFT, anchor=W)
854 {'text':'Log to syslog?',
855 'variable':self.syslog,
856 'relief':GROOVE}).pack(side=LEFT, anchor=W)
857 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
858 log.pack(side=RIGHT, anchor=E)
862 {'text':'Invisible mode?',
863 'variable':self.invisible,
864 'relief':GROOVE}).pack(side=LEFT, anchor=W)
866 log = LabeledEntry(gf, ' Idfile:', self.idfile, '14')
867 log.pack(side=RIGHT, anchor=E)
871 # Expert mode allows us to edit multiple sites
872 lf = Frame(self, relief=RAISED, bd=5)
874 text='Remote Mail Server Configurations',
875 bd=2).pack(side=TOP, pady=10)
876 ListEdit('New Server:',
877 map(lambda x: x.pollname, self.configuration.servers),
878 lambda site, self=self: self.server_edit(site),
879 lambda site, self=self: self.server_delete(site),
884 for sitename in self.subwidgets.keys():
885 self.subwidgets[sitename].destruct()
886 self.master.destroy()
890 if ConfirmQuit(self, self.mode + " configuration editor"):
894 for sitename in self.subwidgets.keys():
895 self.subwidgets[sitename].save()
896 self.fetch(Configuration, 'configuration')
900 elif not os.path.isfile(self.outfile) or Dialog(self,
901 title = 'Overwrite existing run control file?',
902 text = 'Really overwrite existing run control file?',
904 strings = ('Yes', 'No'),
905 default = 1).num == 0:
907 os.rename(self.outfile, self.outfile + "~")
908 # Pre-1.5.2 compatibility...
911 oldumask = os.umask(077)
912 fm = open(self.outfile, 'w')
917 os.chmod(self.outfile, 0600)
918 fm.write("# Configuration created %s by fetchmailconf %s\n" % (time.ctime(time.time()), version))
919 fm.write(`self.configuration`)
925 # Server editing stuff.
928 'title' : 'Remote site help',
929 'banner': 'Remote sites',
931 When you add a site name to the list here,
932 you initialize an entry telling fetchmail
933 how to poll a new site.
935 When you select a sitename (by double-
936 clicking it, or by single-clicking to
937 select and then clicking the Edit button),
938 you will open a window to configure that
943 'title' : 'Server options help',
944 'banner': 'Server Options',
946 The server options screen controls fetchmail
947 options that apply to one of your mailservers.
949 Once you have a mailserver configuration set
950 up as you like it, you can select `OK' to
951 store it in the server list maintained in
952 the main configuration window.
954 If you wish to discard changes to a server
955 configuration, select `Quit'.
959 'title' : 'Run Control help',
960 'banner': 'Run Controls',
962 If the `Poll normally' checkbox is on, the host is polled as part of
963 the normal operation of fetchmail when it is run with no arguments.
964 If it is off, fetchmail will only query this host when it is given as
965 a command-line argument.
967 The `Retrieve Error Policy' specifies how server errors during
968 message retrieval are handled. The default behaviour is to abort the
969 current session. Both the continue and markseen options will skip
970 the message with the error, but continue the session allowing for
971 downloading of subsequent messages. Additionally, the markseen
972 option will mark the skipped message as seen.
974 The `True name of server' box should specify the actual DNS name
975 to query. By default this is the same as the poll name.
977 Normally each host described in the file is queried once each
978 poll cycle. If `Cycles to skip between polls' is greater than 0,
979 that's the number of poll cycles that are skipped between the
980 times this post is actually polled.
982 The `Server timeout' is the number of seconds fetchmail will wait
983 for a reply from the mailserver before concluding it is hung and
988 'title' : 'Protocol and Port help',
989 'banner': 'Protocol and Port',
991 These options control the remote-mail protocol
992 and TCP/IP service port used to query this
995 If you click the `Probe for supported protocols'
996 button, fetchmail will try to find you the most
997 capable server on the selected host (this will
998 only work if you're conncted to the Internet).
999 The probe only checks for ordinary IMAP and POP
1000 protocols; fortunately these are the most
1001 frequently supported.
1003 The `Protocol' button bar offers you a choice of
1004 all the different protocols available. The `auto'
1005 protocol is the default mode; it probes the host
1006 ports for POP3 and IMAP to see if either is
1009 Normally the TCP/IP service port to use is
1010 dictated by the protocol choice. The `Service'
1011 field (only present in expert mode) lets you
1012 set a non-standard service (port).
1016 'title' : 'Security option help',
1017 'banner': 'Security',
1019 The 'authorization mode' allows you to choose the
1020 mode that fetchmail uses to log in to your server. You
1021 can usually leave this at 'any', but you will have to pick
1022 'NTLM' and 'MSN' manually for the nonce.
1024 The 'interface' option allows you to specify a range
1025 of IP addresses to monitor for activity. If these
1026 addresses are not active, fetchmail will not poll.
1027 Specifying this may protect you from a spoofing attack
1028 if your client machine has more than one IP gateway
1029 address and some of the gateways are to insecure nets.
1031 The `monitor' option, if given, specifies the only
1032 device through which fetchmail is permitted to connect
1033 to servers. This option may be used to prevent
1034 fetchmail from triggering an expensive dial-out if the
1035 interface is not already active.
1037 The `interface' and `monitor' options are available
1038 only for Linux and freeBSD systems. See the fetchmail
1039 manual page for details on these.
1041 The ssl option enables SSL communication with a mailserver
1042 supporting Secure Sockets Layer. The sslkey and sslcert options
1043 declare key and certificate files for use with SSL.
1044 The sslcertck option enables strict checking of SSL server
1045 certificates (and sslcertpath gives the trusted certificate
1046 directory). The sslcommonname option helps if the server is
1047 misconfigured and returning "Server CommonName mismatch"
1048 warnings. With sslfingerprint, you can specify a finger-
1049 print the server's key is checked against.
1053 'title' : 'Multidrop option help',
1054 'banner': 'Multidrop',
1056 These options are only useful with multidrop mode.
1057 See the manual page for extended discussion.
1061 'title' : 'User list help',
1062 'banner': 'User list',
1064 When you add a user name to the list here,
1065 you initialize an entry telling fetchmail
1066 to poll the site on behalf of the new user.
1068 When you select a username (by double-
1069 clicking it, or by single-clicking to
1070 select and then clicking the Edit button),
1071 you will open a window to configure the
1072 user's options on that site.
1075 class ServerEdit(Frame, MyWidget):
1076 def __init__(self, host, parent):
1077 self.parent = parent
1079 self.subwidgets = {}
1080 for site in parent.configuration.servers:
1081 if site.pollname == host:
1083 if (self.server == None):
1084 self.server = Server()
1085 self.server.pollname = host
1086 self.server.via = None
1087 parent.configuration.servers.append(self.server)
1089 def edit(self, mode, master=None):
1090 Frame.__init__(self, master)
1092 self.master.title('Fetchmail host ' + self.server.pollname);
1093 self.master.iconname('Fetchmail host ' + self.server.pollname);
1094 self.post(Server, 'server')
1095 self.makeWidgets(self.server.pollname, mode)
1096 self.keepalive = [] # Use this to anchor the PhotoImage object
1097 make_icon_window(self, fetchmail_icon)
1100 # self.wait_window()
1104 for username in self.subwidgets.keys():
1105 self.subwidgets[username].destruct()
1106 del self.parent.subwidgets[self.server.pollname]
1107 self.master.destroy()
1110 if ConfirmQuit(self, 'server option editing'):
1114 self.fetch(Server, 'server')
1115 for username in self.subwidgets.keys():
1116 self.subwidgets[username].save()
1119 def defaultPort(self):
1120 proto = self.protocol.get()
1121 # Callback to reset the port number whenever the protocol type changes.
1122 # We used to only reset the port if it had a default (zero) value.
1123 # This turns out to be a bad idea especially in Novice mode -- if
1124 # you set POP3 and then set IMAP, the port invisibly remained 110.
1125 # Now we reset unconditionally on the theory that if you're setting
1126 # a custom port number you should be in expert mode and playing
1127 # close enough attention to notice this...
1128 self.service.set(defaultports[proto])
1130 def user_edit(self, username, mode):
1131 self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1133 def user_delete(self, username):
1134 if self.subwidgets.has_key(username):
1135 self.subwidgets[username].destruct()
1136 del self.server[username]
1138 def makeWidgets(self, host, mode):
1139 topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1141 leftwin = Frame(self);
1144 if mode != 'novice':
1145 ctlwin = Frame(leftwin, relief=RAISED, bd=5)
1146 Label(ctlwin, text="Run Controls").pack(side=TOP)
1147 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
1148 Checkbutton(ctlwin, text='Pass messages with bad headers?',
1149 variable=self.badheader).pack(side=TOP)
1150 retrieveerrorlist = ['abort', 'continue', 'markseen']
1151 Label(ctlwin, text="Retrieve Error Policy").pack(side=TOP)
1152 ButtonBar(ctlwin, '', self.retrieveerror, retrieveerrorlist, 1, None)
1153 LabeledEntry(ctlwin, 'True name of ' + host + ':',
1154 self.via, leftwidth).pack(side=TOP, fill=X)
1155 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
1156 self.interval, leftwidth).pack(side=TOP, fill=X)
1157 LabeledEntry(ctlwin, 'Server timeout (seconds):',
1158 self.timeout, leftwidth).pack(side=TOP, fill=X)
1159 Button(ctlwin, text='Help', fg='blue',
1160 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
1163 # Compute the available protocols from the compile-time options
1164 protolist = ['auto']
1165 if 'pop3' in feature_options:
1166 protolist = protolist + ["POP3", "APOP", "KPOP"]
1167 if 'sdps' in feature_options:
1168 protolist.append("SDPS")
1169 if 'imap' in feature_options:
1170 protolist.append("IMAP")
1171 if 'etrn' in feature_options:
1172 protolist.append("ETRN")
1173 if 'odmr' in feature_options:
1174 protolist.append("ODMR")
1176 protwin = Frame(leftwin, relief=RAISED, bd=5)
1177 Label(protwin, text="Protocol").pack(side=TOP)
1178 ButtonBar(protwin, '',
1179 self.protocol, protolist, 2,
1181 if mode != 'novice':
1182 LabeledEntry(protwin, 'On server TCP/IP service:',
1183 self.service, leftwidth).pack(side=TOP, fill=X)
1185 Button(protwin, text='Probe for supported protocols', fg='blue',
1186 command=self.autoprobe).pack(side=LEFT)
1187 Button(protwin, text='Help', fg='blue',
1188 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
1189 protwin.pack(fill=X)
1191 userwin = Frame(leftwin, relief=RAISED, bd=5)
1192 Label(userwin, text="User entries for " + host).pack(side=TOP)
1193 ListEdit("New user: ",
1194 map(lambda x: x.remote, self.server.users),
1195 lambda u, m=mode, s=self: s.user_edit(u, m),
1196 lambda u, s=self: s.user_delete(u),
1198 userwin.pack(fill=X)
1200 leftwin.pack(side=LEFT, anchor=N, fill=X);
1202 if mode != 'novice':
1203 rightwin = Frame(self);
1205 mdropwin = Frame(rightwin, relief=RAISED, bd=5)
1206 Label(mdropwin, text="Multidrop options").pack(side=TOP)
1207 LabeledEntry(mdropwin, 'Envelope address header:',
1208 self.envelope, '22').pack(side=TOP, fill=X)
1209 LabeledEntry(mdropwin, 'Envelope headers to skip:',
1210 self.envskip, '22').pack(side=TOP, fill=X)
1211 LabeledEntry(mdropwin, 'Name prefix to strip:',
1212 self.qvirtual, '22').pack(side=TOP, fill=X)
1213 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
1214 variable=self.dns).pack(side=TOP)
1215 Label(mdropwin, text="DNS aliases").pack(side=TOP)
1216 ListEdit("New alias: ", self.server.aka, None, None, mdropwin, None)
1217 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
1218 ListEdit("New domain: ",
1219 self.server.localdomains, None, None, mdropwin, multihelp)
1220 mdropwin.pack(fill=X)
1222 if os_type in ('linux', 'freebsd'):
1223 secwin = Frame(rightwin, relief=RAISED, bd=5)
1224 Label(secwin, text="Security").pack(side=TOP)
1225 # Don't actually let users set this. KPOP sets it implicitly
1226 ButtonBar(secwin, 'Authorization mode:',
1227 self.auth, authlist, 2, None).pack(side=TOP)
1228 if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
1229 LabeledEntry(secwin, 'IP range to check before poll:',
1230 self.interface, leftwidth).pack(side=TOP, fill=X)
1231 if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
1232 LabeledEntry(secwin, 'Interface to monitor:',
1233 self.monitor, leftwidth).pack(side=TOP, fill=X)
1234 # Someday this should handle Kerberos 5 too
1235 if 'kerberos' in feature_options:
1236 LabeledEntry(secwin, 'Principal:',
1237 self.principal, '12').pack(side=TOP, fill=X)
1238 # ESMTP authentication
1239 LabeledEntry(secwin, 'ESMTP name:',
1240 self.esmtpname, '12').pack(side=TOP, fill=X)
1241 LabeledEntry(secwin, 'ESMTP password:',
1242 self.esmtppassword, '12').pack(side=TOP, fill=X)
1243 Button(secwin, text='Help', fg='blue',
1244 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
1247 rightwin.pack(side=LEFT, anchor=N);
1249 def autoprobe(self):
1250 # Note: this only handles case (1) near fetchmail.c:1032
1251 # We're assuming people smart enough to set up ssh tunneling
1252 # won't need autoprobing.
1254 realhost = self.server.via
1256 realhost = self.server.pollname
1258 for protocol in ("IMAP","POP3"):
1259 service = defaultports[protocol]
1260 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1262 sock.connect((realhost, ianaservices[service]))
1263 greetline = sock.recv(1024)
1269 confwin = Toplevel()
1270 if greetline == None:
1271 title = "Autoprobe of " + realhost + " failed"
1273 Fetchmailconf didn't find any mailservers active.
1274 This could mean the host doesn't support any,
1275 or that your Internet connection is down, or
1276 that the host is so slow that the probe timed
1277 out before getting a response.
1281 # OK, now try to recognize potential problems
1283 ### POP3 servers start here
1285 if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
1286 warnings = warnings + """
1287 This appears to be an old version of the UC Davis POP server. These are
1288 dangerously unreliable (among other problems, they may drop your mailbox
1289 on the floor if your connection is interrupted during the session).
1291 It is strongly recommended that you find a better POP3 server. The fetchmail
1292 FAQ includes pointers to good ones.
1295 if string.find(greetline, "comcast.net") > 0:
1296 warnings = warnings + """
1297 The Comcast Maillennium POP3 server only returns the first 80K of a long
1298 message retrieved with TOP. Its response to RETR is normal, so use the
1302 # Steve VanDevender <stevev@efn.org> writes:
1303 # The only system I have seen this happen with is cucipop-1.31
1304 # under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
1305 # 2.x and probably quite a few other systems. It appears to be a
1306 # bug or bad interaction with the SunOS realloc() -- it turns out
1307 # that internally cucipop does allocate a certain data structure in
1308 # multiples of 16, using realloc() to bump it up to the next
1309 # multiple if it needs more.
1311 # The distinctive symptom is that when there are 16 messages in the
1312 # inbox, you can RETR and DELE all 16 messages successfully, but on
1313 # QUIT cucipop returns something like "-ERR Error locking your
1314 # mailbox" and aborts without updating it.
1316 # The cucipop banner looks like:
1318 # +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1320 if string.find(greetline, "Cubic Circle") > 0:
1321 warnings = warnings + """
1322 I see your server is running cucipop. Better make sure the server box
1323 isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
1324 under that version, and doesn't cope with the result gracefully. Newer
1325 SunOS and Solaris machines run cucipop OK.
1327 Also, some versions of cucipop don't assert an exclusive lock on your
1328 mailbox when it's being queried. This means that if you have more than
1329 one fetchmail query running against the same mailbox, bad things can happen.
1331 if string.find(greetline, "David POP3 Server") > 0:
1332 warnings = warnings + """
1333 This POP3 server is badly broken. You should get rid of it -- and the
1334 brain-dead Microsoft operating system it rode in on.
1337 # The greeting line on the server known to be buggy is:
1338 # +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
1340 if string.find(greetline, "FTGate") > 0:
1341 warnings = warnings + """
1342 This POP server has a weird bug; it says OK twice in response to TOP.
1343 Its response to RETR is normal, so use the `fetchall' option.
1346 if string.find(greetline, " geonet.de") > 0:
1347 warnings = warnings + """
1348 You appear to be using geonet. As of late 2002, the TOP command on
1349 geonet's POP3 is broken. Use the fetchall option.
1352 if string.find(greetline, "OpenMail") > 0:
1353 warnings = warnings + """
1354 You appear to be using some version of HP OpenMail. Many versions of
1355 OpenMail do not process the "TOP" command correctly; the symptom is that
1356 only the header and first line of each message is retrieved. To work
1357 around this bug, turn on `fetchall' on all user entries associated with
1361 if string.find(greetline, "Escape character is") > 0:
1362 warnings = warnings + """
1363 Your greeting line looks like it was written by a fetid pile of
1364 camel dung identified to me as `popa3d written by Solar Designer'.
1365 Beware! The UIDL support in this thing is known to be completely broken,
1366 and other things probably are too.
1369 if string.find(greetline, "MercuryP/NLM v1.48") > 0:
1370 warnings = warnings + """
1371 This is not a POP3 server. It has delusions of being one, but after
1372 RETR all messages are automatically marked to be deleted. The only
1373 way to prevent this is to issue an RSET before leaving the server.
1374 Fetchmail does this, but we suspect this is probably broken in lots
1378 if string.find(greetline, "POP-Max") > 0:
1379 warnings = warnings + """
1380 The Mail Max POP3 server screws up on mail with attachments. It
1381 reports the message size with attachments included, but doesn't
1382 download them on a RETR or TOP (this violates the IMAP RFCs). It also
1383 doesn't implement TOP correctly. You should get rid of it -- and the
1384 brain-dead NT server it rode in on.
1387 if string.find(greetline, "POP3 Server Ready") > 0:
1388 warnings = warnings + """
1389 Some server that uses this greeting line has been observed to choke on
1390 TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
1393 if string.find(greetline, "QPOP") > 0:
1394 warnings = warnings + """
1395 This appears to be a version of Eudora qpopper. That's good. Fetchmail
1396 knows all about qpopper. However, be aware that the 2.53 version of
1397 qpopper does something odd that causes fetchmail to hang with a socket
1398 error on very large messages. This is probably not a fetchmail bug, as
1399 it has been observed with fetchpop. The fix is to upgrade to qpopper
1400 3.0beta or a more recent version. Better yet, switch to IMAP.
1403 if string.find(greetline, " sprynet.com") > 0:
1404 warnings = warnings + """
1405 You appear to be using a SpryNet server. In mid-1999 it was reported that
1406 the SpryNet TOP command marks messages seen. Therefore, for proper error
1407 recovery in the event of a line drop, it is strongly recommended that you
1408 turn on `fetchall' on all user entries associated with this server.
1411 if string.find(greetline, "TEMS POP3") > 0:
1412 warnings = warnings + """
1413 Your POP3 server has "TEMS" in its header line. At least one such
1414 server does not process the "TOP" command correctly; the symptom is
1415 that fetchmail hangs when trying to retrieve mail. To work around
1416 this bug, turn on `fetchall' on all user entries associated with this
1420 if string.find(greetline, " spray.se") > 0:
1421 warnings = warnings + """
1422 Your POP3 server has "spray.se" in its header line. In May 2000 at
1423 least one such server did not process the "TOP" command correctly; the
1424 symptom is that messages are treated as headerless. To work around
1425 this bug, turn on `fetchall' on all user entries associated with this
1429 if string.find(greetline, " usa.net") > 0:
1430 warnings = warnings + """
1431 You appear to be using USA.NET's free mail service. Their POP3 servers
1432 (at least as of the 2.2 version in use mid-1998) are quite flaky, but
1433 fetchmail can compensate. They seem to require that fetchall be switched on
1434 (otherwise you won't necessarily see all your mail, not even new mail).
1435 They also botch the TOP command the fetchmail normally uses for retrieval
1436 (it only retrieves about 10 lines rather than the number specified).
1437 Turning on fetchall will disable the use of TOP.
1439 Therefore, it is strongly recommended that you turn on `fetchall' on all
1440 user entries associated with this server.
1443 if string.find(greetline, " Novonyx POP3") > 0:
1444 warnings = warnings + """
1445 Your mailserver is running Novonyx POP3. This server, at least as of
1446 version 2.17, seems to have problems handling and reporting seen bits.
1447 You may have to use the fetchall option.
1450 if string.find(greetline, " IMS POP3") > 0:
1451 warnings = warnings + """
1452 Some servers issuing the greeting line 'IMS POP3' have been known to
1453 do byte-stuffing incorrectly. This means that if a message you receive
1454 has a . (period) at start of line, fetchmail will become confused and
1455 probably wedge itself. (This bug was recorded on IMS POP3 0.86.)
1459 ### IMAP servers start here
1461 if string.find(greetline, "GroupWise") > 0:
1462 warnings = warnings + """
1463 The Novell GroupWise IMAP server would be better named GroupFoolish;
1464 it is (according to the designer of IMAP) unusably broken. Among
1465 other things, it doesn't include a required content length in its
1466 BODY[TEXT] response.<p>
1468 Fetchmail works around this problem, but we strongly recommend voting
1469 with your dollars for a server that isn't brain-dead. If you stick
1470 with code as shoddy as GroupWise seems to be, you will probably pay
1471 for it with other problems.<p>
1474 if string.find(greetline, "InterChange") > 0:
1475 warnings = warnings + """
1477 The InterChange IMAP server at release levels below 3.61.08 screws up
1478 on mail with attachments. It doesn't fetch them if you give it a
1479 BODY[TEXT] request, though it does if you request RFC822.TEXT.
1480 According to the IMAP RFCs and their maintainer these should be
1481 equivalent -- and we can't drop the BODY[TEXT] form because M$
1482 Exchange (quite legally under RFC2062) rejectsit. The InterChange
1483 folks claim to have fixed this bug in 3.61.08.
1486 if string.find(greetline, "Imail") > 0:
1487 warnings = warnings + """
1488 We've seen a bug report indicating that this IMAP server (at least as of
1489 version 5.0.7) returns an invalid body size for messages with MIME
1490 attachments; the effect is to drop the attachments on the floor. We
1491 recommend you upgrade to a non-broken IMAP server.
1494 if string.find(greetline, "Domino IMAP4") > 0:
1495 warnings = warnings + """
1496 Your IMAP server appears to be Lotus Domino. This server, at least up
1497 to version 4.6.2a, has a bug in its generation of MIME boundaries (see
1498 the details in the fetchmail FAQ). As a result, even MIME aware MUAs
1499 will see attachments as part of the message text. If your Domino server's
1500 POP3 facility is enabled, we recommend you fall back on it.
1504 ### Checks for protocol variants start here
1506 closebrak = string.find(greetline, ">")
1507 if closebrak > 0 and greetline[closebrak+1] == "\r":
1508 warnings = warnings + """
1509 It looks like you could use APOP on this server and avoid sending it your
1510 password in clear. You should talk to the mailserver administrator about
1514 if string.find(greetline, "IMAP2bis") > 0:
1515 warnings = warnings + """
1516 IMAP2bis servers have a minor problem; they can't peek at messages without
1517 marking them seen. If you take a line hit during the retrieval, the
1518 interrupted message may get left on the server, marked seen.
1520 To work around this, it is recommended that you set the `fetchall'
1521 option on all user entries associated with this server, so any stuck
1522 mail will be retrieved next time around.
1524 To fix this bug, upgrade to an IMAP4 server. The fetchmail FAQ includes
1525 a pointer to an open-source implementation.
1528 if string.find(greetline, "IMAP4rev1") > 0:
1529 warnings = warnings + """
1530 I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
1531 remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
1532 has therefore been extremely well tested with this class of server.
1536 warnings = warnings + """
1537 Fetchmail doesn't know anything special about this server type.
1541 # Display success window with warnings
1542 title = "Autoprobe of " + realhost + " succeeded"
1543 confirm = "The " + protocol + " server said:\n\n" + greetline + warnings
1544 self.protocol.set(protocol)
1545 self.service.set(defaultports[protocol])
1546 confwin.title(title)
1547 confwin.iconname(title)
1548 Label(confwin, text=title).pack()
1549 Message(confwin, text=confirm, width=600).pack()
1550 Button(confwin, text='Done',
1551 command=lambda x=confwin: x.destroy(), bd=2).pack()
1554 # User editing stuff
1558 'title' : 'User option help',
1559 'banner': 'User options',
1561 You may use this panel to set options
1562 that may differ between individual
1565 Once you have a user configuration set
1566 up as you like it, you can select `OK' to
1567 store it in the user list maintained in
1568 the site configuration window.
1570 If you wish to discard the changes you have
1571 made to user options, select `Quit'.
1575 'title' : 'Local name help',
1576 'banner': 'Local names',
1578 The local name(s) in a user entry are the
1579 people on the client machine who should
1580 receive mail from the poll described.
1582 Note: if a user entry has more than one
1583 local name, messages will be retrieved
1584 in multidrop mode. This complicates
1585 the configuration issues; see the manual
1586 page section on multidrop mode.
1588 Warning: Be careful with local names
1589 such as foo@bar.com, as that can cause
1590 the mail to be sent to foo@bar.com instead
1591 of sending it to your local system.
1594 class UserEdit(Frame, MyWidget):
1595 def __init__(self, username, parent):
1596 self.parent = parent
1598 for user in parent.server.users:
1599 if user.remote == username:
1601 if self.user == None:
1603 self.user.remote = username
1604 self.user.localnames = [username]
1605 parent.server.users.append(self.user)
1607 def edit(self, mode, master=None):
1608 Frame.__init__(self, master)
1610 self.master.title('Fetchmail user ' + self.user.remote
1611 + ' querying ' + self.parent.server.pollname);
1612 self.master.iconname('Fetchmail user ' + self.user.remote);
1613 self.post(User, 'user')
1614 self.makeWidgets(mode, self.parent.server.pollname)
1615 self.keepalive = [] # Use this to anchor the PhotoImage object
1616 make_icon_window(self, fetchmail_icon)
1619 # self.wait_window()
1623 # Yes, this test can fail -- if you delete the parent window.
1624 if self.parent.subwidgets.has_key(self.user.remote):
1625 del self.parent.subwidgets[self.user.remote]
1626 self.master.destroy()
1629 if ConfirmQuit(self, 'user option editing'):
1634 for x in self.user.localnames: ok = ok + (string.find(x, '@') != -1)
1635 if ok == 0 or Dialog(self,
1636 title = "Really accept an embedded '@' ?",
1637 text = "Local names with an embedded '@', such as in foo@bar "
1638 "might result in your mail being sent to foo@bar.com "
1639 "instead of your local system.\n Are you sure you want "
1640 "a local user name with an '@' in it?",
1641 bitmap = 'question',
1642 strings = ('Yes', 'No'),
1643 default = 1).num == 0:
1644 self.fetch(User, 'user')
1647 def makeWidgets(self, mode, servername):
1648 dispose_window(self,
1649 "User options for " + self.user.remote + " querying " + servername,
1652 if mode != 'novice':
1653 leftwin = Frame(self);
1657 secwin = Frame(leftwin, relief=RAISED, bd=5)
1658 Label(secwin, text="Authentication").pack(side=TOP)
1659 LabeledEntry(secwin, 'Password:',
1660 self.password, '12').pack(side=TOP, fill=X)
1661 secwin.pack(fill=X, anchor=N)
1663 if 'ssl' in feature_options or 'ssl' in dictmembers:
1664 sslwin = Frame(leftwin, relief=RAISED, bd=5)
1665 Checkbutton(sslwin, text="Use SSL?",
1666 variable=self.ssl).pack(side=TOP, fill=X)
1667 LabeledEntry(sslwin, 'SSL key:',
1668 self.sslkey, '14').pack(side=TOP, fill=X)
1669 LabeledEntry(sslwin, 'SSL certificate:',
1670 self.sslcert, '14').pack(side=TOP, fill=X)
1671 Checkbutton(sslwin, text="Check server SSL certificate?",
1672 variable=self.sslcertck).pack(side=TOP, fill=X)
1673 LabeledEntry(sslwin, 'SSL trusted certificate directory:',
1674 self.sslcertpath, '14').pack(side=TOP, fill=X)
1675 LabeledEntry(sslwin, 'SSL CommonName:',
1676 self.sslcommonname, '14').pack(side=TOP, fill=X)
1677 LabeledEntry(sslwin, 'SSL key fingerprint:',
1678 self.sslfingerprint, '14').pack(side=TOP, fill=X)
1679 sslwin.pack(fill=X, anchor=N)
1681 names = Frame(leftwin, relief=RAISED, bd=5)
1682 Label(names, text="Local names").pack(side=TOP)
1683 ListEdit("New name: ",
1684 self.user.localnames, None, None, names, localhelp)
1685 names.pack(fill=X, anchor=N)
1687 if mode != 'novice':
1688 targwin = Frame(leftwin, relief=RAISED, bd=5)
1689 Label(targwin, text="Forwarding Options").pack(side=TOP)
1690 Label(targwin, text="Listeners to forward to").pack(side=TOP)
1691 ListEdit("New listener:",
1692 self.user.smtphunt, None, None, targwin, None)
1693 Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
1694 ListEdit("Domains:",
1695 self.user.fetchdomains, None, None, targwin, None)
1696 LabeledEntry(targwin, 'Use domain on RCPT TO line:',
1697 self.smtpaddress, '26').pack(side=TOP, fill=X)
1698 LabeledEntry(targwin, 'Set fixed RCPT TO address:',
1699 self.smtpname, '26').pack(side=TOP, fill=X)
1700 LabeledEntry(targwin, 'Connection setup command:',
1701 self.preconnect, '26').pack(side=TOP, fill=X)
1702 LabeledEntry(targwin, 'Connection wrapup command:',
1703 self.postconnect, '26').pack(side=TOP, fill=X)
1704 LabeledEntry(targwin, 'Local delivery agent:',
1705 self.mda, '26').pack(side=TOP, fill=X)
1706 LabeledEntry(targwin, 'BSMTP output file:',
1707 self.bsmtp, '26').pack(side=TOP, fill=X)
1708 LabeledEntry(targwin, 'Listener spam-block codes:',
1709 self.antispam, '26').pack(side=TOP, fill=X)
1710 LabeledEntry(targwin, 'Pass-through properties:',
1711 self.properties, '26').pack(side=TOP, fill=X)
1712 Checkbutton(targwin, text="Use LMTP?",
1713 variable=self.lmtp).pack(side=TOP, fill=X)
1714 targwin.pack(fill=X, anchor=N)
1716 if mode != 'novice':
1717 leftwin.pack(side=LEFT, fill=X, anchor=N)
1718 rightwin = Frame(self)
1722 optwin = Frame(rightwin, relief=RAISED, bd=5)
1723 Label(optwin, text="Processing Options").pack(side=TOP)
1724 Checkbutton(optwin, text="Suppress deletion of messages after reading",
1725 variable=self.keep).pack(side=TOP, anchor=W)
1726 Checkbutton(optwin, text="Fetch old messages as well as new",
1727 variable=self.fetchall).pack(side=TOP, anchor=W)
1728 if mode != 'novice':
1729 Checkbutton(optwin, text="Flush seen messages before retrieval",
1730 variable=self.flush).pack(side=TOP, anchor=W)
1731 Checkbutton(optwin, text="Flush oversized messages before retrieval",
1732 variable=self.limitflush).pack(side=TOP, anchor=W)
1733 Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply",
1734 variable=self.rewrite).pack(side=TOP, anchor=W)
1735 Checkbutton(optwin, text="Force CR/LF at end of each line",
1736 variable=self.forcecr).pack(side=TOP, anchor=W)
1737 Checkbutton(optwin, text="Strip CR from end of each line",
1738 variable=self.stripcr).pack(side=TOP, anchor=W)
1739 Checkbutton(optwin, text="Pass 8 bits even though SMTP says 7BIT",
1740 variable=self.pass8bits).pack(side=TOP, anchor=W)
1741 Checkbutton(optwin, text="Undo MIME armoring on header and body",
1742 variable=self.mimedecode).pack(side=TOP, anchor=W)
1743 Checkbutton(optwin, text="Drop Status lines from forwarded messages",
1744 variable=self.dropstatus).pack(side=TOP, anchor=W)
1745 Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
1746 variable=self.dropdelivered).pack(side=TOP, anchor=W)
1749 if mode != 'novice':
1750 limwin = Frame(rightwin, relief=RAISED, bd=5)
1751 Label(limwin, text="Resource Limits").pack(side=TOP)
1752 LabeledEntry(limwin, 'Message size limit:',
1753 self.limit, '30').pack(side=TOP, fill=X)
1754 LabeledEntry(limwin, 'Size warning interval:',
1755 self.warnings, '30').pack(side=TOP, fill=X)
1756 LabeledEntry(limwin, 'Max messages to fetch per poll:',
1757 self.fetchlimit, '30').pack(side=TOP, fill=X)
1758 LabeledEntry(limwin, 'Max message sizes to fetch per transaction:',
1759 self.fetchsizelimit, '30').pack(side=TOP, fill=X)
1760 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1761 LabeledEntry(limwin, 'Use fast UIDL:',
1762 self.fastuidl, '30').pack(side=TOP, fill=X)
1763 LabeledEntry(limwin, 'Max messages to forward per poll:',
1764 self.batchlimit, '30').pack(side=TOP, fill=X)
1765 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1766 LabeledEntry(limwin, 'Interval between expunges:',
1767 self.expunge, '30').pack(side=TOP, fill=X)
1768 Checkbutton(limwin, text="Idle after each poll (IMAP only)",
1769 variable=self.idle).pack(side=TOP, anchor=W)
1772 if self.parent.server.protocol == 'IMAP':
1773 foldwin = Frame(rightwin, relief=RAISED, bd=5)
1774 Label(foldwin, text="Remote folders (IMAP only)").pack(side=TOP)
1775 ListEdit("New folder:", self.user.mailboxes,
1776 None, None, foldwin, None)
1777 foldwin.pack(fill=X, anchor=N)
1779 if mode != 'novice':
1780 rightwin.pack(side=LEFT)
1786 # Top-level window that offers either novice or expert mode
1787 # (but not both at once; it disappears when one is selected).
1790 class Configurator(Frame):
1791 def __init__(self, outfile, master, onexit, parent):
1792 Frame.__init__(self, master)
1793 self.outfile = outfile
1794 self.onexit = onexit
1795 self.parent = parent
1796 self.master.title('fetchmail configurator');
1797 self.master.iconname('fetchmail configurator');
1799 self.keepalive = [] # Use this to anchor the PhotoImage object
1800 make_icon_window(self, fetchmail_icon)
1802 Message(self, text="""
1803 Use `Novice Configuration' for basic fetchmail setup;
1804 with this, you can easily set up a single-drop connection
1805 to one remote mail server.
1806 """, width=600).pack(side=TOP)
1807 Button(self, text='Novice Configuration',
1808 fg='blue', command=self.novice).pack()
1810 Message(self, text="""
1811 Use `Expert Configuration' for advanced fetchmail setup,
1812 including multiple-site or multidrop connections.
1813 """, width=600).pack(side=TOP)
1814 Button(self, text='Expert Configuration',
1815 fg='blue', command=self.expert).pack()
1817 Message(self, text="""
1818 Or you can just select `Quit' to leave the configurator now and
1819 return to the main panel.
1820 """, width=600).pack(side=TOP)
1821 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1822 master.protocol("WM_DELETE_WINDOW", self.leave)
1825 self.master.destroy()
1826 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1829 self.master.destroy()
1830 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1833 self.master.destroy()
1836 # Run a command in a scrolling text widget, displaying its output
1838 class RunWindow(Frame):
1839 def __init__(self, command, master, parent):
1840 Frame.__init__(self, master)
1841 self.master = master
1842 self.master.title('fetchmail run window');
1843 self.master.iconname('fetchmail run window');
1846 text="Running "+command,
1847 bd=2).pack(side=TOP, pady=10)
1848 self.keepalive = [] # Use this to anchor the PhotoImage object
1849 make_icon_window(self, fetchmail_icon)
1851 # This is a scrolling text window
1852 textframe = Frame(self)
1853 scroll = Scrollbar(textframe)
1854 self.textwidget = Text(textframe, setgrid=TRUE)
1855 textframe.pack(side=TOP, expand=YES, fill=BOTH)
1856 self.textwidget.config(yscrollcommand=scroll.set)
1857 self.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
1858 scroll.config(command=self.textwidget.yview)
1859 scroll.pack(side=RIGHT, fill=BOTH)
1860 textframe.pack(side=TOP)
1862 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1864 self.update() # Draw widget before executing fetchmail
1866 # Always look for a runnable command in the directory we're running in
1867 # first. This avoids some obscure version-skew errors that can occur
1868 # if you pick up an old fetchmail from the standard system locations.
1869 os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"]
1870 child_stdout = os.popen(command + " 2>&1 </dev/null", "r")
1872 ch = child_stdout.read(1)
1875 self.textwidget.insert(END, ch)
1876 self.textwidget.insert(END, "Done.")
1877 self.textwidget.see(END);
1880 self.master.destroy()
1882 # Here's where we choose either configuration or launching
1884 class MainWindow(Frame):
1885 def __init__(self, outfile, master=None):
1886 Frame.__init__(self, master)
1887 self.outfile = outfile
1888 self.master.title('fetchmail launcher');
1889 self.master.iconname('fetchmail launcher');
1892 text='Fetchmailconf ' + version,
1893 bd=2).pack(side=TOP, pady=10)
1894 self.keepalive = [] # Use this to anchor the PhotoImage object
1895 make_icon_window(self, fetchmail_icon)
1898 ## Test icon display with the following:
1899 # icon_image = PhotoImage(data=fetchmail_icon)
1900 # Label(self, image=icon_image).pack(side=TOP, pady=10)
1901 # self.keepalive.append(icon_image)
1903 Message(self, text="""
1904 Use `Configure fetchmail' to tell fetchmail about the remote
1905 servers it should poll (the host name, your username there,
1906 whether to use POP or IMAP, and so forth).
1907 """, width=600).pack(side=TOP)
1908 self.configbutton = Button(self, text='Configure fetchmail',
1909 fg='blue', command=self.configure)
1910 self.configbutton.pack()
1912 Message(self, text="""
1913 Use `Run fetchmail' to run fetchmail with debugging enabled.
1914 This is a good way to test out a new configuration.
1915 """, width=600).pack(side=TOP)
1916 Button(self, text='Run fetchmail',fg='blue', command=self.test).pack()
1918 Message(self, text="""
1919 Use `Run fetchmail' to run fetchmail in foreground.
1920 Progress messages will be shown, but not debug messages.
1921 """, width=600).pack(side=TOP)
1922 Button(self, text='Run fetchmail', fg='blue', command=self.run).pack()
1924 Message(self, text="""
1925 Or you can just select `Quit' to exit the launcher now.
1926 """, width=600).pack(side=TOP)
1927 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1929 def configure(self):
1930 self.configbutton.configure(state=DISABLED)
1931 Configurator(self.outfile, Toplevel(),
1932 lambda self=self: self.configbutton.configure(state=NORMAL),
1935 cmd = "fetchmail -N -d0 --nosyslog -v"
1937 cmd = cmd + " -f " + rcfile
1938 RunWindow(cmd, Toplevel(), self)
1941 cmd = "fetchmail -N -d0"
1943 cmd = cmd + " -f " + rcfile
1944 RunWindow(cmd, Toplevel(), self)
1949 # Functions for turning a dictionary into an instantiated object tree.
1951 def intersect(list1, list2):
1952 # Compute set intersection of lists
1959 def setdiff(list1, list2):
1960 # Compute set difference of lists
1967 def copy_instance(toclass, fromdict):
1968 # Initialize a class object of given type from a conformant dictionary.
1969 for fld in fromdict.keys():
1970 if not fld in dictmembers:
1971 dictmembers.append(fld)
1972 # The `optional' fields are the ones we can ignore for purposes of
1973 # conformability checking; they'll still get copied if they are
1974 # present in the dictionary.
1975 optional = ('interface', 'monitor',
1976 'esmtpname', 'esmtppassword',
1977 'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
1978 'sslcertpath', 'sslcommonname', 'sslfingerprint', 'showdots')
1979 class_sig = setdiff(toclass.__dict__.keys(), optional)
1981 dict_keys = setdiff(fromdict.keys(), optional)
1983 common = intersect(class_sig, dict_keys)
1984 if 'typemap' in class_sig:
1985 class_sig.remove('typemap')
1986 if tuple(class_sig) != tuple(dict_keys):
1987 print "Fields don't match what fetchmailconf expected:"
1988 # print "Class signature: " + `class_sig`
1989 # print "Dictionary keys: " + `dict_keys`
1990 diff = setdiff(class_sig, common)
1992 print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
1993 diff = setdiff(dict_keys, common)
1995 print "Not matched in dictionary keys: " + `diff`
1998 for x in fromdict.keys():
1999 setattr(toclass, x, fromdict[x])
2002 # And this is the main sequence. How it works:
2004 # First, call `fetchmail --configdump' and trap the output in a tempfile.
2005 # This should fill it with a Python initializer for a variable `fetchmailrc'.
2006 # Run execfile on the file to pull fetchmailrc into Python global space.
2007 # You don't want static data, though; you want, instead, a tree of objects
2008 # with the same data members and added appropriate methods.
2010 # This is what the copy_instance function() is for. It tries to copy a
2011 # dictionary field by field into a class, aborting if the class and dictionary
2012 # have different data members (except for any typemap member in the class;
2013 # that one is strictly for use by the MyWidget supperclass).
2015 # Once the object tree is set up, require user to choose novice or expert
2016 # mode and instantiate an edit object for the configuration. Class methods
2017 # will take it all from there.
2019 # Options (not documented because they're for fetchmailconf debuggers only):
2020 # -d: Read the configuration and dump it to stdout before editing. Dump
2021 # the edited result to stdout as well.
2022 # -f: specify the run control file to read.
2024 if __name__ == '__main__':
2026 if not os.environ.has_key("DISPLAY"):
2027 print "fetchmailconf must be run under X"
2030 fetchmail_icon = """
2031 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
2032 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
2033 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
2034 CJR7Y/+UMdbOxnNrY97Ove/Wvd7GrZyEa961jL2Ua9alc86ca7WEUntSKcaMSqVjGNZ7GGM5CNa1
2035 jPfOnN6tc3taMffeve/WtWtaQv/OjGtSMYRzWv/erda1hM6te7WUY62MWs61jP/vzv/ntda9jL2l
2036 czEhAO/n1oyEc//elDEpGEo5EOfexpyUe+/epefevffvxnNrQpyUStbWzsbGvZyclN7ezmNjWv//
2037 5/f33qWllNbWve/vzv//1ufnve/vvf//xvf3vefnrf//taWlc0pKMf//pbW1Y///jKWlWq2tWsbG
2038 Y///c97eUvf3Ut7nc+/3a87We8bOjOfv1u/37/f//621tb3Gxtbn52Nra87n53uUlJTv/6W9xuf3
2039 /8bW3iExOXu11tbv/5TW/4TO/63e/zmt/1KUxlK1/2u9/wCM/73GzrXG1gBKjACE/87e72NzhCkx
2040 OaXO92OMtUql/xCE/wApUtbe57W9xnN7hHut52Ot/xBSnABKnABavQB7/2ul7zF71gBr77XO73Oc
2041 1lqc9yFSlBApSimE/wAYOQApY0J7zlKM5wAxhABS1gBj/6W95wAhWgA5nAAYSgBS7wBS/wBK9wAp
2042 jABC5wBK/wApnABC/wApxgAhtYSMtQAQYwAp/3OE74SMxgAYxlpjvWNr70pS/wgQ3sbGzs7O1qWl
2043 3qWl70pKe0JC/yEhlCkp/wgI/wAAEAAAIQAAKQAAOQAASgAAUgAAYwAAawAAlAAAnAAApQAArQAA
2044 zgAA1gAA5wAA9wAA/0pC/xgQ52Na9ykhe4R7zikhYxgQSjEpQgAAACwAAAAAPAAoAAAI/wABCBxI
2045 sKDBgwgTKiRIYKHDhxARIvgXsaLFhGgEUBSYoKPHjyBDihxJkuS/kwNLqlzJcuTJjQBaypxpEiVH
2046 mjhxvkyZs2fLnTd9ehxAtKjRo0ZrwhTasUsENhYHKOUpk1E3j11mxCBiQVLEBlJd2owp9iVRjwUs
2047 zMCQ5IcLD4saPVxjIKxIoGTvvqSoyFEFGTBeqEhyxAoSFR/USGKVcEGBAwDshsSr1OYTEyhQpJiS
2048 ZcoUKWOQtJDRJFSaggzUGBgoGSTlsjahlPCRIkWVKT16THHRIoqIISBIEUgAYIGBhgRbf3ytFygU
2049 FZp9UDmxQkkMCRwyZKDBQy4aApABhP8XqNwj88l7BVpQYZtF5iArWgwAgGZBq24HU7OeGhQ90PVA
2050 aKZZCiiUMJ9ArSTEwGqR8ZeXfzbV0MIIMQTBwoUdxDDfAm8sZFyDZVEF4UYSKBEBD0+k6IEFPMxH
2051 3FzldXSea+kBgANJSOWIlIMhXZXAXv+c1WM3PuJEpH8iuhbAkv+MdENPRHaTRkdF/jiWSKCAwlKW
2052 VbbkY5Q0LgUSKExgoYBKCjCxARpdltQNKHaUoYAddnR53lVRnJLKBWh4RIEGCZx5FSOv1OLNDUVe
2053 deZHaWiZAB35fIOGNtbEUeV5oGAByzPOrBPFGt3kwEgxITACSg5oLGGLMg60oQAjaNz/oAAcN4Ai
2054 a0c3kHFDK3jYsw4g9sRzBgPLXdkRrBrQ8gsWQUxCCRZX9IJNBQ1s8IgCdeBCzBYN6IBIN2TUsQYd
2055 dXhDBxdzlAHOHHKEcocZdWwDjx8MTCmjsR2FMAstw1RyiSzHqPLALaOwk8QmzCzDCSi0xJKMMk4E
2056 Yw8389iTDT32GAKOPf7YY0Aa9tATyD3w/EGsefgmgEYUtPiChLKWQDMBJtEUgYkzH2RiTgGfTMCI
2057 Mlu0Yc85hNiDziH2tMqOGL72QY47gshLb7Fi4roELcjoQIsxWpDwQyfS2OCJMkLI4YUmyhgxSTVg
2058 CP2FHPZ80UDcieBjStNPD5LPOyZT/y0iHGiMwswexDSzRiRq6KIMJBc4M8skwKAyChia2KPH3P24
2059 YU8/lFhOTj152OPOHuXMU4g48vCRiN/9rZGLMdS4csUu1JzDgxuipOMDHMKsAwEnq/ByzTrrZMNO
2060 OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
2061 xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
2062 gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
2064 # The base64 data in the string above was generated by the following procedure:
2067 # print base64.encodestring(open("fetchmail.gif", "rb").read())
2071 (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV", ["help",
2073 dump = rcfile = None;
2074 for (switch, val) in options:
2075 if (switch == '-d'):
2077 elif (switch == '-f'):
2079 elif (switch == '-h' or switch == '--help'):
2081 Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|--help|-V|--version}
2082 -d - dump configuration (for debugging)
2083 -f fmrc - read alternate fetchmailrc file
2084 --help, -h - print this help text and quit
2085 --version, -V - print fetchmailconf version and quit
2088 elif (switch == '-V' or switch == '--version'):
2089 print "fetchmailconf %s" % version
2091 Copyright (C) 1997 - 2003 Eric S. Raymond
2092 Copyright (C) 2005, 2006, 2008, 2009 Matthias Andree
2093 fetchmailconf comes with ABSOLUTELY NO WARRANTY. This is free software, you are
2094 welcome to redistribute it under certain conditions. Please see the file
2095 COPYING in the source or documentation directory for details."""
2098 # Get client host's FQDN
2099 hostname = socket.gethostbyaddr(socket.gethostname())[0]
2102 ConfigurationDefaults = Configuration()
2103 ServerDefaults = Server()
2104 UserDefaults = User()
2106 # Read the existing configuration. We set the umask to 077 to make sure
2107 # that group & other read/write permissions are shut off -- we wouldn't
2108 # want crackers to snoop password information out of the tempfile.
2109 tmpfile = tempfile.mktemp()
2111 cmd = "umask 077 && fetchmail </dev/null -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2113 cmd = "umask 077 && fetchmail </dev/null --configdump --nosyslog >" + tmpfile
2118 print "`" + cmd + "' run failure, status " + `s`
2121 print "Unknown error while running fetchmail --configdump"
2128 print "Can't read configuration output of fetchmail --configdump."
2134 # The tricky part -- initializing objects from the configuration global
2135 # `Configuration' is the top level of the object tree we're going to mung.
2136 # The dictmembers list is used to track the set of fields the dictionary
2137 # contains; in particular, we can use it to tell whether things like the
2138 # monitor, interface, ssl, sslkey, or sslcert fields are present.
2140 Fetchmailrc = Configuration()
2141 copy_instance(Fetchmailrc, fetchmailrc)
2142 Fetchmailrc.servers = [];
2143 for server in fetchmailrc['servers']:
2145 copy_instance(Newsite, server)
2146 Fetchmailrc.servers.append(Newsite)
2148 for user in server['users']:
2150 copy_instance(Newuser, user)
2151 Newsite.users.append(Newuser)
2153 # We may want to display the configuration and quit
2155 print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2157 # The theory here is that -f alone sets the rcfile location,
2158 # but -d and -f together mean the new configuration should go to stdout.
2159 if not rcfile and not dump:
2160 rcfile = os.environ["HOME"] + "/.fetchmailrc"
2162 # OK, now run the configuration edit
2163 root = MainWindow(rcfile)
2166 # The following sets edit modes for GNU EMACS