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"
68 for site in self.servers:
69 str = str + repr(site)
72 def __delitem__(self, name):
73 for si in range(len(self.servers)):
74 if self.servers[si].pollname == name:
79 return "[Configuration: " + repr(self) + "]"
83 self.pollname = None # Poll label
84 self.via = None # True name of host
85 self.active = TRUE # Poll status
86 self.interval = 0 # Skip interval
87 self.protocol = 'auto' # Default to auto protocol
88 self.service = None # Service name to use
89 self.uidl = FALSE # Don't use RFC1725 UIDLs by default
90 self.auth = 'any' # Default to password authentication
91 self.timeout = 300 # 5-minute timeout
92 self.envelope = 'Received' # Envelope-address header
93 self.envskip = 0 # Number of envelope headers to skip
94 self.qvirtual = None # Name prefix to strip
95 self.aka = [] # List of DNS aka names
96 self.dns = TRUE # Enable DNS lookup on multidrop
97 self.localdomains = [] # Domains to be considered local
98 self.interface = None # IP address and range
99 self.monitor = None # IP address and range
100 self.plugin = None # Plugin command for going to server
101 self.plugout = None # Plugin command for going to listener
102 self.principal = None # Kerberos principal
103 self.esmtpname = None # ESMTP 2554 name
104 self.esmtppassword = None # ESMTP 2554 password
105 self.tracepolls = FALSE # Add trace-poll info to headers
106 self.badheader = FALSE # Pass messages with bad headers on?
107 self.users = [] # List of user entries for site
109 ('pollname', 'String'),
111 ('active', 'Boolean'),
113 ('protocol', 'String'),
114 ('service', 'String'),
118 ('envelope', 'String'),
120 ('qvirtual', 'String'),
123 # leave localdomains out
124 ('interface', 'String'),
125 ('monitor', 'String'),
126 ('plugin', 'String'),
127 ('plugout', 'String'),
128 ('esmtpname', 'String'),
129 ('esmtppassword', 'String'),
130 ('principal', 'String'),
131 ('tracepolls','Boolean'),
132 ('badheader', 'Boolean'))
134 def dump(self, folded):
136 if self.active: res = res + "poll"
137 else: res = res + "skip"
138 res = res + (" " + self.pollname)
140 res = res + (" via " + str(self.via) + "\n");
141 if self.protocol != ServerDefaults.protocol:
142 res = res + " with proto " + self.protocol
143 if self.service and self.protocol and self.service != defaultports[self.protocol] and defaultports[self.protocol] and self.service != ianaservices[defaultports[self.protocol]]:
144 res = res + " service " + self.service
145 if self.timeout != ServerDefaults.timeout:
146 res = res + " timeout " + `self.timeout`
147 if self.interval != ServerDefaults.interval:
148 res = res + " interval " + `self.interval`
149 if self.envelope != ServerDefaults.envelope or self.envskip != ServerDefaults.envskip:
151 res = res + " envelope " + `self.envskip` + " " + self.envelope
153 res = res + " envelope " + self.envelope
155 res = res + (" qvirtual " + str(self.qvirtual) + "\n");
156 if self.auth != ServerDefaults.auth:
157 res = res + " auth " + self.auth
158 if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl:
159 res = res + " and options"
160 if self.dns != ServerDefaults.dns:
161 res = res + flag2str(self.dns, 'dns')
162 if self.uidl != ServerDefaults.uidl:
163 res = res + flag2str(self.uidl, 'uidl')
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:
203 res = res + "bad-header pass "
205 if res[-1] == " ": res = res[0:-1]
207 for user in self.users:
208 res = res + repr(user)
212 def __delitem__(self, name):
213 for ui in range(len(self.users)):
214 if self.users[ui].remote == name:
219 return self.dump(TRUE)
222 return "[Server: " + self.dump(FALSE) + "]"
226 if os.environ.has_key("USER"):
227 self.remote = os.environ["USER"] # Remote username
228 elif os.environ.has_key("LOGNAME"):
229 self.remote = os.environ["LOGNAME"]
231 print "Can't get your username!"
233 self.localnames = [self.remote,]# Local names
234 self.password = None # Password for mail account access
235 self.mailboxes = [] # Remote folders to retrieve from
236 self.smtphunt = [] # Hosts to forward to
237 self.fetchdomains = [] # Domains to fetch from
238 self.smtpaddress = None # Append this to MAIL FROM line
239 self.smtpname = None # Use this for RCPT TO
240 self.preconnect = None # Connection setup
241 self.postconnect = None # Connection wrapup
242 self.mda = None # Mail Delivery Agent
243 self.bsmtp = None # BSMTP output file
244 self.lmtp = FALSE # Use LMTP rather than SMTP?
245 self.antispam = "" # Listener's spam-block code
246 self.keep = FALSE # Keep messages
247 self.flush = FALSE # Flush messages
248 self.limitflush = FALSE # Flush oversized messages
249 self.fetchall = FALSE # Fetch old messages
250 self.rewrite = TRUE # Rewrite message headers
251 self.forcecr = FALSE # Force LF -> CR/LF
252 self.stripcr = FALSE # Strip CR
253 self.pass8bits = FALSE # Force BODY=7BIT
254 self.mimedecode = FALSE # Undo MIME armoring
255 self.dropstatus = FALSE # Drop incoming Status lines
256 self.dropdelivered = FALSE # Drop incoming Delivered-To lines
257 self.idle = FALSE # IDLE after poll
258 self.limit = 0 # Message size limit
259 self.warnings = 3600 # Size warning interval (see tunable.h)
260 self.fetchlimit = 0 # Max messages fetched per batch
261 self.fetchsizelimit = 100 # Max message sizes fetched per transaction
262 self.fastuidl = 4 # Do fast uidl 3 out of 4 times
263 self.batchlimit = 0 # Max message forwarded per batch
264 self.expunge = 0 # Interval between expunges (IMAP)
265 self.ssl = 0 # Enable Seccure Socket Layer
266 self.sslkey = None # SSL key filename
267 self.sslcert = None # SSL certificate filename
268 self.sslproto = None # Force SSL?
269 self.sslcertck = 0 # Enable strict SSL cert checking
270 self.sslcertpath = None # Path to trusted certificates
271 self.sslcommonname = None # SSL CommonName to expect
272 self.sslfingerprint = None # SSL key fingerprint to check
273 self.properties = None # Extension properties
275 ('remote', 'String'),
276 # leave out mailboxes and localnames
277 ('password', 'String'),
278 # Leave out smtphunt, fetchdomains
279 ('smtpaddress', 'String'),
280 ('smtpname', 'String'),
281 ('preconnect', 'String'),
282 ('postconnect', 'String'),
286 ('antispam', 'String'),
288 ('flush', 'Boolean'),
289 ('limitflush', 'Boolean'),
290 ('fetchall', 'Boolean'),
291 ('rewrite', 'Boolean'),
292 ('forcecr', 'Boolean'),
293 ('stripcr', 'Boolean'),
294 ('pass8bits', 'Boolean'),
295 ('mimedecode', 'Boolean'),
296 ('dropstatus', 'Boolean'),
297 ('dropdelivered', 'Boolean'),
301 ('fetchlimit', 'Int'),
302 ('fetchsizelimit', 'Int'),
304 ('batchlimit', 'Int'),
307 ('sslkey', 'String'),
308 ('sslcert', 'String'),
309 ('sslcertck', 'Boolean'),
310 ('sslcertpath', 'String'),
311 ('sslcommonname', 'String'),
312 ('sslfingerprint', 'String'),
313 ('properties', 'String'))
317 res = res + "user " + `self.remote` + " there ";
319 res = res + "with password " + `self.password` + " "
322 for x in self.localnames:
323 res = res + " " + `x`
325 if (self.keep != UserDefaults.keep
326 or self.flush != UserDefaults.flush
327 or self.limitflush != UserDefaults.limitflush
328 or self.fetchall != UserDefaults.fetchall
329 or self.rewrite != UserDefaults.rewrite
330 or self.forcecr != UserDefaults.forcecr
331 or self.stripcr != UserDefaults.stripcr
332 or self.pass8bits != UserDefaults.pass8bits
333 or self.mimedecode != UserDefaults.mimedecode
334 or self.dropstatus != UserDefaults.dropstatus
335 or self.dropdelivered != UserDefaults.dropdelivered
336 or self.idle != UserDefaults.idle):
337 res = res + " options"
338 if self.keep != UserDefaults.keep:
339 res = res + flag2str(self.keep, 'keep')
340 if self.flush != UserDefaults.flush:
341 res = res + flag2str(self.flush, 'flush')
342 if self.limitflush != UserDefaults.limitflush:
343 res = res + flag2str(self.limitflush, 'limitflush')
344 if self.fetchall != UserDefaults.fetchall:
345 res = res + flag2str(self.fetchall, 'fetchall')
346 if self.rewrite != UserDefaults.rewrite:
347 res = res + flag2str(self.rewrite, 'rewrite')
348 if self.forcecr != UserDefaults.forcecr:
349 res = res + flag2str(self.forcecr, 'forcecr')
350 if self.stripcr != UserDefaults.stripcr:
351 res = res + flag2str(self.stripcr, 'stripcr')
352 if self.pass8bits != UserDefaults.pass8bits:
353 res = res + flag2str(self.pass8bits, 'pass8bits')
354 if self.mimedecode != UserDefaults.mimedecode:
355 res = res + flag2str(self.mimedecode, 'mimedecode')
356 if self.dropstatus != UserDefaults.dropstatus:
357 res = res + flag2str(self.dropstatus, 'dropstatus')
358 if self.dropdelivered != UserDefaults.dropdelivered:
359 res = res + flag2str(self.dropdelivered, 'dropdelivered')
360 if self.idle != UserDefaults.idle:
361 res = res + flag2str(self.idle, 'idle')
362 if self.limit != UserDefaults.limit:
363 res = res + " limit " + `self.limit`
364 if self.warnings != UserDefaults.warnings:
365 res = res + " warnings " + `self.warnings`
366 if self.fetchlimit != UserDefaults.fetchlimit:
367 res = res + " fetchlimit " + `self.fetchlimit`
368 if self.fetchsizelimit != UserDefaults.fetchsizelimit:
369 res = res + " fetchsizelimit " + `self.fetchsizelimit`
370 if self.fastuidl != UserDefaults.fastuidl:
371 res = res + " fastuidl " + `self.fastuidl`
372 if self.batchlimit != UserDefaults.batchlimit:
373 res = res + " batchlimit " + `self.batchlimit`
374 if self.ssl and self.ssl != UserDefaults.ssl:
375 res = res + flag2str(self.ssl, 'ssl')
376 if self.sslkey and self.sslkey != UserDefaults.sslkey:
377 res = res + " sslkey " + `self.sslkey`
378 if self.sslcert and self.sslcert != UserDefaults.sslcert:
379 res = res + " sslcert " + `self.sslcert`
380 if self.sslproto and self.sslproto != UserDefaults.sslproto:
381 res = res + " sslproto " + `self.sslproto`
382 if self.sslcertck and self.sslcertck != UserDefaults.sslcertck:
383 res = res + flag2str(self.sslcertck, 'sslcertck')
384 if self.sslcertpath and self.sslcertpath != UserDefaults.sslcertpath:
385 res = res + " sslcertpath " + `self.sslcertpath`
386 if self.sslcommonname and self.sslcommonname != UserDefaults.sslcommonname:
387 res = res + " sslcommonname " + `self.sslcommonname`
388 if self.sslfingerprint and self.sslfingerprint != UserDefaults.sslfingerprint:
389 res = res + " sslfingerprint " + `self.sslfingerprint`
390 if self.expunge != UserDefaults.expunge:
391 res = res + " expunge " + `self.expunge`
393 trimmed = self.smtphunt;
394 if trimmed != [] and trimmed[len(trimmed) - 1] == "localhost":
395 trimmed = trimmed[0:len(trimmed) - 1]
396 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
397 trimmed = trimmed[0:len(trimmed) - 1]
399 res = res + " smtphost "
403 trimmed = self.fetchdomains
404 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
405 trimmed = trimmed[0:len(trimmed) - 1]
407 res = res + " fetchdomains "
412 res = res + " folder"
413 for x in self.mailboxes:
414 res = res + ' "%s"' % x
416 for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'):
417 if getattr(self, fld):
418 res = res + " %s %s\n" % (fld, `getattr(self, fld)`)
419 if self.lmtp != UserDefaults.lmtp:
420 res = res + flag2str(self.lmtp, 'lmtp')
421 if self.antispam != UserDefaults.antispam:
422 res = res + " antispam " + self.antispam + "\n"
426 return "[User: " + repr(self) + "]"
432 # IANA port assignments and bogus 1109 entry
433 ianaservices = {"pop2":109,
440 # fetchmail protocol to IANA service name
441 defaultports = {"auto":None,
450 authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp",
454 'title' : 'List Selection Help',
455 'banner': 'List Selection',
457 You must select an item in the list box (by clicking on it).
460 def flag2str(value, string):
461 # make a string representation of a .fetchmailrc flag or negated flag
465 if value == FALSE: str = str + ("no ")
469 class LabeledEntry(Frame):
470 # widget consisting of entry field with caption to left
471 def bind(self, key, action):
472 self.E.bind(key, action)
475 def __init__(self, Master, text, textvar, lwidth, ewidth=12):
476 Frame.__init__(self, Master)
477 self.L = Label(self, {'text':text, 'width':lwidth, 'anchor':'w'})
478 self.E = Entry(self, {'textvar':textvar, 'width':ewidth})
479 self.L.pack({'side':'left'})
480 self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
482 def ButtonBar(frame, legend, ref, alternatives, depth, command):
483 # array of radio buttons, caption to left, picking from a string list
485 width = (len(alternatives)+1) / depth;
486 Label(bar, text=legend).pack(side=LEFT)
487 for column in range(width):
488 subframe = Frame(bar)
489 for row in range(depth):
490 ind = width * row + column
491 if ind < len(alternatives):
492 Radiobutton(subframe,
493 {'text':alternatives[ind],
495 'value':alternatives[ind],
496 'command':command}).pack(side=TOP, anchor=W)
498 # This is just a spacer
499 Radiobutton(subframe,
500 {'text':" ",'state':DISABLED}).pack(side=TOP, anchor=W)
501 subframe.pack(side=LEFT)
505 def helpwin(helpdict):
506 # help message window with a self-destruct button
508 helpwin.title(helpdict['title'])
509 helpwin.iconname(helpdict['title'])
510 Label(helpwin, text=helpdict['banner']).pack()
511 textframe = Frame(helpwin)
512 scroll = Scrollbar(textframe)
513 helpwin.textwidget = Text(textframe, setgrid=TRUE)
514 textframe.pack(side=TOP, expand=YES, fill=BOTH)
515 helpwin.textwidget.config(yscrollcommand=scroll.set)
516 helpwin.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
517 scroll.config(command=helpwin.textwidget.yview)
518 scroll.pack(side=RIGHT, fill=BOTH)
519 helpwin.textwidget.insert(END, helpdict['text']);
520 Button(helpwin, text='Done',
521 command=lambda x=helpwin: x.destroy(), bd=2).pack()
522 textframe.pack(side=TOP)
524 def make_icon_window(base, image):
526 # Some older pythons will error out on this
527 icon_image = PhotoImage(data=image)
528 icon_window = Toplevel()
529 Label(icon_window, image=icon_image, bg='black').pack()
530 base.master.iconwindow(icon_window)
531 # Avoid TkInter brain death. PhotoImage objects go out of
532 # scope when the enclosing function returns. Therefore
533 # we have to explicitly link them to something.
534 base.keepalive.append(icon_image)
538 class ListEdit(Frame):
539 # edit a list of values (duplicates not allowed) with a supplied editor hook
540 def __init__(self, newlegend, list, editor, deletor, master, helptxt):
542 self.deletor = deletor
545 # Set up a widget to accept new elements
546 self.newval = StringVar(master)
547 newwin = LabeledEntry(master, newlegend, self.newval, '12')
548 newwin.bind('<Double-1>', self.handleNew)
549 newwin.bind('<Return>', self.handleNew)
550 newwin.pack(side=TOP, fill=X, anchor=E)
552 # Edit the existing list
553 listframe = Frame(master)
554 scroll = Scrollbar(listframe)
555 self.listwidget = Listbox(listframe, height=0, selectmode='browse')
558 self.listwidget.insert(END, x)
559 listframe.pack(side=TOP, expand=YES, fill=BOTH)
560 self.listwidget.config(yscrollcommand=scroll.set)
561 self.listwidget.pack(side=LEFT, expand=YES, fill=BOTH)
562 scroll.config(command=self.listwidget.yview)
563 scroll.pack(side=RIGHT, fill=BOTH)
564 self.listwidget.config(selectmode=SINGLE, setgrid=TRUE)
565 self.listwidget.bind('<Double-1>', self.handleList);
566 self.listwidget.bind('<Return>', self.handleList);
570 Button(bf, text='Edit', command=self.editItem).pack(side=LEFT)
571 Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT)
573 self.helptxt = helptxt
574 Button(bf, text='Help', fg='blue',
575 command=self.help).pack(side=RIGHT)
579 helpwin(self.helptxt)
581 def handleList(self, event):
584 def handleNew(self, event):
585 item = self.newval.get()
587 entire = self.listwidget.get(0, self.listwidget.index('end'));
588 if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
589 self.listwidget.insert('end', item)
590 if self.list != None: self.list.append(item)
592 apply(self.editor, (item,))
596 select = self.listwidget.curselection()
601 if index and self.editor:
602 label = self.listwidget.get(index);
604 apply(self.editor, (label,))
606 def deleteItem(self):
607 select = self.listwidget.curselection()
611 index = string.atoi(select[0])
612 label = self.listwidget.get(index);
613 self.listwidget.delete(index)
614 if self.list != None:
616 if self.deletor != None:
617 apply(self.deletor, (label,))
619 def ConfirmQuit(frame, context):
622 text = 'Really quit ' + context + ' without saving?',
624 strings = ('Yes', 'No'),
628 def dispose_window(master, legend, help, savelegend='OK'):
629 dispose = Frame(master, relief=RAISED, bd=5)
630 Label(dispose, text=legend).pack(side=TOP,pady=10)
631 Button(dispose, text=savelegend, fg='blue',
632 command=master.save).pack(side=LEFT)
633 Button(dispose, text='Quit', fg='blue',
634 command=master.nosave).pack(side=LEFT)
635 Button(dispose, text='Help', fg='blue',
636 command=lambda x=help: helpwin(x)).pack(side=RIGHT)
641 # Common methods for Tkinter widgets -- deals with Tkinter declaration
642 def post(self, widgetclass, field):
643 for x in widgetclass.typemap:
644 if x[1] == 'Boolean':
645 setattr(self, x[0], BooleanVar(self))
646 elif x[1] == 'String':
647 setattr(self, x[0], StringVar(self))
649 setattr(self, x[0], IntVar(self))
650 source = getattr(getattr(self, field), x[0])
652 getattr(self, x[0]).set(source)
654 def fetch(self, widgetclass, field):
655 for x in widgetclass.typemap:
656 setattr(getattr(self, field), x[0], getattr(self, x[0]).get())
659 # First, code to set the global fetchmail run controls.
662 configure_novice_help = {
663 'title' : 'Fetchmail novice configurator help',
664 'banner': 'Novice configurator help',
666 In the `Novice Configurator Controls' panel, you can:
668 Press `Save' to save the new fetchmail configuration you have created.
669 Press `Quit' to exit without saving.
670 Press `Help' to bring up this help message.
672 In the `Novice Configuration' panels, you will set up the basic data
673 needed to create a simple fetchmail setup. These include:
675 1. The name of the remote site you want to query.
677 2. Your login name on that site.
679 3. Your password on that site.
681 4. A protocol to use (POP, IMAP, ETRN, etc.)
683 5. A poll interval in seconds.
684 If 0, fetchmail will run in the foreground once when started.
685 If > 0, fetchmail will run in the background and start a new poll
686 cycle after the interval has elapsed.
688 6. Options to fetch old messages as well as new, or to suppress
689 deletion of fetched message.
691 The novice-configuration code will assume that you want to forward mail
692 to a local sendmail listener with no special options.
695 configure_expert_help = {
696 'title' : 'Fetchmail expert configurator help',
697 'banner': 'Expert configurator help',
699 In the `Expert Configurator Controls' panel, you can:
701 Press `Save' to save the new fetchmail configuration you have edited.
702 Press `Quit' to exit without saving.
703 Press `Help' to bring up this help message.
705 In the `Run Controls' panel, you can set the following options that
706 control how fetchmail runs:
709 Number of seconds to wait between polls in the background.
710 If zero, fetchmail will run in foreground.
713 If empty, emit progress and error messages to stderr.
714 Otherwise this gives the name of the files to write to.
715 This field is ignored if the "Log to syslog?" option is on.
718 If empty, store seen-message IDs in .fetchids under user's home
719 directory. If nonempty, use given file name.
722 Who to send multidrop mail to as a last resort if no address can
723 be matched. Normally empty; in this case, fetchmail treats the
724 invoking user as the address of last resort unless that user is
725 root. If that user is root, fetchmail sends to `postmaster'.
728 If this option is on (the default) error mail goes to the sender.
729 Otherwise it goes to the postmaster.
732 If this option is on, spam bounces are sent to the sender or
733 postmaster (depending on the "Bounces to sender?" option. Otherwise,
734 spam bounces are not sent (the default).
737 If this option is on, permanent delivery errors are treated as
738 temporary, i. e. mail is kept on the upstream server. Useful
739 during testing and after configuration changes, and on by
741 If this option is off, permanent delivery errors delete
742 undeliverable mail from the upstream.
745 If false (the default) fetchmail generates a Received line into
746 each message and generates a HELO from the machine it is running on.
747 If true, fetchmail generates no Received line and HELOs as if it were
750 In the `Remote Mail Configurations' panel, you can:
752 1. Enter the name of a new remote mail server you want fetchmail to query.
754 To do this, simply enter a label for the poll configuration in the
755 `New Server:' box. The label should be a DNS name of the server (unless
756 you are using ssh or some other tunneling method and will fill in the `via'
757 option on the site configuration screen).
759 2. Change the configuration of an existing site.
761 To do this, find the site's label in the listbox and double-click it.
762 This will take you to a site configuration dialogue.
766 class ConfigurationEdit(Frame, MyWidget):
767 def __init__(self, configuration, outfile, master, onexit):
769 self.configuration = configuration
770 self.outfile = outfile
771 self.container = master
773 ConfigurationEdit.mode_to_help = {
774 'novice':configure_novice_help, 'expert':configure_expert_help
777 def server_edit(self, sitename):
778 self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
780 def server_delete(self, sitename):
782 for user in self.subwidgets.keys():
784 del self.configuration[sitename]
788 def edit(self, mode):
790 Frame.__init__(self, self.container)
791 self.master.title('fetchmail ' + self.mode + ' configurator');
792 self.master.iconname('fetchmail ' + self.mode + ' configurator');
793 self.master.protocol('WM_DELETE_WINDOW', self.nosave)
794 self.keepalive = [] # Use this to anchor the PhotoImage object
795 make_icon_window(self, fetchmail_icon)
797 self.post(Configuration, 'configuration')
800 'Configurator ' + self.mode + ' Controls',
801 ConfigurationEdit.mode_to_help[self.mode],
804 gf = Frame(self, relief=RAISED, bd = 5)
806 text='Fetchmail Run Controls',
807 bd=2).pack(side=TOP, pady=10)
812 if self.mode != 'novice':
814 log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
815 log.pack(side=RIGHT, anchor=E)
817 # Set the poll interval
818 de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
819 de.pack(side=RIGHT, anchor=E)
824 if self.mode != 'novice':
827 {'text':'Bounces to sender?',
828 'variable':self.bouncemail,
829 'relief':GROOVE}).pack(side=LEFT, anchor=W)
834 {'text':'Send spam bounces?',
835 'variable':self.spambounce,
836 'relief':GROOVE}).pack(side=LEFT, anchor=W)
841 {'text':'Treat permanent errors as temporary?',
842 'variable':self.softbounce,
843 'relief':GROOVE}).pack(side=LEFT, anchor=W)
848 {'text':'Log to syslog?',
849 'variable':self.syslog,
850 'relief':GROOVE}).pack(side=LEFT, anchor=W)
851 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
852 log.pack(side=RIGHT, anchor=E)
856 {'text':'Invisible mode?',
857 'variable':self.invisible,
858 'relief':GROOVE}).pack(side=LEFT, anchor=W)
860 log = LabeledEntry(gf, ' Idfile:', self.idfile, '14')
861 log.pack(side=RIGHT, anchor=E)
865 # Expert mode allows us to edit multiple sites
866 lf = Frame(self, relief=RAISED, bd=5)
868 text='Remote Mail Server Configurations',
869 bd=2).pack(side=TOP, pady=10)
870 ListEdit('New Server:',
871 map(lambda x: x.pollname, self.configuration.servers),
872 lambda site, self=self: self.server_edit(site),
873 lambda site, self=self: self.server_delete(site),
878 for sitename in self.subwidgets.keys():
879 self.subwidgets[sitename].destruct()
880 self.master.destroy()
884 if ConfirmQuit(self, self.mode + " configuration editor"):
888 for sitename in self.subwidgets.keys():
889 self.subwidgets[sitename].save()
890 self.fetch(Configuration, 'configuration')
894 elif not os.path.isfile(self.outfile) or Dialog(self,
895 title = 'Overwrite existing run control file?',
896 text = 'Really overwrite existing run control file?',
898 strings = ('Yes', 'No'),
899 default = 1).num == 0:
901 os.rename(self.outfile, self.outfile + "~")
902 # Pre-1.5.2 compatibility...
905 oldumask = os.umask(077)
906 fm = open(self.outfile, 'w')
911 os.chmod(self.outfile, 0600)
912 fm.write("# Configuration created %s by fetchmailconf %s\n" % (time.ctime(time.time()), version))
913 fm.write(`self.configuration`)
919 # Server editing stuff.
922 'title' : 'Remote site help',
923 'banner': 'Remote sites',
925 When you add a site name to the list here,
926 you initialize an entry telling fetchmail
927 how to poll a new site.
929 When you select a sitename (by double-
930 clicking it, or by single-clicking to
931 select and then clicking the Edit button),
932 you will open a window to configure that
937 'title' : 'Server options help',
938 'banner': 'Server Options',
940 The server options screen controls fetchmail
941 options that apply to one of your mailservers.
943 Once you have a mailserver configuration set
944 up as you like it, you can select `OK' to
945 store it in the server list maintained in
946 the main configuration window.
948 If you wish to discard changes to a server
949 configuration, select `Quit'.
953 'title' : 'Run Control help',
954 'banner': 'Run Controls',
956 If the `Poll normally' checkbox is on, the host is polled as part of
957 the normal operation of fetchmail when it is run with no arguments.
958 If it is off, fetchmail will only query this host when it is given as
959 a command-line argument.
961 The `True name of server' box should specify the actual DNS name
962 to query. By default this is the same as the poll name.
964 Normally each host described in the file is queried once each
965 poll cycle. If `Cycles to skip between polls' is greater than 0,
966 that's the number of poll cycles that are skipped between the
967 times this post is actually polled.
969 The `Server timeout' is the number of seconds fetchmail will wait
970 for a reply from the mailserver before concluding it is hung and
975 'title' : 'Protocol and Port help',
976 'banner': 'Protocol and Port',
978 These options control the remote-mail protocol
979 and TCP/IP service port used to query this
982 If you click the `Probe for supported protocols'
983 button, fetchmail will try to find you the most
984 capable server on the selected host (this will
985 only work if you're conncted to the Internet).
986 The probe only checks for ordinary IMAP and POP
987 protocols; fortunately these are the most
988 frequently supported.
990 The `Protocol' button bar offers you a choice of
991 all the different protocols available. The `auto'
992 protocol is the default mode; it probes the host
993 ports for POP3 and IMAP to see if either is
996 Normally the TCP/IP service port to use is
997 dictated by the protocol choice. The `Service'
998 field (only present in expert mode) lets you
999 set a non-standard service (port).
1003 'title' : 'Security option help',
1004 'banner': 'Security',
1006 The 'authorization mode' allows you to choose the
1007 mode that fetchmail uses to log in to your server. You
1008 can usually leave this at 'any', but you will have to pick
1009 'NTLM' and 'MSN' manually for the nonce.
1011 The 'interface' option allows you to specify a range
1012 of IP addresses to monitor for activity. If these
1013 addresses are not active, fetchmail will not poll.
1014 Specifying this may protect you from a spoofing attack
1015 if your client machine has more than one IP gateway
1016 address and some of the gateways are to insecure nets.
1018 The `monitor' option, if given, specifies the only
1019 device through which fetchmail is permitted to connect
1020 to servers. This option may be used to prevent
1021 fetchmail from triggering an expensive dial-out if the
1022 interface is not already active.
1024 The `interface' and `monitor' options are available
1025 only for Linux and freeBSD systems. See the fetchmail
1026 manual page for details on these.
1028 The ssl option enables SSL communication with a mailserver
1029 supporting Secure Sockets Layer. The sslkey and sslcert options
1030 declare key and certificate files for use with SSL.
1031 The sslcertck option enables strict checking of SSL server
1032 certificates (and sslcertpath gives the trusted certificate
1033 directory). The sslcommonname option helps if the server is
1034 misconfigured and returning "Server CommonName mismatch"
1035 warnings. With sslfingerprint, you can specify a finger-
1036 print the server's key is checked against.
1040 'title' : 'Multidrop option help',
1041 'banner': 'Multidrop',
1043 These options are only useful with multidrop mode.
1044 See the manual page for extended discussion.
1048 'title' : 'User list help',
1049 'banner': 'User list',
1051 When you add a user name to the list here,
1052 you initialize an entry telling fetchmail
1053 to poll the site on behalf of the new user.
1055 When you select a username (by double-
1056 clicking it, or by single-clicking to
1057 select and then clicking the Edit button),
1058 you will open a window to configure the
1059 user's options on that site.
1062 class ServerEdit(Frame, MyWidget):
1063 def __init__(self, host, parent):
1064 self.parent = parent
1066 self.subwidgets = {}
1067 for site in parent.configuration.servers:
1068 if site.pollname == host:
1070 if (self.server == None):
1071 self.server = Server()
1072 self.server.pollname = host
1073 self.server.via = None
1074 parent.configuration.servers.append(self.server)
1076 def edit(self, mode, master=None):
1077 Frame.__init__(self, master)
1079 self.master.title('Fetchmail host ' + self.server.pollname);
1080 self.master.iconname('Fetchmail host ' + self.server.pollname);
1081 self.post(Server, 'server')
1082 self.makeWidgets(self.server.pollname, mode)
1083 self.keepalive = [] # Use this to anchor the PhotoImage object
1084 make_icon_window(self, fetchmail_icon)
1087 # self.wait_window()
1091 for username in self.subwidgets.keys():
1092 self.subwidgets[username].destruct()
1093 del self.parent.subwidgets[self.server.pollname]
1094 self.master.destroy()
1097 if ConfirmQuit(self, 'server option editing'):
1101 self.fetch(Server, 'server')
1102 for username in self.subwidgets.keys():
1103 self.subwidgets[username].save()
1106 def defaultPort(self):
1107 proto = self.protocol.get()
1108 # Callback to reset the port number whenever the protocol type changes.
1109 # We used to only reset the port if it had a default (zero) value.
1110 # This turns out to be a bad idea especially in Novice mode -- if
1111 # you set POP3 and then set IMAP, the port invisibly remained 110.
1112 # Now we reset unconditionally on the theory that if you're setting
1113 # a custom port number you should be in expert mode and playing
1114 # close enough attention to notice this...
1115 self.service.set(defaultports[proto])
1116 if not proto in ("POP3", "APOP", "KPOP"): self.uidl.state = DISABLED
1118 def user_edit(self, username, mode):
1119 self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1121 def user_delete(self, username):
1122 if self.subwidgets.has_key(username):
1123 self.subwidgets[username].destruct()
1124 del self.server[username]
1126 def makeWidgets(self, host, mode):
1127 topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1129 leftwin = Frame(self);
1132 if mode != 'novice':
1133 ctlwin = Frame(leftwin, relief=RAISED, bd=5)
1134 Label(ctlwin, text="Run Controls").pack(side=TOP)
1135 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
1136 Checkbutton(ctlwin, text='Pass messages with bad headers?',
1137 variable=self.badheader).pack(side=TOP)
1138 LabeledEntry(ctlwin, 'True name of ' + host + ':',
1139 self.via, leftwidth).pack(side=TOP, fill=X)
1140 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
1141 self.interval, leftwidth).pack(side=TOP, fill=X)
1142 LabeledEntry(ctlwin, 'Server timeout (seconds):',
1143 self.timeout, leftwidth).pack(side=TOP, fill=X)
1144 Button(ctlwin, text='Help', fg='blue',
1145 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
1148 # Compute the available protocols from the compile-time options
1149 protolist = ['auto']
1150 if 'pop2' in feature_options:
1151 protolist.append("POP2")
1152 if 'pop3' in feature_options:
1153 protolist = protolist + ["POP3", "APOP", "KPOP"]
1154 if 'sdps' in feature_options:
1155 protolist.append("SDPS")
1156 if 'imap' in feature_options:
1157 protolist.append("IMAP")
1158 if 'etrn' in feature_options:
1159 protolist.append("ETRN")
1160 if 'odmr' in feature_options:
1161 protolist.append("ODMR")
1163 protwin = Frame(leftwin, relief=RAISED, bd=5)
1164 Label(protwin, text="Protocol").pack(side=TOP)
1165 ButtonBar(protwin, '',
1166 self.protocol, protolist, 2,
1168 if mode != 'novice':
1169 LabeledEntry(protwin, 'On server TCP/IP service:',
1170 self.service, leftwidth).pack(side=TOP, fill=X)
1172 Checkbutton(protwin,
1173 text="POP3: track `seen' with client-side UIDLs?",
1174 variable=self.uidl).pack(side=TOP)
1175 Button(protwin, text='Probe for supported protocols', fg='blue',
1176 command=self.autoprobe).pack(side=LEFT)
1177 Button(protwin, text='Help', fg='blue',
1178 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
1179 protwin.pack(fill=X)
1181 userwin = Frame(leftwin, relief=RAISED, bd=5)
1182 Label(userwin, text="User entries for " + host).pack(side=TOP)
1183 ListEdit("New user: ",
1184 map(lambda x: x.remote, self.server.users),
1185 lambda u, m=mode, s=self: s.user_edit(u, m),
1186 lambda u, s=self: s.user_delete(u),
1188 userwin.pack(fill=X)
1190 leftwin.pack(side=LEFT, anchor=N, fill=X);
1192 if mode != 'novice':
1193 rightwin = Frame(self);
1195 mdropwin = Frame(rightwin, relief=RAISED, bd=5)
1196 Label(mdropwin, text="Multidrop options").pack(side=TOP)
1197 LabeledEntry(mdropwin, 'Envelope address header:',
1198 self.envelope, '22').pack(side=TOP, fill=X)
1199 LabeledEntry(mdropwin, 'Envelope headers to skip:',
1200 self.envskip, '22').pack(side=TOP, fill=X)
1201 LabeledEntry(mdropwin, 'Name prefix to strip:',
1202 self.qvirtual, '22').pack(side=TOP, fill=X)
1203 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
1204 variable=self.dns).pack(side=TOP)
1205 Label(mdropwin, text="DNS aliases").pack(side=TOP)
1206 ListEdit("New alias: ", self.server.aka, None, None, mdropwin, None)
1207 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
1208 ListEdit("New domain: ",
1209 self.server.localdomains, None, None, mdropwin, multihelp)
1210 mdropwin.pack(fill=X)
1212 if os_type in ('linux', 'freebsd'):
1213 secwin = Frame(rightwin, relief=RAISED, bd=5)
1214 Label(secwin, text="Security").pack(side=TOP)
1215 # Don't actually let users set this. KPOP sets it implicitly
1216 ButtonBar(secwin, 'Authorization mode:',
1217 self.auth, authlist, 2, None).pack(side=TOP)
1218 if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
1219 LabeledEntry(secwin, 'IP range to check before poll:',
1220 self.interface, leftwidth).pack(side=TOP, fill=X)
1221 if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
1222 LabeledEntry(secwin, 'Interface to monitor:',
1223 self.monitor, leftwidth).pack(side=TOP, fill=X)
1224 # Someday this should handle Kerberos 5 too
1225 if 'kerberos' in feature_options:
1226 LabeledEntry(secwin, 'Principal:',
1227 self.principal, '12').pack(side=TOP, fill=X)
1228 # ESMTP authentication
1229 LabeledEntry(secwin, 'ESMTP name:',
1230 self.esmtpname, '12').pack(side=TOP, fill=X)
1231 LabeledEntry(secwin, 'ESMTP password:',
1232 self.esmtppassword, '12').pack(side=TOP, fill=X)
1233 Button(secwin, text='Help', fg='blue',
1234 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
1237 rightwin.pack(side=LEFT, anchor=N);
1239 def autoprobe(self):
1240 # Note: this only handles case (1) near fetchmail.c:1032
1241 # We're assuming people smart enough to set up ssh tunneling
1242 # won't need autoprobing.
1244 realhost = self.server.via
1246 realhost = self.server.pollname
1248 for protocol in ("IMAP","POP3","POP2"):
1249 service = defaultports[protocol]
1250 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1252 sock.connect((realhost, ianaservices[service]))
1253 greetline = sock.recv(1024)
1259 confwin = Toplevel()
1260 if greetline == None:
1261 title = "Autoprobe of " + realhost + " failed"
1263 Fetchmailconf didn't find any mailservers active.
1264 This could mean the host doesn't support any,
1265 or that your Internet connection is down, or
1266 that the host is so slow that the probe timed
1267 out before getting a response.
1271 # OK, now try to recognize potential problems
1273 if protocol == "POP2":
1274 warnings = warnings + """
1275 It appears you have somehow found a mailserver running only POP2.
1276 Congratulations. Have you considered a career in archaeology?
1278 Unfortunately, stock fetchmail binaries don't include POP2 support anymore.
1279 Unless the first line of your fetchmail -V output includes the string "POP2",
1280 you'll have to build it from sources yourself with the configure
1281 switch --enable-POP2.
1285 ### POP3 servers start here
1287 if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
1288 warnings = warnings + """
1289 This appears to be an old version of the UC Davis POP server. These are
1290 dangerously unreliable (among other problems, they may drop your mailbox
1291 on the floor if your connection is interrupted during the session).
1293 It is strongly recommended that you find a better POP3 server. The fetchmail
1294 FAQ includes pointers to good ones.
1297 if string.find(greetline, "comcast.net") > 0:
1298 warnings = warnings + """
1299 The Comcast Maillennium POP3 server only returns the first 80K of a long
1300 message retrieved with TOP. Its response to RETR is normal, so use the
1304 # Steve VanDevender <stevev@efn.org> writes:
1305 # The only system I have seen this happen with is cucipop-1.31
1306 # under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
1307 # 2.x and probably quite a few other systems. It appears to be a
1308 # bug or bad interaction with the SunOS realloc() -- it turns out
1309 # that internally cucipop does allocate a certain data structure in
1310 # multiples of 16, using realloc() to bump it up to the next
1311 # multiple if it needs more.
1313 # The distinctive symptom is that when there are 16 messages in the
1314 # inbox, you can RETR and DELE all 16 messages successfully, but on
1315 # QUIT cucipop returns something like "-ERR Error locking your
1316 # mailbox" and aborts without updating it.
1318 # The cucipop banner looks like:
1320 # +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1322 if string.find(greetline, "Cubic Circle") > 0:
1323 warnings = warnings + """
1324 I see your server is running cucipop. Better make sure the server box
1325 isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
1326 under that version, and doesn't cope with the result gracefully. Newer
1327 SunOS and Solaris machines run cucipop OK.
1329 Also, some versions of cucipop don't assert an exclusive lock on your
1330 mailbox when it's being queried. This means that if you have more than
1331 one fetchmail query running against the same mailbox, bad things can happen.
1333 if string.find(greetline, "David POP3 Server") > 0:
1334 warnings = warnings + """
1335 This POP3 server is badly broken. You should get rid of it -- and the
1336 brain-dead Microsoft operating system it rode in on.
1339 # The greeting line on the server known to be buggy is:
1340 # +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
1342 if string.find(greetline, "FTGate") > 0:
1343 warnings = warnings + """
1344 This POP server has a weird bug; it says OK twice in response to TOP.
1345 Its response to RETR is normal, so use the `fetchall' option.
1348 if string.find(greetline, " geonet.de") > 0:
1349 warnings = warnings + """
1350 You appear to be using geonet. As of late 2002, the TOP command on
1351 geonet's POP3 is broken. Use the fetchall option.
1354 if string.find(greetline, "OpenMail") > 0:
1355 warnings = warnings + """
1356 You appear to be using some version of HP OpenMail. Many versions of
1357 OpenMail do not process the "TOP" command correctly; the symptom is that
1358 only the header and first line of each message is retrieved. To work
1359 around this bug, turn on `fetchall' on all user entries associated with
1363 if string.find(greetline, "Escape character is") > 0:
1364 warnings = warnings + """
1365 Your greeting line looks like it was written by a fetid pile of
1366 camel dung identified to me as `popa3d written by Solar Designer'.
1367 Beware! The UIDL support in this thing is known to be completely broken,
1368 and other things probably are too.
1371 if string.find(greetline, "MercuryP/NLM v1.48") > 0:
1372 warnings = warnings + """
1373 This is not a POP3 server. It has delusions of being one, but after
1374 RETR all messages are automatically marked to be deleted. The only
1375 way to prevent this is to issue an RSET before leaving the server.
1376 Fetchmail does this, but we suspect this is probably broken in lots
1380 if string.find(greetline, "POP-Max") > 0:
1381 warnings = warnings + """
1382 The Mail Max POP3 server screws up on mail with attachments. It
1383 reports the message size with attachments included, but doesn't
1384 download them on a RETR or TOP (this violates the IMAP RFCs). It also
1385 doesn't implement TOP correctly. You should get rid of it -- and the
1386 brain-dead NT server it rode in on.
1389 if string.find(greetline, "POP3 Server Ready") > 0:
1390 warnings = warnings + """
1391 Some server that uses this greeting line has been observed to choke on
1392 TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
1395 if string.find(greetline, "QPOP") > 0:
1396 warnings = warnings + """
1397 This appears to be a version of Eudora qpopper. That's good. Fetchmail
1398 knows all about qpopper. However, be aware that the 2.53 version of
1399 qpopper does something odd that causes fetchmail to hang with a socket
1400 error on very large messages. This is probably not a fetchmail bug, as
1401 it has been observed with fetchpop. The fix is to upgrade to qpopper
1402 3.0beta or a more recent version. Better yet, switch to IMAP.
1405 if string.find(greetline, " sprynet.com") > 0:
1406 warnings = warnings + """
1407 You appear to be using a SpryNet server. In mid-1999 it was reported that
1408 the SpryNet TOP command marks messages seen. Therefore, for proper error
1409 recovery in the event of a line drop, it is strongly recommended that you
1410 turn on `fetchall' on all user entries associated with this server.
1413 if string.find(greetline, "TEMS POP3") > 0:
1414 warnings = warnings + """
1415 Your POP3 server has "TEMS" in its header line. At least one such
1416 server does not process the "TOP" command correctly; the symptom is
1417 that fetchmail hangs when trying to retrieve mail. To work around
1418 this bug, turn on `fetchall' on all user entries associated with this
1422 if string.find(greetline, " spray.se") > 0:
1423 warnings = warnings + """
1424 Your POP3 server has "spray.se" in its header line. In May 2000 at
1425 least one such server did not process the "TOP" command correctly; the
1426 symptom is that messages are treated as headerless. To work around
1427 this bug, turn on `fetchall' on all user entries associated with this
1431 if string.find(greetline, " usa.net") > 0:
1432 warnings = warnings + """
1433 You appear to be using USA.NET's free mail service. Their POP3 servers
1434 (at least as of the 2.2 version in use mid-1998) are quite flaky, but
1435 fetchmail can compensate. They seem to require that fetchall be switched on
1436 (otherwise you won't necessarily see all your mail, not even new mail).
1437 They also botch the TOP command the fetchmail normally uses for retrieval
1438 (it only retrieves about 10 lines rather than the number specified).
1439 Turning on fetchall will disable the use of TOP.
1441 Therefore, it is strongly recommended that you turn on `fetchall' on all
1442 user entries associated with this server.
1445 if string.find(greetline, " Novonyx POP3") > 0:
1446 warnings = warnings + """
1447 Your mailserver is running Novonyx POP3. This server, at least as of
1448 version 2.17, seems to have problems handling and reporting seen bits.
1449 You may have to use the fetchall option.
1452 if string.find(greetline, " IMS POP3") > 0:
1453 warnings = warnings + """
1454 Some servers issuing the greeting line 'IMS POP3' have been known to
1455 do byte-stuffing incorrectly. This means that if a message you receive
1456 has a . (period) at start of line, fetchmail will become confused and
1457 probably wedge itself. (This bug was recorded on IMS POP3 0.86.)
1461 ### IMAP servers start here
1463 if string.find(greetline, "GroupWise") > 0:
1464 warnings = warnings + """
1465 The Novell GroupWise IMAP server would be better named GroupFoolish;
1466 it is (according to the designer of IMAP) unusably broken. Among
1467 other things, it doesn't include a required content length in its
1468 BODY[TEXT] response.<p>
1470 Fetchmail works around this problem, but we strongly recommend voting
1471 with your dollars for a server that isn't brain-dead. If you stick
1472 with code as shoddy as GroupWise seems to be, you will probably pay
1473 for it with other problems.<p>
1476 if string.find(greetline, "InterChange") > 0:
1477 warnings = warnings + """
1479 The InterChange IMAP server at release levels below 3.61.08 screws up
1480 on mail with attachments. It doesn't fetch them if you give it a
1481 BODY[TEXT] request, though it does if you request RFC822.TEXT.
1482 According to the IMAP RFCs and their maintainer these should be
1483 equivalent -- and we can't drop the BODY[TEXT] form because M$
1484 Exchange (quite legally under RFC2062) rejectsit. The InterChange
1485 folks claim to have fixed this bug in 3.61.08.
1488 if string.find(greetline, "Imail") > 0:
1489 warnings = warnings + """
1490 We've seen a bug report indicating that this IMAP server (at least as of
1491 version 5.0.7) returns an invalid body size for messages with MIME
1492 attachments; the effect is to drop the attachments on the floor. We
1493 recommend you upgrade to a non-broken IMAP server.
1496 if string.find(greetline, "Domino IMAP4") > 0:
1497 warnings = warnings + """
1498 Your IMAP server appears to be Lotus Domino. This server, at least up
1499 to version 4.6.2a, has a bug in its generation of MIME boundaries (see
1500 the details in the fetchmail FAQ). As a result, even MIME aware MUAs
1501 will see attachments as part of the message text. If your Domino server's
1502 POP3 facility is enabled, we recommend you fall back on it.
1506 ### Checks for protocol variants start here
1508 closebrak = string.find(greetline, ">")
1509 if closebrak > 0 and greetline[closebrak+1] == "\r":
1510 warnings = warnings + """
1511 It looks like you could use APOP on this server and avoid sending it your
1512 password in clear. You should talk to the mailserver administrator about
1516 if string.find(greetline, "IMAP2bis") > 0:
1517 warnings = warnings + """
1518 IMAP2bis servers have a minor problem; they can't peek at messages without
1519 marking them seen. If you take a line hit during the retrieval, the
1520 interrupted message may get left on the server, marked seen.
1522 To work around this, it is recommended that you set the `fetchall'
1523 option on all user entries associated with this server, so any stuck
1524 mail will be retrieved next time around.
1526 To fix this bug, upgrade to an IMAP4 server. The fetchmail FAQ includes
1527 a pointer to an open-source implementation.
1530 if string.find(greetline, "IMAP4rev1") > 0:
1531 warnings = warnings + """
1532 I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
1533 remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
1534 has therefore been extremely well tested with this class of server.
1538 warnings = warnings + """
1539 Fetchmail doesn't know anything special about this server type.
1543 # Display success window with warnings
1544 title = "Autoprobe of " + realhost + " succeeded"
1545 confirm = "The " + protocol + " server said:\n\n" + greetline + warnings
1546 self.protocol.set(protocol)
1547 self.service.set(defaultports[protocol])
1548 confwin.title(title)
1549 confwin.iconname(title)
1550 Label(confwin, text=title).pack()
1551 Message(confwin, text=confirm, width=600).pack()
1552 Button(confwin, text='Done',
1553 command=lambda x=confwin: x.destroy(), bd=2).pack()
1556 # User editing stuff
1560 'title' : 'User option help',
1561 'banner': 'User options',
1563 You may use this panel to set options
1564 that may differ between individual
1567 Once you have a user configuration set
1568 up as you like it, you can select `OK' to
1569 store it in the user list maintained in
1570 the site configuration window.
1572 If you wish to discard the changes you have
1573 made to user options, select `Quit'.
1577 'title' : 'Local name help',
1578 'banner': 'Local names',
1580 The local name(s) in a user entry are the
1581 people on the client machine who should
1582 receive mail from the poll described.
1584 Note: if a user entry has more than one
1585 local name, messages will be retrieved
1586 in multidrop mode. This complicates
1587 the configuration issues; see the manual
1588 page section on multidrop mode.
1590 Warning: Be careful with local names
1591 such as foo@bar.com, as that can cause
1592 the mail to be sent to foo@bar.com instead
1593 of sending it to your local system.
1596 class UserEdit(Frame, MyWidget):
1597 def __init__(self, username, parent):
1598 self.parent = parent
1600 for user in parent.server.users:
1601 if user.remote == username:
1603 if self.user == None:
1605 self.user.remote = username
1606 self.user.localnames = [username]
1607 parent.server.users.append(self.user)
1609 def edit(self, mode, master=None):
1610 Frame.__init__(self, master)
1612 self.master.title('Fetchmail user ' + self.user.remote
1613 + ' querying ' + self.parent.server.pollname);
1614 self.master.iconname('Fetchmail user ' + self.user.remote);
1615 self.post(User, 'user')
1616 self.makeWidgets(mode, self.parent.server.pollname)
1617 self.keepalive = [] # Use this to anchor the PhotoImage object
1618 make_icon_window(self, fetchmail_icon)
1621 # self.wait_window()
1625 # Yes, this test can fail -- if you delete the parent window.
1626 if self.parent.subwidgets.has_key(self.user.remote):
1627 del self.parent.subwidgets[self.user.remote]
1628 self.master.destroy()
1631 if ConfirmQuit(self, 'user option editing'):
1636 for x in self.user.localnames: ok = ok + (string.find(x, '@') != -1)
1637 if ok == 0 or Dialog(self,
1638 title = "Really accept an embedded '@' ?",
1639 text = "Local names with an embedded '@', such as in foo@bar "
1640 "might result in your mail being sent to foo@bar.com "
1641 "instead of your local system.\n Are you sure you want "
1642 "a local user name with an '@' in it?",
1643 bitmap = 'question',
1644 strings = ('Yes', 'No'),
1645 default = 1).num == 0:
1646 self.fetch(User, 'user')
1649 def makeWidgets(self, mode, servername):
1650 dispose_window(self,
1651 "User options for " + self.user.remote + " querying " + servername,
1654 if mode != 'novice':
1655 leftwin = Frame(self);
1659 secwin = Frame(leftwin, relief=RAISED, bd=5)
1660 Label(secwin, text="Authentication").pack(side=TOP)
1661 LabeledEntry(secwin, 'Password:',
1662 self.password, '12').pack(side=TOP, fill=X)
1663 secwin.pack(fill=X, anchor=N)
1665 if 'ssl' in feature_options or 'ssl' in dictmembers:
1666 sslwin = Frame(leftwin, relief=RAISED, bd=5)
1667 Checkbutton(sslwin, text="Use SSL?",
1668 variable=self.ssl).pack(side=TOP, fill=X)
1669 LabeledEntry(sslwin, 'SSL key:',
1670 self.sslkey, '14').pack(side=TOP, fill=X)
1671 LabeledEntry(sslwin, 'SSL certificate:',
1672 self.sslcert, '14').pack(side=TOP, fill=X)
1673 Checkbutton(sslwin, text="Check server SSL certificate?",
1674 variable=self.sslcertck).pack(side=TOP, fill=X)
1675 LabeledEntry(sslwin, 'SSL trusted certificate directory:',
1676 self.sslcertpath, '14').pack(side=TOP, fill=X)
1677 LabeledEntry(sslwin, 'SSL CommonName:',
1678 self.sslcommonname, '14').pack(side=TOP, fill=X)
1679 LabeledEntry(sslwin, 'SSL key fingerprint:',
1680 self.sslfingerprint, '14').pack(side=TOP, fill=X)
1681 sslwin.pack(fill=X, anchor=N)
1683 names = Frame(leftwin, relief=RAISED, bd=5)
1684 Label(names, text="Local names").pack(side=TOP)
1685 ListEdit("New name: ",
1686 self.user.localnames, None, None, names, localhelp)
1687 names.pack(fill=X, anchor=N)
1689 if mode != 'novice':
1690 targwin = Frame(leftwin, relief=RAISED, bd=5)
1691 Label(targwin, text="Forwarding Options").pack(side=TOP)
1692 Label(targwin, text="Listeners to forward to").pack(side=TOP)
1693 ListEdit("New listener:",
1694 self.user.smtphunt, None, None, targwin, None)
1695 Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
1696 ListEdit("Domains:",
1697 self.user.fetchdomains, None, None, targwin, None)
1698 LabeledEntry(targwin, 'Use domain on RCPT TO line:',
1699 self.smtpaddress, '26').pack(side=TOP, fill=X)
1700 LabeledEntry(targwin, 'Set fixed RCPT TO address:',
1701 self.smtpname, '26').pack(side=TOP, fill=X)
1702 LabeledEntry(targwin, 'Connection setup command:',
1703 self.preconnect, '26').pack(side=TOP, fill=X)
1704 LabeledEntry(targwin, 'Connection wrapup command:',
1705 self.postconnect, '26').pack(side=TOP, fill=X)
1706 LabeledEntry(targwin, 'Local delivery agent:',
1707 self.mda, '26').pack(side=TOP, fill=X)
1708 LabeledEntry(targwin, 'BSMTP output file:',
1709 self.bsmtp, '26').pack(side=TOP, fill=X)
1710 LabeledEntry(targwin, 'Listener spam-block codes:',
1711 self.antispam, '26').pack(side=TOP, fill=X)
1712 LabeledEntry(targwin, 'Pass-through properties:',
1713 self.properties, '26').pack(side=TOP, fill=X)
1714 Checkbutton(targwin, text="Use LMTP?",
1715 variable=self.lmtp).pack(side=TOP, fill=X)
1716 targwin.pack(fill=X, anchor=N)
1718 if mode != 'novice':
1719 leftwin.pack(side=LEFT, fill=X, anchor=N)
1720 rightwin = Frame(self)
1724 optwin = Frame(rightwin, relief=RAISED, bd=5)
1725 Label(optwin, text="Processing Options").pack(side=TOP)
1726 Checkbutton(optwin, text="Suppress deletion of messages after reading",
1727 variable=self.keep).pack(side=TOP, anchor=W)
1728 Checkbutton(optwin, text="Fetch old messages as well as new",
1729 variable=self.fetchall).pack(side=TOP, anchor=W)
1730 if mode != 'novice':
1731 Checkbutton(optwin, text="Flush seen messages before retrieval",
1732 variable=self.flush).pack(side=TOP, anchor=W)
1733 Checkbutton(optwin, text="Flush oversized messages before retrieval",
1734 variable=self.limitflush).pack(side=TOP, anchor=W)
1735 Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply",
1736 variable=self.rewrite).pack(side=TOP, anchor=W)
1737 Checkbutton(optwin, text="Force CR/LF at end of each line",
1738 variable=self.forcecr).pack(side=TOP, anchor=W)
1739 Checkbutton(optwin, text="Strip CR from end of each line",
1740 variable=self.stripcr).pack(side=TOP, anchor=W)
1741 Checkbutton(optwin, text="Pass 8 bits even though SMTP says 7BIT",
1742 variable=self.pass8bits).pack(side=TOP, anchor=W)
1743 Checkbutton(optwin, text="Undo MIME armoring on header and body",
1744 variable=self.mimedecode).pack(side=TOP, anchor=W)
1745 Checkbutton(optwin, text="Drop Status lines from forwarded messages",
1746 variable=self.dropstatus).pack(side=TOP, anchor=W)
1747 Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
1748 variable=self.dropdelivered).pack(side=TOP, anchor=W)
1751 if mode != 'novice':
1752 limwin = Frame(rightwin, relief=RAISED, bd=5)
1753 Label(limwin, text="Resource Limits").pack(side=TOP)
1754 LabeledEntry(limwin, 'Message size limit:',
1755 self.limit, '30').pack(side=TOP, fill=X)
1756 LabeledEntry(limwin, 'Size warning interval:',
1757 self.warnings, '30').pack(side=TOP, fill=X)
1758 LabeledEntry(limwin, 'Max messages to fetch per poll:',
1759 self.fetchlimit, '30').pack(side=TOP, fill=X)
1760 LabeledEntry(limwin, 'Max message sizes to fetch per transaction:',
1761 self.fetchsizelimit, '30').pack(side=TOP, fill=X)
1762 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1763 LabeledEntry(limwin, 'Use fast UIDL:',
1764 self.fastuidl, '30').pack(side=TOP, fill=X)
1765 LabeledEntry(limwin, 'Max messages to forward per poll:',
1766 self.batchlimit, '30').pack(side=TOP, fill=X)
1767 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1768 LabeledEntry(limwin, 'Interval between expunges:',
1769 self.expunge, '30').pack(side=TOP, fill=X)
1770 Checkbutton(limwin, text="Idle after each poll (IMAP only)",
1771 variable=self.idle).pack(side=TOP, anchor=W)
1774 if self.parent.server.protocol == 'IMAP':
1775 foldwin = Frame(rightwin, relief=RAISED, bd=5)
1776 Label(foldwin, text="Remote folders (IMAP only)").pack(side=TOP)
1777 ListEdit("New folder:", self.user.mailboxes,
1778 None, None, foldwin, None)
1779 foldwin.pack(fill=X, anchor=N)
1781 if mode != 'novice':
1782 rightwin.pack(side=LEFT)
1788 # Top-level window that offers either novice or expert mode
1789 # (but not both at once; it disappears when one is selected).
1792 class Configurator(Frame):
1793 def __init__(self, outfile, master, onexit, parent):
1794 Frame.__init__(self, master)
1795 self.outfile = outfile
1796 self.onexit = onexit
1797 self.parent = parent
1798 self.master.title('fetchmail configurator');
1799 self.master.iconname('fetchmail configurator');
1801 self.keepalive = [] # Use this to anchor the PhotoImage object
1802 make_icon_window(self, fetchmail_icon)
1804 Message(self, text="""
1805 Use `Novice Configuration' for basic fetchmail setup;
1806 with this, you can easily set up a single-drop connection
1807 to one remote mail server.
1808 """, width=600).pack(side=TOP)
1809 Button(self, text='Novice Configuration',
1810 fg='blue', command=self.novice).pack()
1812 Message(self, text="""
1813 Use `Expert Configuration' for advanced fetchmail setup,
1814 including multiple-site or multidrop connections.
1815 """, width=600).pack(side=TOP)
1816 Button(self, text='Expert Configuration',
1817 fg='blue', command=self.expert).pack()
1819 Message(self, text="""
1820 Or you can just select `Quit' to leave the configurator now and
1821 return to the main panel.
1822 """, width=600).pack(side=TOP)
1823 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1824 master.protocol("WM_DELETE_WINDOW", self.leave)
1827 self.master.destroy()
1828 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1831 self.master.destroy()
1832 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1835 self.master.destroy()
1838 # Run a command in a scrolling text widget, displaying its output
1840 class RunWindow(Frame):
1841 def __init__(self, command, master, parent):
1842 Frame.__init__(self, master)
1843 self.master = master
1844 self.master.title('fetchmail run window');
1845 self.master.iconname('fetchmail run window');
1848 text="Running "+command,
1849 bd=2).pack(side=TOP, pady=10)
1850 self.keepalive = [] # Use this to anchor the PhotoImage object
1851 make_icon_window(self, fetchmail_icon)
1853 # This is a scrolling text window
1854 textframe = Frame(self)
1855 scroll = Scrollbar(textframe)
1856 self.textwidget = Text(textframe, setgrid=TRUE)
1857 textframe.pack(side=TOP, expand=YES, fill=BOTH)
1858 self.textwidget.config(yscrollcommand=scroll.set)
1859 self.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
1860 scroll.config(command=self.textwidget.yview)
1861 scroll.pack(side=RIGHT, fill=BOTH)
1862 textframe.pack(side=TOP)
1864 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1866 self.update() # Draw widget before executing fetchmail
1868 # Always look for a runnable command in the directory we're running in
1869 # first. This avoids some obscure version-skew errors that can occur
1870 # if you pick up an old fetchmail from the standard system locations.
1871 os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"]
1872 child_stdout = os.popen(command + " 2>&1 </dev/null", "r")
1874 ch = child_stdout.read(1)
1877 self.textwidget.insert(END, ch)
1878 self.textwidget.insert(END, "Done.")
1879 self.textwidget.see(END);
1882 self.master.destroy()
1884 # Here's where we choose either configuration or launching
1886 class MainWindow(Frame):
1887 def __init__(self, outfile, master=None):
1888 Frame.__init__(self, master)
1889 self.outfile = outfile
1890 self.master.title('fetchmail launcher');
1891 self.master.iconname('fetchmail launcher');
1894 text='Fetchmailconf ' + version,
1895 bd=2).pack(side=TOP, pady=10)
1896 self.keepalive = [] # Use this to anchor the PhotoImage object
1897 make_icon_window(self, fetchmail_icon)
1900 ## Test icon display with the following:
1901 # icon_image = PhotoImage(data=fetchmail_icon)
1902 # Label(self, image=icon_image).pack(side=TOP, pady=10)
1903 # self.keepalive.append(icon_image)
1905 Message(self, text="""
1906 Use `Configure fetchmail' to tell fetchmail about the remote
1907 servers it should poll (the host name, your username there,
1908 whether to use POP or IMAP, and so forth).
1909 """, width=600).pack(side=TOP)
1910 self.configbutton = Button(self, text='Configure fetchmail',
1911 fg='blue', command=self.configure)
1912 self.configbutton.pack()
1914 Message(self, text="""
1915 Use `Run fetchmail' to run fetchmail with debugging enabled.
1916 This is a good way to test out a new configuration.
1917 """, width=600).pack(side=TOP)
1918 Button(self, text='Run fetchmail',fg='blue', command=self.test).pack()
1920 Message(self, text="""
1921 Use `Run fetchmail' to run fetchmail in foreground.
1922 Progress messages will be shown, but not debug messages.
1923 """, width=600).pack(side=TOP)
1924 Button(self, text='Run fetchmail', fg='blue', command=self.run).pack()
1926 Message(self, text="""
1927 Or you can just select `Quit' to exit the launcher now.
1928 """, width=600).pack(side=TOP)
1929 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1931 def configure(self):
1932 self.configbutton.configure(state=DISABLED)
1933 Configurator(self.outfile, Toplevel(),
1934 lambda self=self: self.configbutton.configure(state=NORMAL),
1937 cmd = "fetchmail -N -d0 --nosyslog -v"
1939 cmd = cmd + " -f " + rcfile
1940 RunWindow(cmd, Toplevel(), self)
1943 cmd = "fetchmail -N -d0"
1945 cmd = cmd + " -f " + rcfile
1946 RunWindow(cmd, Toplevel(), self)
1951 # Functions for turning a dictionary into an instantiated object tree.
1953 def intersect(list1, list2):
1954 # Compute set intersection of lists
1961 def setdiff(list1, list2):
1962 # Compute set difference of lists
1969 def copy_instance(toclass, fromdict):
1970 # Initialize a class object of given type from a conformant dictionary.
1971 for fld in fromdict.keys():
1972 if not fld in dictmembers:
1973 dictmembers.append(fld)
1974 # The `optional' fields are the ones we can ignore for purposes of
1975 # conformability checking; they'll still get copied if they are
1976 # present in the dictionary.
1977 optional = ('interface', 'monitor',
1978 'esmtpname', 'esmtppassword',
1979 'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
1980 'sslcertpath', 'sslcommonname', 'sslfingerprint', 'showdots')
1981 class_sig = setdiff(toclass.__dict__.keys(), optional)
1983 dict_keys = setdiff(fromdict.keys(), optional)
1985 common = intersect(class_sig, dict_keys)
1986 if 'typemap' in class_sig:
1987 class_sig.remove('typemap')
1988 if tuple(class_sig) != tuple(dict_keys):
1989 print "Fields don't match what fetchmailconf expected:"
1990 # print "Class signature: " + `class_sig`
1991 # print "Dictionary keys: " + `dict_keys`
1992 diff = setdiff(class_sig, common)
1994 print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
1995 diff = setdiff(dict_keys, common)
1997 print "Not matched in dictionary keys: " + `diff`
2000 for x in fromdict.keys():
2001 setattr(toclass, x, fromdict[x])
2004 # And this is the main sequence. How it works:
2006 # First, call `fetchmail --configdump' and trap the output in a tempfile.
2007 # This should fill it with a Python initializer for a variable `fetchmailrc'.
2008 # Run execfile on the file to pull fetchmailrc into Python global space.
2009 # You don't want static data, though; you want, instead, a tree of objects
2010 # with the same data members and added appropriate methods.
2012 # This is what the copy_instance function() is for. It tries to copy a
2013 # dictionary field by field into a class, aborting if the class and dictionary
2014 # have different data members (except for any typemap member in the class;
2015 # that one is strictly for use by the MyWidget supperclass).
2017 # Once the object tree is set up, require user to choose novice or expert
2018 # mode and instantiate an edit object for the configuration. Class methods
2019 # will take it all from there.
2021 # Options (not documented because they're for fetchmailconf debuggers only):
2022 # -d: Read the configuration and dump it to stdout before editing. Dump
2023 # the edited result to stdout as well.
2024 # -f: specify the run control file to read.
2026 if __name__ == '__main__':
2028 if not os.environ.has_key("DISPLAY"):
2029 print "fetchmailconf must be run under X"
2032 fetchmail_icon = """
2033 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
2034 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
2035 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
2036 CJR7Y/+UMdbOxnNrY97Ove/Wvd7GrZyEa961jL2Ua9alc86ca7WEUntSKcaMSqVjGNZ7GGM5CNa1
2037 jPfOnN6tc3taMffeve/WtWtaQv/OjGtSMYRzWv/erda1hM6te7WUY62MWs61jP/vzv/ntda9jL2l
2038 czEhAO/n1oyEc//elDEpGEo5EOfexpyUe+/epefevffvxnNrQpyUStbWzsbGvZyclN7ezmNjWv//
2039 5/f33qWllNbWve/vzv//1ufnve/vvf//xvf3vefnrf//taWlc0pKMf//pbW1Y///jKWlWq2tWsbG
2040 Y///c97eUvf3Ut7nc+/3a87We8bOjOfv1u/37/f//621tb3Gxtbn52Nra87n53uUlJTv/6W9xuf3
2041 /8bW3iExOXu11tbv/5TW/4TO/63e/zmt/1KUxlK1/2u9/wCM/73GzrXG1gBKjACE/87e72NzhCkx
2042 OaXO92OMtUql/xCE/wApUtbe57W9xnN7hHut52Ot/xBSnABKnABavQB7/2ul7zF71gBr77XO73Oc
2043 1lqc9yFSlBApSimE/wAYOQApY0J7zlKM5wAxhABS1gBj/6W95wAhWgA5nAAYSgBS7wBS/wBK9wAp
2044 jABC5wBK/wApnABC/wApxgAhtYSMtQAQYwAp/3OE74SMxgAYxlpjvWNr70pS/wgQ3sbGzs7O1qWl
2045 3qWl70pKe0JC/yEhlCkp/wgI/wAAEAAAIQAAKQAAOQAASgAAUgAAYwAAawAAlAAAnAAApQAArQAA
2046 zgAA1gAA5wAA9wAA/0pC/xgQ52Na9ykhe4R7zikhYxgQSjEpQgAAACwAAAAAPAAoAAAI/wABCBxI
2047 sKDBgwgTKiRIYKHDhxARIvgXsaLFhGgEUBSYoKPHjyBDihxJkuS/kwNLqlzJcuTJjQBaypxpEiVH
2048 mjhxvkyZs2fLnTd9ehxAtKjRo0ZrwhTasUsENhYHKOUpk1E3j11mxCBiQVLEBlJd2owp9iVRjwUs
2049 zMCQ5IcLD4saPVxjIKxIoGTvvqSoyFEFGTBeqEhyxAoSFR/USGKVcEGBAwDshsSr1OYTEyhQpJiS
2050 ZcoUKWOQtJDRJFSaggzUGBgoGSTlsjahlPCRIkWVKT16THHRIoqIISBIEUgAYIGBhgRbf3ytFygU
2051 FZp9UDmxQkkMCRwyZKDBQy4aApABhP8XqNwj88l7BVpQYZtF5iArWgwAgGZBq24HU7OeGhQ90PVA
2052 aKZZCiiUMJ9ArSTEwGqR8ZeXfzbV0MIIMQTBwoUdxDDfAm8sZFyDZVEF4UYSKBEBD0+k6IEFPMxH
2053 3FzldXSea+kBgANJSOWIlIMhXZXAXv+c1WM3PuJEpH8iuhbAkv+MdENPRHaTRkdF/jiWSKCAwlKW
2054 VbbkY5Q0LgUSKExgoYBKCjCxARpdltQNKHaUoYAddnR53lVRnJLKBWh4RIEGCZx5FSOv1OLNDUVe
2055 deZHaWiZAB35fIOGNtbEUeV5oGAByzPOrBPFGt3kwEgxITACSg5oLGGLMg60oQAjaNz/oAAcN4Ai
2056 a0c3kHFDK3jYsw4g9sRzBgPLXdkRrBrQ8gsWQUxCCRZX9IJNBQ1s8IgCdeBCzBYN6IBIN2TUsQYd
2057 dXhDBxdzlAHOHHKEcocZdWwDjx8MTCmjsR2FMAstw1RyiSzHqPLALaOwk8QmzCzDCSi0xJKMMk4E
2058 Yw8389iTDT32GAKOPf7YY0Aa9tATyD3w/EGsefgmgEYUtPiChLKWQDMBJtEUgYkzH2RiTgGfTMCI
2059 Mlu0Yc85hNiDziH2tMqOGL72QY47gshLb7Fi4roELcjoQIsxWpDwQyfS2OCJMkLI4YUmyhgxSTVg
2060 CP2FHPZ80UDcieBjStNPD5LPOyZT/y0iHGiMwswexDSzRiRq6KIMJBc4M8skwKAyChia2KPH3P24
2061 YU8/lFhOTj152OPOHuXMU4g48vCRiN/9rZGLMdS4csUu1JzDgxuipOMDHMKsAwEnq/ByzTrrZMNO
2062 OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
2063 xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
2064 gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
2066 # The base64 data in the string above was generated by the following procedure:
2069 # print base64.encodestring(open("fetchmail.gif", "rb").read())
2073 (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV", ["help",
2075 dump = rcfile = None;
2076 for (switch, val) in options:
2077 if (switch == '-d'):
2079 elif (switch == '-f'):
2081 elif (switch == '-h' or switch == '--help'):
2083 Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|--help|-V|--version}
2084 -d - dump configuration (for debugging)
2085 -f fmrc - read alternate fetchmailrc file
2086 --help, -h - print this help text and quit
2087 --version, -V - print fetchmailconf version and quit
2090 elif (switch == '-V' or switch == '--version'):
2091 print "fetchmailconf %s" % version
2093 Copyright (C) 1997 - 2003 Eric S. Raymond
2094 Copyright (C) 2005, 2006, 2008, 2009 Matthias Andree
2095 fetchmailconf comes with ABSOLUTELY NO WARRANTY. This is free software, you are
2096 welcome to redistribute it under certain conditions. Please see the file
2097 COPYING in the source or documentation directory for details."""
2100 # Get client host's FQDN
2101 hostname = socket.gethostbyaddr(socket.gethostname())[0]
2104 ConfigurationDefaults = Configuration()
2105 ServerDefaults = Server()
2106 UserDefaults = User()
2108 # Read the existing configuration. We set the umask to 077 to make sure
2109 # that group & other read/write permissions are shut off -- we wouldn't
2110 # want crackers to snoop password information out of the tempfile.
2111 tmpfile = tempfile.mktemp()
2113 cmd = "umask 077 && fetchmail </dev/null -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2115 cmd = "umask 077 && fetchmail </dev/null --configdump --nosyslog >" + tmpfile
2120 print "`" + cmd + "' run failure, status " + `s`
2123 print "Unknown error while running fetchmail --configdump"
2130 print "Can't read configuration output of fetchmail --configdump."
2136 # The tricky part -- initializing objects from the configuration global
2137 # `Configuration' is the top level of the object tree we're going to mung.
2138 # The dictmembers list is used to track the set of fields the dictionary
2139 # contains; in particular, we can use it to tell whether things like the
2140 # monitor, interface, ssl, sslkey, or sslcert fields are present.
2142 Fetchmailrc = Configuration()
2143 copy_instance(Fetchmailrc, fetchmailrc)
2144 Fetchmailrc.servers = [];
2145 for server in fetchmailrc['servers']:
2147 copy_instance(Newsite, server)
2148 Fetchmailrc.servers.append(Newsite)
2150 for user in server['users']:
2152 copy_instance(Newuser, user)
2153 Newsite.users.append(Newuser)
2155 # We may want to display the configuration and quit
2157 print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2159 # The theory here is that -f alone sets the rcfile location,
2160 # but -d and -f together mean the new configuration should go to stdout.
2161 if not rcfile and not dump:
2162 rcfile = os.environ["HOME"] + "/.fetchmailrc"
2164 # OK, now run the configuration edit
2165 root = MainWindow(rcfile)
2168 # The following sets edit modes for GNU EMACS