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
8 version = "1.55 $Revision$"
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.users = [] # List of user entries for site
108 ('pollname', 'String'),
110 ('active', 'Boolean'),
112 ('protocol', 'String'),
113 ('service', 'String'),
117 ('envelope', 'String'),
119 ('qvirtual', 'String'),
122 # leave localdomains out
123 ('interface', 'String'),
124 ('monitor', 'String'),
125 ('plugin', 'String'),
126 ('plugout', 'String'),
127 ('esmtpname', 'String'),
128 ('esmtppassword', 'String'),
129 ('principal', 'String'),
130 ('tracepolls','Boolean'))
132 def dump(self, folded):
134 if self.active: res = res + "poll"
135 else: res = res + "skip"
136 res = res + (" " + self.pollname)
138 res = res + (" via " + str(self.via) + "\n");
139 if self.protocol != ServerDefaults.protocol:
140 res = res + " with proto " + self.protocol
141 if self.service and self.protocol and self.service != defaultports[self.protocol] and defaultports[self.protocol] and self.service != ianaservices[defaultports[self.protocol]]:
142 res = res + " service " + self.service
143 if self.timeout != ServerDefaults.timeout:
144 res = res + " timeout " + `self.timeout`
145 if self.interval != ServerDefaults.interval:
146 res = res + " interval " + `self.interval`
147 if self.envelope != ServerDefaults.envelope or self.envskip != ServerDefaults.envskip:
149 res = res + " envelope " + `self.envskip` + " " + self.envelope
151 res = res + " envelope " + self.envelope
153 res = res + (" qvirtual " + str(self.qvirtual) + "\n");
154 if self.auth != ServerDefaults.auth:
155 res = res + " auth " + self.auth
156 if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl:
157 res = res + " and options"
158 if self.dns != ServerDefaults.dns:
159 res = res + flag2str(self.dns, 'dns')
160 if self.uidl != ServerDefaults.uidl:
161 res = res + flag2str(self.uidl, 'uidl')
162 if folded: res = res + "\n "
163 else: res = res + " "
169 if self.aka and self.localdomains: res = res + " "
170 if self.localdomains:
171 res = res + ("localdomains")
172 for x in self.localdomains:
174 if (self.aka or self.localdomains):
181 res = res + "tracepolls\n"
184 res = res + " interface " + str(self.interface)
186 res = res + " monitor " + str(self.monitor)
188 res = res + " plugin " + `self.plugin`
190 res = res + " plugout " + `self.plugout`
192 res = res + " principal " + `self.principal`
194 res = res + " esmtpname " + `self.esmtpname`
195 if self.esmtppassword:
196 res = res + " esmtppassword " + `self.esmtppassword`
197 if self.interface or self.monitor or self.principal or self.plugin or self.plugout:
201 if res[-1] == " ": res = res[0:-1]
203 for user in self.users:
204 res = res + repr(user)
208 def __delitem__(self, name):
209 for ui in range(len(self.users)):
210 if self.users[ui].remote == name:
215 return self.dump(TRUE)
218 return "[Server: " + self.dump(FALSE) + "]"
222 if os.environ.has_key("USER"):
223 self.remote = os.environ["USER"] # Remote username
224 elif os.environ.has_key("LOGNAME"):
225 self.remote = os.environ["LOGNAME"]
227 print "Can't get your username!"
229 self.localnames = [self.remote,]# Local names
230 self.password = None # Password for mail account access
231 self.mailboxes = [] # Remote folders to retrieve from
232 self.smtphunt = [] # Hosts to forward to
233 self.fetchdomains = [] # Domains to fetch from
234 self.smtpaddress = None # Append this to MAIL FROM line
235 self.smtpname = None # Use this for RCPT TO
236 self.preconnect = None # Connection setup
237 self.postconnect = None # Connection wrapup
238 self.mda = None # Mail Delivery Agent
239 self.bsmtp = None # BSMTP output file
240 self.lmtp = FALSE # Use LMTP rather than SMTP?
241 self.antispam = "" # Listener's spam-block code
242 self.keep = FALSE # Keep messages
243 self.flush = FALSE # Flush messages
244 self.limitflush = FALSE # Flush oversized messages
245 self.fetchall = FALSE # Fetch old messages
246 self.rewrite = TRUE # Rewrite message headers
247 self.forcecr = FALSE # Force LF -> CR/LF
248 self.stripcr = FALSE # Strip CR
249 self.pass8bits = FALSE # Force BODY=7BIT
250 self.mimedecode = FALSE # Undo MIME armoring
251 self.dropstatus = FALSE # Drop incoming Status lines
252 self.dropdelivered = FALSE # Drop incoming Delivered-To lines
253 self.idle = FALSE # IDLE after poll
254 self.limit = 0 # Message size limit
255 self.warnings = 3600 # Size warning interval (see tunable.h)
256 self.fetchlimit = 0 # Max messages fetched per batch
257 self.fetchsizelimit = 100 # Max message sizes fetched per transaction
258 self.fastuidl = 4 # Do fast uidl 3 out of 4 times
259 self.batchlimit = 0 # Max message forwarded per batch
260 self.expunge = 0 # Interval between expunges (IMAP)
261 self.ssl = 0 # Enable Seccure Socket Layer
262 self.sslkey = None # SSL key filename
263 self.sslcert = None # SSL certificate filename
264 self.sslproto = None # Force SSL?
265 self.sslcertck = 0 # Enable strict SSL cert checking
266 self.sslcertpath = None # Path to trusted certificates
267 self.sslcommonname = None # SSL CommonName to expect
268 self.sslfingerprint = None # SSL key fingerprint to check
269 self.properties = None # Extension properties
271 ('remote', 'String'),
272 # leave out mailboxes and localnames
273 ('password', 'String'),
274 # Leave out smtphunt, fetchdomains
275 ('smtpaddress', 'String'),
276 ('smtpname', 'String'),
277 ('preconnect', 'String'),
278 ('postconnect', 'String'),
282 ('antispam', 'String'),
284 ('flush', 'Boolean'),
285 ('limitflush', 'Boolean'),
286 ('fetchall', 'Boolean'),
287 ('rewrite', 'Boolean'),
288 ('forcecr', 'Boolean'),
289 ('stripcr', 'Boolean'),
290 ('pass8bits', 'Boolean'),
291 ('mimedecode', 'Boolean'),
292 ('dropstatus', 'Boolean'),
293 ('dropdelivered', 'Boolean'),
297 ('fetchlimit', 'Int'),
298 ('fetchsizelimit', 'Int'),
300 ('batchlimit', 'Int'),
303 ('sslkey', 'String'),
304 ('sslcert', 'String'),
305 ('sslcertck', 'Boolean'),
306 ('sslcertpath', 'String'),
307 ('sslcommonname', 'String'),
308 ('sslfingerprint', 'String'),
309 ('properties', 'String'))
313 res = res + "user " + `self.remote` + " there ";
315 res = res + "with password " + `self.password` + " "
318 for x in self.localnames:
319 res = res + " " + `x`
321 if (self.keep != UserDefaults.keep
322 or self.flush != UserDefaults.flush
323 or self.limitflush != UserDefaults.limitflush
324 or self.fetchall != UserDefaults.fetchall
325 or self.rewrite != UserDefaults.rewrite
326 or self.forcecr != UserDefaults.forcecr
327 or self.stripcr != UserDefaults.stripcr
328 or self.pass8bits != UserDefaults.pass8bits
329 or self.mimedecode != UserDefaults.mimedecode
330 or self.dropstatus != UserDefaults.dropstatus
331 or self.dropdelivered != UserDefaults.dropdelivered
332 or self.idle != UserDefaults.idle):
333 res = res + " options"
334 if self.keep != UserDefaults.keep:
335 res = res + flag2str(self.keep, 'keep')
336 if self.flush != UserDefaults.flush:
337 res = res + flag2str(self.flush, 'flush')
338 if self.limitflush != UserDefaults.limitflush:
339 res = res + flag2str(self.limitflush, 'limitflush')
340 if self.fetchall != UserDefaults.fetchall:
341 res = res + flag2str(self.fetchall, 'fetchall')
342 if self.rewrite != UserDefaults.rewrite:
343 res = res + flag2str(self.rewrite, 'rewrite')
344 if self.forcecr != UserDefaults.forcecr:
345 res = res + flag2str(self.forcecr, 'forcecr')
346 if self.stripcr != UserDefaults.stripcr:
347 res = res + flag2str(self.stripcr, 'stripcr')
348 if self.pass8bits != UserDefaults.pass8bits:
349 res = res + flag2str(self.pass8bits, 'pass8bits')
350 if self.mimedecode != UserDefaults.mimedecode:
351 res = res + flag2str(self.mimedecode, 'mimedecode')
352 if self.dropstatus != UserDefaults.dropstatus:
353 res = res + flag2str(self.dropstatus, 'dropstatus')
354 if self.dropdelivered != UserDefaults.dropdelivered:
355 res = res + flag2str(self.dropdelivered, 'dropdelivered')
356 if self.idle != UserDefaults.idle:
357 res = res + flag2str(self.idle, 'idle')
358 if self.limit != UserDefaults.limit:
359 res = res + " limit " + `self.limit`
360 if self.warnings != UserDefaults.warnings:
361 res = res + " warnings " + `self.warnings`
362 if self.fetchlimit != UserDefaults.fetchlimit:
363 res = res + " fetchlimit " + `self.fetchlimit`
364 if self.fetchsizelimit != UserDefaults.fetchsizelimit:
365 res = res + " fetchsizelimit " + `self.fetchsizelimit`
366 if self.fastuidl != UserDefaults.fastuidl:
367 res = res + " fastuidl " + `self.fastuidl`
368 if self.batchlimit != UserDefaults.batchlimit:
369 res = res + " batchlimit " + `self.batchlimit`
370 if self.ssl and self.ssl != UserDefaults.ssl:
371 res = res + flag2str(self.ssl, 'ssl')
372 if self.sslkey and self.sslkey != UserDefaults.sslkey:
373 res = res + " sslkey " + `self.sslkey`
374 if self.sslcert and self.sslcert != UserDefaults.sslcert:
375 res = res + " sslcert " + `self.sslcert`
376 if self.sslproto and self.sslproto != UserDefaults.sslproto:
377 res = res + " sslproto " + `self.sslproto`
378 if self.sslcertck and self.sslcertck != UserDefaults.sslcertck:
379 res = res + flag2str(self.sslcertck, 'sslcertck')
380 if self.sslcertpath and self.sslcertpath != UserDefaults.sslcertpath:
381 res = res + " sslcertpath " + `self.sslcertpath`
382 if self.sslcommonname and self.sslcommonname != UserDefaults.sslcommonname:
383 res = res + " sslcommonname " + `self.sslcommonname`
384 if self.sslfingerprint and self.sslfingerprint != UserDefaults.sslfingerprint:
385 res = res + " sslfingerprint " + `self.sslfingerprint`
386 if self.expunge != UserDefaults.expunge:
387 res = res + " expunge " + `self.expunge`
389 trimmed = self.smtphunt;
390 if trimmed != [] and trimmed[len(trimmed) - 1] == "localhost":
391 trimmed = trimmed[0:len(trimmed) - 1]
392 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
393 trimmed = trimmed[0:len(trimmed) - 1]
395 res = res + " smtphost "
399 trimmed = self.fetchdomains
400 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
401 trimmed = trimmed[0:len(trimmed) - 1]
403 res = res + " fetchdomains "
408 res = res + " folder"
409 for x in self.mailboxes:
410 res = res + ' "%s"' % x
412 for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'):
413 if getattr(self, fld):
414 res = res + " %s %s\n" % (fld, `getattr(self, fld)`)
415 if self.lmtp != UserDefaults.lmtp:
416 res = res + flag2str(self.lmtp, 'lmtp')
417 if self.antispam != UserDefaults.antispam:
418 res = res + " antispam " + self.antispam + "\n"
422 return "[User: " + repr(self) + "]"
428 # IANA port assignments and bogus 1109 entry
429 ianaservices = {"pop2":109,
436 # fetchmail protocol to IANA service name
437 defaultports = {"auto":None,
446 authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp",
450 'title' : 'List Selection Help',
451 'banner': 'List Selection',
453 You must select an item in the list box (by clicking on it).
456 def flag2str(value, string):
457 # make a string representation of a .fetchmailrc flag or negated flag
461 if value == FALSE: str = str + ("no ")
465 class LabeledEntry(Frame):
466 # widget consisting of entry field with caption to left
467 def bind(self, key, action):
468 self.E.bind(key, action)
471 def __init__(self, Master, text, textvar, lwidth, ewidth=12):
472 Frame.__init__(self, Master)
473 self.L = Label(self, {'text':text, 'width':lwidth, 'anchor':'w'})
474 self.E = Entry(self, {'textvar':textvar, 'width':ewidth})
475 self.L.pack({'side':'left'})
476 self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
478 def ButtonBar(frame, legend, ref, alternatives, depth, command):
479 # array of radio buttons, caption to left, picking from a string list
481 width = (len(alternatives)+1) / depth;
482 Label(bar, text=legend).pack(side=LEFT)
483 for column in range(width):
484 subframe = Frame(bar)
485 for row in range(depth):
486 ind = width * row + column
487 if ind < len(alternatives):
488 Radiobutton(subframe,
489 {'text':alternatives[ind],
491 'value':alternatives[ind],
492 'command':command}).pack(side=TOP, anchor=W)
494 # This is just a spacer
495 Radiobutton(subframe,
496 {'text':" ",'state':DISABLED}).pack(side=TOP, anchor=W)
497 subframe.pack(side=LEFT)
501 def helpwin(helpdict):
502 # help message window with a self-destruct button
504 helpwin.title(helpdict['title'])
505 helpwin.iconname(helpdict['title'])
506 Label(helpwin, text=helpdict['banner']).pack()
507 textframe = Frame(helpwin)
508 scroll = Scrollbar(textframe)
509 helpwin.textwidget = Text(textframe, setgrid=TRUE)
510 textframe.pack(side=TOP, expand=YES, fill=BOTH)
511 helpwin.textwidget.config(yscrollcommand=scroll.set)
512 helpwin.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
513 scroll.config(command=helpwin.textwidget.yview)
514 scroll.pack(side=RIGHT, fill=BOTH)
515 helpwin.textwidget.insert(END, helpdict['text']);
516 Button(helpwin, text='Done',
517 command=lambda x=helpwin: x.destroy(), bd=2).pack()
518 textframe.pack(side=TOP)
520 def make_icon_window(base, image):
522 # Some older pythons will error out on this
523 icon_image = PhotoImage(data=image)
524 icon_window = Toplevel()
525 Label(icon_window, image=icon_image, bg='black').pack()
526 base.master.iconwindow(icon_window)
527 # Avoid TkInter brain death. PhotoImage objects go out of
528 # scope when the enclosing function returns. Therefore
529 # we have to explicitly link them to something.
530 base.keepalive.append(icon_image)
534 class ListEdit(Frame):
535 # edit a list of values (duplicates not allowed) with a supplied editor hook
536 def __init__(self, newlegend, list, editor, deletor, master, helptxt):
538 self.deletor = deletor
541 # Set up a widget to accept new elements
542 self.newval = StringVar(master)
543 newwin = LabeledEntry(master, newlegend, self.newval, '12')
544 newwin.bind('<Double-1>', self.handleNew)
545 newwin.bind('<Return>', self.handleNew)
546 newwin.pack(side=TOP, fill=X, anchor=E)
548 # Edit the existing list
549 listframe = Frame(master)
550 scroll = Scrollbar(listframe)
551 self.listwidget = Listbox(listframe, height=0, selectmode='browse')
554 self.listwidget.insert(END, x)
555 listframe.pack(side=TOP, expand=YES, fill=BOTH)
556 self.listwidget.config(yscrollcommand=scroll.set)
557 self.listwidget.pack(side=LEFT, expand=YES, fill=BOTH)
558 scroll.config(command=self.listwidget.yview)
559 scroll.pack(side=RIGHT, fill=BOTH)
560 self.listwidget.config(selectmode=SINGLE, setgrid=TRUE)
561 self.listwidget.bind('<Double-1>', self.handleList);
562 self.listwidget.bind('<Return>', self.handleList);
566 Button(bf, text='Edit', command=self.editItem).pack(side=LEFT)
567 Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT)
569 self.helptxt = helptxt
570 Button(bf, text='Help', fg='blue',
571 command=self.help).pack(side=RIGHT)
575 helpwin(self.helptxt)
577 def handleList(self, event):
580 def handleNew(self, event):
581 item = self.newval.get()
583 entire = self.listwidget.get(0, self.listwidget.index('end'));
584 if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
585 self.listwidget.insert('end', item)
586 if self.list != None: self.list.append(item)
588 apply(self.editor, (item,))
592 select = self.listwidget.curselection()
597 if index and self.editor:
598 label = self.listwidget.get(index);
600 apply(self.editor, (label,))
602 def deleteItem(self):
603 select = self.listwidget.curselection()
607 index = string.atoi(select[0])
608 label = self.listwidget.get(index);
609 self.listwidget.delete(index)
610 if self.list != None:
612 if self.deletor != None:
613 apply(self.deletor, (label,))
615 def ConfirmQuit(frame, context):
618 text = 'Really quit ' + context + ' without saving?',
620 strings = ('Yes', 'No'),
624 def dispose_window(master, legend, help, savelegend='OK'):
625 dispose = Frame(master, relief=RAISED, bd=5)
626 Label(dispose, text=legend).pack(side=TOP,pady=10)
627 Button(dispose, text=savelegend, fg='blue',
628 command=master.save).pack(side=LEFT)
629 Button(dispose, text='Quit', fg='blue',
630 command=master.nosave).pack(side=LEFT)
631 Button(dispose, text='Help', fg='blue',
632 command=lambda x=help: helpwin(x)).pack(side=RIGHT)
637 # Common methods for Tkinter widgets -- deals with Tkinter declaration
638 def post(self, widgetclass, field):
639 for x in widgetclass.typemap:
640 if x[1] == 'Boolean':
641 setattr(self, x[0], BooleanVar(self))
642 elif x[1] == 'String':
643 setattr(self, x[0], StringVar(self))
645 setattr(self, x[0], IntVar(self))
646 source = getattr(getattr(self, field), x[0])
648 getattr(self, x[0]).set(source)
650 def fetch(self, widgetclass, field):
651 for x in widgetclass.typemap:
652 setattr(getattr(self, field), x[0], getattr(self, x[0]).get())
655 # First, code to set the global fetchmail run controls.
658 configure_novice_help = {
659 'title' : 'Fetchmail novice configurator help',
660 'banner': 'Novice configurator help',
662 In the `Novice Configurator Controls' panel, you can:
664 Press `Save' to save the new fetchmail configuration you have created.
665 Press `Quit' to exit without saving.
666 Press `Help' to bring up this help message.
668 In the `Novice Configuration' panels, you will set up the basic data
669 needed to create a simple fetchmail setup. These include:
671 1. The name of the remote site you want to query.
673 2. Your login name on that site.
675 3. Your password on that site.
677 4. A protocol to use (POP, IMAP, ETRN, etc.)
679 5. A poll interval in seconds.
680 If 0, fetchmail will run in the foreground once when started.
681 If > 0, fetchmail will run in the background and start a new poll
682 cycle after the interval has elapsed.
684 6. Options to fetch old messages as well as new, or to suppress
685 deletion of fetched message.
687 The novice-configuration code will assume that you want to forward mail
688 to a local sendmail listener with no special options.
691 configure_expert_help = {
692 'title' : 'Fetchmail expert configurator help',
693 'banner': 'Expert configurator help',
695 In the `Expert Configurator Controls' panel, you can:
697 Press `Save' to save the new fetchmail configuration you have edited.
698 Press `Quit' to exit without saving.
699 Press `Help' to bring up this help message.
701 In the `Run Controls' panel, you can set the following options that
702 control how fetchmail runs:
705 Number of seconds to wait between polls in the background.
706 If zero, fetchmail will run in foreground.
709 If empty, emit progress and error messages to stderr.
710 Otherwise this gives the name of the files to write to.
711 This field is ignored if the "Log to syslog?" option is on.
714 If empty, store seen-message IDs in .fetchids under user's home
715 directory. If nonempty, use given file name.
718 Who to send multidrop mail to as a last resort if no address can
719 be matched. Normally empty; in this case, fetchmail treats the
720 invoking user as the address of last resort unless that user is
721 root. If that user is root, fetchmail sends to `postmaster'.
724 If this option is on (the default) error mail goes to the sender.
725 Otherwise it goes to the postmaster.
728 If this option is on, spam bounces are sent to the sender or
729 postmaster (depending on the "Bounces to sender?" option. Otherwise,
730 spam bounces are not sent (the default).
733 If this option is on, permanent delivery errors are treated as
734 temporary, i. e. mail is kept on the upstream server. Useful
735 during testing and after configuration changes, and on by
737 If this option is off, permanent delivery errors delete
738 undeliverable mail from the upstream.
741 If false (the default) fetchmail generates a Received line into
742 each message and generates a HELO from the machine it is running on.
743 If true, fetchmail generates no Received line and HELOs as if it were
746 In the `Remote Mail Configurations' panel, you can:
748 1. Enter the name of a new remote mail server you want fetchmail to query.
750 To do this, simply enter a label for the poll configuration in the
751 `New Server:' box. The label should be a DNS name of the server (unless
752 you are using ssh or some other tunneling method and will fill in the `via'
753 option on the site configuration screen).
755 2. Change the configuration of an existing site.
757 To do this, find the site's label in the listbox and double-click it.
758 This will take you to a site configuration dialogue.
762 class ConfigurationEdit(Frame, MyWidget):
763 def __init__(self, configuration, outfile, master, onexit):
765 self.configuration = configuration
766 self.outfile = outfile
767 self.container = master
769 ConfigurationEdit.mode_to_help = {
770 'novice':configure_novice_help, 'expert':configure_expert_help
773 def server_edit(self, sitename):
774 self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
776 def server_delete(self, sitename):
778 for user in self.subwidgets.keys():
780 del self.configuration[sitename]
784 def edit(self, mode):
786 Frame.__init__(self, self.container)
787 self.master.title('fetchmail ' + self.mode + ' configurator');
788 self.master.iconname('fetchmail ' + self.mode + ' configurator');
789 self.master.protocol('WM_DELETE_WINDOW', self.nosave)
790 self.keepalive = [] # Use this to anchor the PhotoImage object
791 make_icon_window(self, fetchmail_icon)
793 self.post(Configuration, 'configuration')
796 'Configurator ' + self.mode + ' Controls',
797 ConfigurationEdit.mode_to_help[self.mode],
800 gf = Frame(self, relief=RAISED, bd = 5)
802 text='Fetchmail Run Controls',
803 bd=2).pack(side=TOP, pady=10)
808 if self.mode != 'novice':
810 log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
811 log.pack(side=RIGHT, anchor=E)
813 # Set the poll interval
814 de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
815 de.pack(side=RIGHT, anchor=E)
820 if self.mode != 'novice':
823 {'text':'Bounces to sender?',
824 'variable':self.bouncemail,
825 'relief':GROOVE}).pack(side=LEFT, anchor=W)
830 {'text':'Send spam bounces?',
831 'variable':self.spambounce,
832 'relief':GROOVE}).pack(side=LEFT, anchor=W)
837 {'text':'Treat permanent errors as temporary?',
838 'variable':self.softbounce,
839 'relief':GROOVE}).pack(side=LEFT, anchor=W)
844 {'text':'Log to syslog?',
845 'variable':self.syslog,
846 'relief':GROOVE}).pack(side=LEFT, anchor=W)
847 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
848 log.pack(side=RIGHT, anchor=E)
852 {'text':'Invisible mode?',
853 'variable':self.invisible,
854 'relief':GROOVE}).pack(side=LEFT, anchor=W)
856 log = LabeledEntry(gf, ' Idfile:', self.idfile, '14')
857 log.pack(side=RIGHT, anchor=E)
861 # Expert mode allows us to edit multiple sites
862 lf = Frame(self, relief=RAISED, bd=5)
864 text='Remote Mail Server Configurations',
865 bd=2).pack(side=TOP, pady=10)
866 ListEdit('New Server:',
867 map(lambda x: x.pollname, self.configuration.servers),
868 lambda site, self=self: self.server_edit(site),
869 lambda site, self=self: self.server_delete(site),
874 for sitename in self.subwidgets.keys():
875 self.subwidgets[sitename].destruct()
876 self.master.destroy()
880 if ConfirmQuit(self, self.mode + " configuration editor"):
884 for sitename in self.subwidgets.keys():
885 self.subwidgets[sitename].save()
886 self.fetch(Configuration, 'configuration')
890 elif not os.path.isfile(self.outfile) or Dialog(self,
891 title = 'Overwrite existing run control file?',
892 text = 'Really overwrite existing run control file?',
894 strings = ('Yes', 'No'),
895 default = 1).num == 0:
897 os.rename(self.outfile, self.outfile + "~")
898 # Pre-1.5.2 compatibility...
901 oldumask = os.umask(077)
902 fm = open(self.outfile, 'w')
907 os.chmod(self.outfile, 0600)
908 fm.write("# Configuration created %s by fetchmailconf %s\n" % (time.ctime(time.time()), version))
909 fm.write(`self.configuration`)
915 # Server editing stuff.
918 'title' : 'Remote site help',
919 'banner': 'Remote sites',
921 When you add a site name to the list here,
922 you initialize an entry telling fetchmail
923 how to poll a new site.
925 When you select a sitename (by double-
926 clicking it, or by single-clicking to
927 select and then clicking the Edit button),
928 you will open a window to configure that
933 'title' : 'Server options help',
934 'banner': 'Server Options',
936 The server options screen controls fetchmail
937 options that apply to one of your mailservers.
939 Once you have a mailserver configuration set
940 up as you like it, you can select `OK' to
941 store it in the server list maintained in
942 the main configuration window.
944 If you wish to discard changes to a server
945 configuration, select `Quit'.
949 'title' : 'Run Control help',
950 'banner': 'Run Controls',
952 If the `Poll normally' checkbox is on, the host is polled as part of
953 the normal operation of fetchmail when it is run with no arguments.
954 If it is off, fetchmail will only query this host when it is given as
955 a command-line argument.
957 The `True name of server' box should specify the actual DNS name
958 to query. By default this is the same as the poll name.
960 Normally each host described in the file is queried once each
961 poll cycle. If `Cycles to skip between polls' is greater than 0,
962 that's the number of poll cycles that are skipped between the
963 times this post is actually polled.
965 The `Server timeout' is the number of seconds fetchmail will wait
966 for a reply from the mailserver before concluding it is hung and
971 'title' : 'Protocol and Port help',
972 'banner': 'Protocol and Port',
974 These options control the remote-mail protocol
975 and TCP/IP service port used to query this
978 If you click the `Probe for supported protocols'
979 button, fetchmail will try to find you the most
980 capable server on the selected host (this will
981 only work if you're conncted to the Internet).
982 The probe only checks for ordinary IMAP and POP
983 protocols; fortunately these are the most
984 frequently supported.
986 The `Protocol' button bar offers you a choice of
987 all the different protocols available. The `auto'
988 protocol is the default mode; it probes the host
989 ports for POP3 and IMAP to see if either is
992 Normally the TCP/IP service port to use is
993 dictated by the protocol choice. The `Service'
994 field (only present in expert mode) lets you
995 set a non-standard service (port).
999 'title' : 'Security option help',
1000 'banner': 'Security',
1002 The 'authorization mode' allows you to choose the
1003 mode that fetchmail uses to log in to your server. You
1004 can usually leave this at 'any', but you will have to pick
1005 'NTLM' and 'MSN' manually for the nonce.
1007 The 'interface' option allows you to specify a range
1008 of IP addresses to monitor for activity. If these
1009 addresses are not active, fetchmail will not poll.
1010 Specifying this may protect you from a spoofing attack
1011 if your client machine has more than one IP gateway
1012 address and some of the gateways are to insecure nets.
1014 The `monitor' option, if given, specifies the only
1015 device through which fetchmail is permitted to connect
1016 to servers. This option may be used to prevent
1017 fetchmail from triggering an expensive dial-out if the
1018 interface is not already active.
1020 The `interface' and `monitor' options are available
1021 only for Linux and freeBSD systems. See the fetchmail
1022 manual page for details on these.
1024 The ssl option enables SSL communication with a mailserver
1025 supporting Secure Sockets Layer. The sslkey and sslcert options
1026 declare key and certificate files for use with SSL.
1027 The sslcertck option enables strict checking of SSL server
1028 certificates (and sslcertpath gives the trusted certificate
1029 directory). The sslcommonname option helps if the server is
1030 misconfigured and returning "Server CommonName mismatch"
1031 warnings. With sslfingerprint, you can specify a finger-
1032 print the server's key is checked against.
1036 'title' : 'Multidrop option help',
1037 'banner': 'Multidrop',
1039 These options are only useful with multidrop mode.
1040 See the manual page for extended discussion.
1044 'title' : 'User list help',
1045 'banner': 'User list',
1047 When you add a user name to the list here,
1048 you initialize an entry telling fetchmail
1049 to poll the site on behalf of the new user.
1051 When you select a username (by double-
1052 clicking it, or by single-clicking to
1053 select and then clicking the Edit button),
1054 you will open a window to configure the
1055 user's options on that site.
1058 class ServerEdit(Frame, MyWidget):
1059 def __init__(self, host, parent):
1060 self.parent = parent
1062 self.subwidgets = {}
1063 for site in parent.configuration.servers:
1064 if site.pollname == host:
1066 if (self.server == None):
1067 self.server = Server()
1068 self.server.pollname = host
1069 self.server.via = None
1070 parent.configuration.servers.append(self.server)
1072 def edit(self, mode, master=None):
1073 Frame.__init__(self, master)
1075 self.master.title('Fetchmail host ' + self.server.pollname);
1076 self.master.iconname('Fetchmail host ' + self.server.pollname);
1077 self.post(Server, 'server')
1078 self.makeWidgets(self.server.pollname, mode)
1079 self.keepalive = [] # Use this to anchor the PhotoImage object
1080 make_icon_window(self, fetchmail_icon)
1083 # self.wait_window()
1087 for username in self.subwidgets.keys():
1088 self.subwidgets[username].destruct()
1089 del self.parent.subwidgets[self.server.pollname]
1090 self.master.destroy()
1093 if ConfirmQuit(self, 'server option editing'):
1097 self.fetch(Server, 'server')
1098 for username in self.subwidgets.keys():
1099 self.subwidgets[username].save()
1102 def defaultPort(self):
1103 proto = self.protocol.get()
1104 # Callback to reset the port number whenever the protocol type changes.
1105 # We used to only reset the port if it had a default (zero) value.
1106 # This turns out to be a bad idea especially in Novice mode -- if
1107 # you set POP3 and then set IMAP, the port invisibly remained 110.
1108 # Now we reset unconditionally on the theory that if you're setting
1109 # a custom port number you should be in expert mode and playing
1110 # close enough attention to notice this...
1111 self.service.set(defaultports[proto])
1112 if not proto in ("POP3", "APOP", "KPOP"): self.uidl.state = DISABLED
1114 def user_edit(self, username, mode):
1115 self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1117 def user_delete(self, username):
1118 if self.subwidgets.has_key(username):
1119 self.subwidgets[username].destruct()
1120 del self.server[username]
1122 def makeWidgets(self, host, mode):
1123 topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1125 leftwin = Frame(self);
1128 if mode != 'novice':
1129 ctlwin = Frame(leftwin, relief=RAISED, bd=5)
1130 Label(ctlwin, text="Run Controls").pack(side=TOP)
1131 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
1132 LabeledEntry(ctlwin, 'True name of ' + host + ':',
1133 self.via, leftwidth).pack(side=TOP, fill=X)
1134 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
1135 self.interval, leftwidth).pack(side=TOP, fill=X)
1136 LabeledEntry(ctlwin, 'Server timeout (seconds):',
1137 self.timeout, leftwidth).pack(side=TOP, fill=X)
1138 Button(ctlwin, text='Help', fg='blue',
1139 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
1142 # Compute the available protocols from the compile-time options
1143 protolist = ['auto']
1144 if 'pop2' in feature_options:
1145 protolist.append("POP2")
1146 if 'pop3' in feature_options:
1147 protolist = protolist + ["POP3", "APOP", "KPOP"]
1148 if 'sdps' in feature_options:
1149 protolist.append("SDPS")
1150 if 'imap' in feature_options:
1151 protolist.append("IMAP")
1152 if 'etrn' in feature_options:
1153 protolist.append("ETRN")
1154 if 'odmr' in feature_options:
1155 protolist.append("ODMR")
1157 protwin = Frame(leftwin, relief=RAISED, bd=5)
1158 Label(protwin, text="Protocol").pack(side=TOP)
1159 ButtonBar(protwin, '',
1160 self.protocol, protolist, 2,
1162 if mode != 'novice':
1163 LabeledEntry(protwin, 'On server TCP/IP service:',
1164 self.service, leftwidth).pack(side=TOP, fill=X)
1166 Checkbutton(protwin,
1167 text="POP3: track `seen' with client-side UIDLs?",
1168 variable=self.uidl).pack(side=TOP)
1169 Button(protwin, text='Probe for supported protocols', fg='blue',
1170 command=self.autoprobe).pack(side=LEFT)
1171 Button(protwin, text='Help', fg='blue',
1172 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
1173 protwin.pack(fill=X)
1175 userwin = Frame(leftwin, relief=RAISED, bd=5)
1176 Label(userwin, text="User entries for " + host).pack(side=TOP)
1177 ListEdit("New user: ",
1178 map(lambda x: x.remote, self.server.users),
1179 lambda u, m=mode, s=self: s.user_edit(u, m),
1180 lambda u, s=self: s.user_delete(u),
1182 userwin.pack(fill=X)
1184 leftwin.pack(side=LEFT, anchor=N, fill=X);
1186 if mode != 'novice':
1187 rightwin = Frame(self);
1189 mdropwin = Frame(rightwin, relief=RAISED, bd=5)
1190 Label(mdropwin, text="Multidrop options").pack(side=TOP)
1191 LabeledEntry(mdropwin, 'Envelope address header:',
1192 self.envelope, '22').pack(side=TOP, fill=X)
1193 LabeledEntry(mdropwin, 'Envelope headers to skip:',
1194 self.envskip, '22').pack(side=TOP, fill=X)
1195 LabeledEntry(mdropwin, 'Name prefix to strip:',
1196 self.qvirtual, '22').pack(side=TOP, fill=X)
1197 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
1198 variable=self.dns).pack(side=TOP)
1199 Label(mdropwin, text="DNS aliases").pack(side=TOP)
1200 ListEdit("New alias: ", self.server.aka, None, None, mdropwin, None)
1201 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
1202 ListEdit("New domain: ",
1203 self.server.localdomains, None, None, mdropwin, multihelp)
1204 mdropwin.pack(fill=X)
1206 if os_type in ('linux', 'freebsd'):
1207 secwin = Frame(rightwin, relief=RAISED, bd=5)
1208 Label(secwin, text="Security").pack(side=TOP)
1209 # Don't actually let users set this. KPOP sets it implicitly
1210 ButtonBar(secwin, 'Authorization mode:',
1211 self.auth, authlist, 2, None).pack(side=TOP)
1212 if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
1213 LabeledEntry(secwin, 'IP range to check before poll:',
1214 self.interface, leftwidth).pack(side=TOP, fill=X)
1215 if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
1216 LabeledEntry(secwin, 'Interface to monitor:',
1217 self.monitor, leftwidth).pack(side=TOP, fill=X)
1218 # Someday this should handle Kerberos 5 too
1219 if 'kerberos' in feature_options:
1220 LabeledEntry(secwin, 'Principal:',
1221 self.principal, '12').pack(side=TOP, fill=X)
1222 # ESMTP authentication
1223 LabeledEntry(secwin, 'ESMTP name:',
1224 self.esmtpname, '12').pack(side=TOP, fill=X)
1225 LabeledEntry(secwin, 'ESMTP password:',
1226 self.esmtppassword, '12').pack(side=TOP, fill=X)
1227 Button(secwin, text='Help', fg='blue',
1228 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
1231 rightwin.pack(side=LEFT, anchor=N);
1233 def autoprobe(self):
1234 # Note: this only handles case (1) near fetchmail.c:1032
1235 # We're assuming people smart enough to set up ssh tunneling
1236 # won't need autoprobing.
1238 realhost = self.server.via
1240 realhost = self.server.pollname
1242 for protocol in ("IMAP","POP3","POP2"):
1243 service = defaultports[protocol]
1244 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1246 sock.connect((realhost, ianaservices[service]))
1247 greetline = sock.recv(1024)
1253 confwin = Toplevel()
1254 if greetline == None:
1255 title = "Autoprobe of " + realhost + " failed"
1257 Fetchmailconf didn't find any mailservers active.
1258 This could mean the host doesn't support any,
1259 or that your Internet connection is down, or
1260 that the host is so slow that the probe timed
1261 out before getting a response.
1265 # OK, now try to recognize potential problems
1267 if protocol == "POP2":
1268 warnings = warnings + """
1269 It appears you have somehow found a mailserver running only POP2.
1270 Congratulations. Have you considered a career in archaeology?
1272 Unfortunately, stock fetchmail binaries don't include POP2 support anymore.
1273 Unless the first line of your fetchmail -V output includes the string "POP2",
1274 you'll have to build it from sources yourself with the configure
1275 switch --enable-POP2.
1279 ### POP3 servers start here
1281 if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
1282 warnings = warnings + """
1283 This appears to be an old version of the UC Davis POP server. These are
1284 dangerously unreliable (among other problems, they may drop your mailbox
1285 on the floor if your connection is interrupted during the session).
1287 It is strongly recommended that you find a better POP3 server. The fetchmail
1288 FAQ includes pointers to good ones.
1291 if string.find(greetline, "comcast.net") > 0:
1292 warnings = warnings + """
1293 The Comcast Maillennium POP3 server only returns the first 80K of a long
1294 message retrieved with TOP. Its response to RETR is normal, so use the
1298 # Steve VanDevender <stevev@efn.org> writes:
1299 # The only system I have seen this happen with is cucipop-1.31
1300 # under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
1301 # 2.x and probably quite a few other systems. It appears to be a
1302 # bug or bad interaction with the SunOS realloc() -- it turns out
1303 # that internally cucipop does allocate a certain data structure in
1304 # multiples of 16, using realloc() to bump it up to the next
1305 # multiple if it needs more.
1307 # The distinctive symptom is that when there are 16 messages in the
1308 # inbox, you can RETR and DELE all 16 messages successfully, but on
1309 # QUIT cucipop returns something like "-ERR Error locking your
1310 # mailbox" and aborts without updating it.
1312 # The cucipop banner looks like:
1314 # +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1316 if string.find(greetline, "Cubic Circle") > 0:
1317 warnings = warnings + """
1318 I see your server is running cucipop. Better make sure the server box
1319 isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
1320 under that version, and doesn't cope with the result gracefully. Newer
1321 SunOS and Solaris machines run cucipop OK.
1323 Also, some versions of cucipop don't assert an exclusive lock on your
1324 mailbox when it's being queried. This means that if you have more than
1325 one fetchmail query running against the same mailbox, bad things can happen.
1327 if string.find(greetline, "David POP3 Server") > 0:
1328 warnings = warnings + """
1329 This POP3 server is badly broken. You should get rid of it -- and the
1330 brain-dead Microsoft operating system it rode in on.
1333 # The greeting line on the server known to be buggy is:
1334 # +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
1336 if string.find(greetline, "FTGate") > 0:
1337 warnings = warnings + """
1338 This POP server has a weird bug; it says OK twice in response to TOP.
1339 Its response to RETR is normal, so use the `fetchall' option.
1342 if string.find(greetline, " geonet.de") > 0:
1343 warnings = warnings + """
1344 You appear to be using geonet. As of late 2002, the TOP command on
1345 geonet's POP3 is broken. Use the fetchall option.
1348 if string.find(greetline, "OpenMail") > 0:
1349 warnings = warnings + """
1350 You appear to be using some version of HP OpenMail. Many versions of
1351 OpenMail do not process the "TOP" command correctly; the symptom is that
1352 only the header and first line of each message is retrieved. To work
1353 around this bug, turn on `fetchall' on all user entries associated with
1357 if string.find(greetline, "Escape character is") > 0:
1358 warnings = warnings + """
1359 Your greeting line looks like it was written by a fetid pile of
1360 camel dung identified to me as `popa3d written by Solar Designer'.
1361 Beware! The UIDL support in this thing is known to be completely broken,
1362 and other things probably are too.
1365 if string.find(greetline, "MercuryP/NLM v1.48") > 0:
1366 warnings = warnings + """
1367 This is not a POP3 server. It has delusions of being one, but after
1368 RETR all messages are automatically marked to be deleted. The only
1369 way to prevent this is to issue an RSET before leaving the server.
1370 Fetchmail does this, but we suspect this is probably broken in lots
1374 if string.find(greetline, "POP-Max") > 0:
1375 warnings = warnings + """
1376 The Mail Max POP3 server screws up on mail with attachments. It
1377 reports the message size with attachments included, but doesn't
1378 download them on a RETR or TOP (this violates the IMAP RFCs). It also
1379 doesn't implement TOP correctly. You should get rid of it -- and the
1380 brain-dead NT server it rode in on.
1383 if string.find(greetline, "POP3 Server Ready") > 0:
1384 warnings = warnings + """
1385 Some server that uses this greeting line has been observed to choke on
1386 TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
1389 if string.find(greetline, "QPOP") > 0:
1390 warnings = warnings + """
1391 This appears to be a version of Eudora qpopper. That's good. Fetchmail
1392 knows all about qpopper. However, be aware that the 2.53 version of
1393 qpopper does something odd that causes fetchmail to hang with a socket
1394 error on very large messages. This is probably not a fetchmail bug, as
1395 it has been observed with fetchpop. The fix is to upgrade to qpopper
1396 3.0beta or a more recent version. Better yet, switch to IMAP.
1399 if string.find(greetline, " sprynet.com") > 0:
1400 warnings = warnings + """
1401 You appear to be using a SpryNet server. In mid-1999 it was reported that
1402 the SpryNet TOP command marks messages seen. Therefore, for proper error
1403 recovery in the event of a line drop, it is strongly recommended that you
1404 turn on `fetchall' on all user entries associated with this server.
1407 if string.find(greetline, "TEMS POP3") > 0:
1408 warnings = warnings + """
1409 Your POP3 server has "TEMS" in its header line. At least one such
1410 server does not process the "TOP" command correctly; the symptom is
1411 that fetchmail hangs when trying to retrieve mail. To work around
1412 this bug, turn on `fetchall' on all user entries associated with this
1416 if string.find(greetline, " spray.se") > 0:
1417 warnings = warnings + """
1418 Your POP3 server has "spray.se" in its header line. In May 2000 at
1419 least one such server did not process the "TOP" command correctly; the
1420 symptom is that messages are treated as headerless. To work around
1421 this bug, turn on `fetchall' on all user entries associated with this
1425 if string.find(greetline, " usa.net") > 0:
1426 warnings = warnings + """
1427 You appear to be using USA.NET's free mail service. Their POP3 servers
1428 (at least as of the 2.2 version in use mid-1998) are quite flaky, but
1429 fetchmail can compensate. They seem to require that fetchall be switched on
1430 (otherwise you won't necessarily see all your mail, not even new mail).
1431 They also botch the TOP command the fetchmail normally uses for retrieval
1432 (it only retrieves about 10 lines rather than the number specified).
1433 Turning on fetchall will disable the use of TOP.
1435 Therefore, it is strongly recommended that you turn on `fetchall' on all
1436 user entries associated with this server.
1439 if string.find(greetline, " Novonyx POP3") > 0:
1440 warnings = warnings + """
1441 Your mailserver is running Novonyx POP3. This server, at least as of
1442 version 2.17, seems to have problems handling and reporting seen bits.
1443 You may have to use the fetchall option.
1446 if string.find(greetline, " IMS POP3") > 0:
1447 warnings = warnings + """
1448 Some servers issuing the greeting line 'IMS POP3' have been known to
1449 do byte-stuffing incorrectly. This means that if a message you receive
1450 has a . (period) at start of line, fetchmail will become confused and
1451 probably wedge itself. (This bug was recorded on IMS POP3 0.86.)
1455 ### IMAP servers start here
1457 if string.find(greetline, "GroupWise") > 0:
1458 warnings = warnings + """
1459 The Novell GroupWise IMAP server would be better named GroupFoolish;
1460 it is (according to the designer of IMAP) unusably broken. Among
1461 other things, it doesn't include a required content length in its
1462 BODY[TEXT] response.<p>
1464 Fetchmail works around this problem, but we strongly recommend voting
1465 with your dollars for a server that isn't brain-dead. If you stick
1466 with code as shoddy as GroupWise seems to be, you will probably pay
1467 for it with other problems.<p>
1470 if string.find(greetline, "InterChange") > 0:
1471 warnings = warnings + """
1473 The InterChange IMAP server at release levels below 3.61.08 screws up
1474 on mail with attachments. It doesn't fetch them if you give it a
1475 BODY[TEXT] request, though it does if you request RFC822.TEXT.
1476 According to the IMAP RFCs and their maintainer these should be
1477 equivalent -- and we can't drop the BODY[TEXT] form because M$
1478 Exchange (quite legally under RFC2062) rejectsit. The InterChange
1479 folks claim to have fixed this bug in 3.61.08.
1482 if string.find(greetline, "Imail") > 0:
1483 warnings = warnings + """
1484 We've seen a bug report indicating that this IMAP server (at least as of
1485 version 5.0.7) returns an invalid body size for messages with MIME
1486 attachments; the effect is to drop the attachments on the floor. We
1487 recommend you upgrade to a non-broken IMAP server.
1490 if string.find(greetline, "Domino IMAP4") > 0:
1491 warnings = warnings + """
1492 Your IMAP server appears to be Lotus Domino. This server, at least up
1493 to version 4.6.2a, has a bug in its generation of MIME boundaries (see
1494 the details in the fetchmail FAQ). As a result, even MIME aware MUAs
1495 will see attachments as part of the message text. If your Domino server's
1496 POP3 facility is enabled, we recommend you fall back on it.
1500 ### Checks for protocol variants start here
1502 closebrak = string.find(greetline, ">")
1503 if closebrak > 0 and greetline[closebrak+1] == "\r":
1504 warnings = warnings + """
1505 It looks like you could use APOP on this server and avoid sending it your
1506 password in clear. You should talk to the mailserver administrator about
1510 if string.find(greetline, "IMAP2bis") > 0:
1511 warnings = warnings + """
1512 IMAP2bis servers have a minor problem; they can't peek at messages without
1513 marking them seen. If you take a line hit during the retrieval, the
1514 interrupted message may get left on the server, marked seen.
1516 To work around this, it is recommended that you set the `fetchall'
1517 option on all user entries associated with this server, so any stuck
1518 mail will be retrieved next time around.
1520 To fix this bug, upgrade to an IMAP4 server. The fetchmail FAQ includes
1521 a pointer to an open-source implementation.
1524 if string.find(greetline, "IMAP4rev1") > 0:
1525 warnings = warnings + """
1526 I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
1527 remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
1528 has therefore been extremely well tested with this class of server.
1532 warnings = warnings + """
1533 Fetchmail doesn't know anything special about this server type.
1537 # Display success window with warnings
1538 title = "Autoprobe of " + realhost + " succeeded"
1539 confirm = "The " + protocol + " server said:\n\n" + greetline + warnings
1540 self.protocol.set(protocol)
1541 self.service.set(defaultports[protocol])
1542 confwin.title(title)
1543 confwin.iconname(title)
1544 Label(confwin, text=title).pack()
1545 Message(confwin, text=confirm, width=600).pack()
1546 Button(confwin, text='Done',
1547 command=lambda x=confwin: x.destroy(), bd=2).pack()
1550 # User editing stuff
1554 'title' : 'User option help',
1555 'banner': 'User options',
1557 You may use this panel to set options
1558 that may differ between individual
1561 Once you have a user configuration set
1562 up as you like it, you can select `OK' to
1563 store it in the user list maintained in
1564 the site configuration window.
1566 If you wish to discard the changes you have
1567 made to user options, select `Quit'.
1571 'title' : 'Local name help',
1572 'banner': 'Local names',
1574 The local name(s) in a user entry are the
1575 people on the client machine who should
1576 receive mail from the poll described.
1578 Note: if a user entry has more than one
1579 local name, messages will be retrieved
1580 in multidrop mode. This complicates
1581 the configuration issues; see the manual
1582 page section on multidrop mode.
1584 Warning: Be careful with local names
1585 such as foo@bar.com, as that can cause
1586 the mail to be sent to foo@bar.com instead
1587 of sending it to your local system.
1590 class UserEdit(Frame, MyWidget):
1591 def __init__(self, username, parent):
1592 self.parent = parent
1594 for user in parent.server.users:
1595 if user.remote == username:
1597 if self.user == None:
1599 self.user.remote = username
1600 self.user.localnames = [username]
1601 parent.server.users.append(self.user)
1603 def edit(self, mode, master=None):
1604 Frame.__init__(self, master)
1606 self.master.title('Fetchmail user ' + self.user.remote
1607 + ' querying ' + self.parent.server.pollname);
1608 self.master.iconname('Fetchmail user ' + self.user.remote);
1609 self.post(User, 'user')
1610 self.makeWidgets(mode, self.parent.server.pollname)
1611 self.keepalive = [] # Use this to anchor the PhotoImage object
1612 make_icon_window(self, fetchmail_icon)
1615 # self.wait_window()
1619 # Yes, this test can fail -- if you delete the parent window.
1620 if self.parent.subwidgets.has_key(self.user.remote):
1621 del self.parent.subwidgets[self.user.remote]
1622 self.master.destroy()
1625 if ConfirmQuit(self, 'user option editing'):
1630 for x in self.user.localnames: ok = ok + (string.find(x, '@') != -1)
1631 if ok == 0 or Dialog(self,
1632 title = "Really accept an embedded '@' ?",
1633 text = "Local names with an embedded '@', such as in foo@bar "
1634 "might result in your mail being sent to foo@bar.com "
1635 "instead of your local system.\n Are you sure you want "
1636 "a local user name with an '@' in it?",
1637 bitmap = 'question',
1638 strings = ('Yes', 'No'),
1639 default = 1).num == 0:
1640 self.fetch(User, 'user')
1643 def makeWidgets(self, mode, servername):
1644 dispose_window(self,
1645 "User options for " + self.user.remote + " querying " + servername,
1648 if mode != 'novice':
1649 leftwin = Frame(self);
1653 secwin = Frame(leftwin, relief=RAISED, bd=5)
1654 Label(secwin, text="Authentication").pack(side=TOP)
1655 LabeledEntry(secwin, 'Password:',
1656 self.password, '12').pack(side=TOP, fill=X)
1657 secwin.pack(fill=X, anchor=N)
1659 if 'ssl' in feature_options or 'ssl' in dictmembers:
1660 sslwin = Frame(leftwin, relief=RAISED, bd=5)
1661 Checkbutton(sslwin, text="Use SSL?",
1662 variable=self.ssl).pack(side=TOP, fill=X)
1663 LabeledEntry(sslwin, 'SSL key:',
1664 self.sslkey, '14').pack(side=TOP, fill=X)
1665 LabeledEntry(sslwin, 'SSL certificate:',
1666 self.sslcert, '14').pack(side=TOP, fill=X)
1667 Checkbutton(sslwin, text="Check server SSL certificate?",
1668 variable=self.sslcertck).pack(side=TOP, fill=X)
1669 LabeledEntry(sslwin, 'SSL trusted certificate directory:',
1670 self.sslcertpath, '14').pack(side=TOP, fill=X)
1671 LabeledEntry(sslwin, 'SSL CommonName:',
1672 self.sslcommonname, '14').pack(side=TOP, fill=X)
1673 LabeledEntry(sslwin, 'SSL key fingerprint:',
1674 self.sslfingerprint, '14').pack(side=TOP, fill=X)
1675 sslwin.pack(fill=X, anchor=N)
1677 names = Frame(leftwin, relief=RAISED, bd=5)
1678 Label(names, text="Local names").pack(side=TOP)
1679 ListEdit("New name: ",
1680 self.user.localnames, None, None, names, localhelp)
1681 names.pack(fill=X, anchor=N)
1683 if mode != 'novice':
1684 targwin = Frame(leftwin, relief=RAISED, bd=5)
1685 Label(targwin, text="Forwarding Options").pack(side=TOP)
1686 Label(targwin, text="Listeners to forward to").pack(side=TOP)
1687 ListEdit("New listener:",
1688 self.user.smtphunt, None, None, targwin, None)
1689 Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
1690 ListEdit("Domains:",
1691 self.user.fetchdomains, None, None, targwin, None)
1692 LabeledEntry(targwin, 'Append to MAIL FROM line:',
1693 self.smtpaddress, '26').pack(side=TOP, fill=X)
1694 LabeledEntry(targwin, 'Set RCPT To address:',
1695 self.smtpname, '26').pack(side=TOP, fill=X)
1696 LabeledEntry(targwin, 'Connection setup command:',
1697 self.preconnect, '26').pack(side=TOP, fill=X)
1698 LabeledEntry(targwin, 'Connection wrapup command:',
1699 self.postconnect, '26').pack(side=TOP, fill=X)
1700 LabeledEntry(targwin, 'Local delivery agent:',
1701 self.mda, '26').pack(side=TOP, fill=X)
1702 LabeledEntry(targwin, 'BSMTP output file:',
1703 self.bsmtp, '26').pack(side=TOP, fill=X)
1704 LabeledEntry(targwin, 'Listener spam-block codes:',
1705 self.antispam, '26').pack(side=TOP, fill=X)
1706 LabeledEntry(targwin, 'Pass-through properties:',
1707 self.properties, '26').pack(side=TOP, fill=X)
1708 Checkbutton(targwin, text="Use LMTP?",
1709 variable=self.lmtp).pack(side=TOP, fill=X)
1710 targwin.pack(fill=X, anchor=N)
1712 if mode != 'novice':
1713 leftwin.pack(side=LEFT, fill=X, anchor=N)
1714 rightwin = Frame(self)
1718 optwin = Frame(rightwin, relief=RAISED, bd=5)
1719 Label(optwin, text="Processing Options").pack(side=TOP)
1720 Checkbutton(optwin, text="Suppress deletion of messages after reading",
1721 variable=self.keep).pack(side=TOP, anchor=W)
1722 Checkbutton(optwin, text="Fetch old messages as well as new",
1723 variable=self.fetchall).pack(side=TOP, anchor=W)
1724 if mode != 'novice':
1725 Checkbutton(optwin, text="Flush seen messages before retrieval",
1726 variable=self.flush).pack(side=TOP, anchor=W)
1727 Checkbutton(optwin, text="Flush oversized messages before retrieval",
1728 variable=self.limitflush).pack(side=TOP, anchor=W)
1729 Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply",
1730 variable=self.rewrite).pack(side=TOP, anchor=W)
1731 Checkbutton(optwin, text="Force CR/LF at end of each line",
1732 variable=self.forcecr).pack(side=TOP, anchor=W)
1733 Checkbutton(optwin, text="Strip CR from end of each line",
1734 variable=self.stripcr).pack(side=TOP, anchor=W)
1735 Checkbutton(optwin, text="Pass 8 bits even though SMTP says 7BIT",
1736 variable=self.pass8bits).pack(side=TOP, anchor=W)
1737 Checkbutton(optwin, text="Undo MIME armoring on header and body",
1738 variable=self.mimedecode).pack(side=TOP, anchor=W)
1739 Checkbutton(optwin, text="Drop Status lines from forwarded messages",
1740 variable=self.dropstatus).pack(side=TOP, anchor=W)
1741 Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
1742 variable=self.dropdelivered).pack(side=TOP, anchor=W)
1745 if mode != 'novice':
1746 limwin = Frame(rightwin, relief=RAISED, bd=5)
1747 Label(limwin, text="Resource Limits").pack(side=TOP)
1748 LabeledEntry(limwin, 'Message size limit:',
1749 self.limit, '30').pack(side=TOP, fill=X)
1750 LabeledEntry(limwin, 'Size warning interval:',
1751 self.warnings, '30').pack(side=TOP, fill=X)
1752 LabeledEntry(limwin, 'Max messages to fetch per poll:',
1753 self.fetchlimit, '30').pack(side=TOP, fill=X)
1754 LabeledEntry(limwin, 'Max message sizes to fetch per transaction:',
1755 self.fetchsizelimit, '30').pack(side=TOP, fill=X)
1756 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1757 LabeledEntry(limwin, 'Use fast UIDL:',
1758 self.fastuidl, '30').pack(side=TOP, fill=X)
1759 LabeledEntry(limwin, 'Max messages to forward per poll:',
1760 self.batchlimit, '30').pack(side=TOP, fill=X)
1761 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1762 LabeledEntry(limwin, 'Interval between expunges:',
1763 self.expunge, '30').pack(side=TOP, fill=X)
1764 Checkbutton(limwin, text="Idle after each poll (IMAP only)",
1765 variable=self.idle).pack(side=TOP, anchor=W)
1768 if self.parent.server.protocol == 'IMAP':
1769 foldwin = Frame(rightwin, relief=RAISED, bd=5)
1770 Label(foldwin, text="Remote folders (IMAP only)").pack(side=TOP)
1771 ListEdit("New folder:", self.user.mailboxes,
1772 None, None, foldwin, None)
1773 foldwin.pack(fill=X, anchor=N)
1775 if mode != 'novice':
1776 rightwin.pack(side=LEFT)
1782 # Top-level window that offers either novice or expert mode
1783 # (but not both at once; it disappears when one is selected).
1786 class Configurator(Frame):
1787 def __init__(self, outfile, master, onexit, parent):
1788 Frame.__init__(self, master)
1789 self.outfile = outfile
1790 self.onexit = onexit
1791 self.parent = parent
1792 self.master.title('fetchmail configurator');
1793 self.master.iconname('fetchmail configurator');
1795 self.keepalive = [] # Use this to anchor the PhotoImage object
1796 make_icon_window(self, fetchmail_icon)
1798 Message(self, text="""
1799 Use `Novice Configuration' for basic fetchmail setup;
1800 with this, you can easily set up a single-drop connection
1801 to one remote mail server.
1802 """, width=600).pack(side=TOP)
1803 Button(self, text='Novice Configuration',
1804 fg='blue', command=self.novice).pack()
1806 Message(self, text="""
1807 Use `Expert Configuration' for advanced fetchmail setup,
1808 including multiple-site or multidrop connections.
1809 """, width=600).pack(side=TOP)
1810 Button(self, text='Expert Configuration',
1811 fg='blue', command=self.expert).pack()
1813 Message(self, text="""
1814 Or you can just select `Quit' to leave the configurator now and
1815 return to the main panel.
1816 """, width=600).pack(side=TOP)
1817 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1818 master.protocol("WM_DELETE_WINDOW", self.leave)
1821 self.master.destroy()
1822 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1825 self.master.destroy()
1826 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1829 self.master.destroy()
1832 # Run a command in a scrolling text widget, displaying its output
1834 class RunWindow(Frame):
1835 def __init__(self, command, master, parent):
1836 Frame.__init__(self, master)
1837 self.master = master
1838 self.master.title('fetchmail run window');
1839 self.master.iconname('fetchmail run window');
1842 text="Running "+command,
1843 bd=2).pack(side=TOP, pady=10)
1844 self.keepalive = [] # Use this to anchor the PhotoImage object
1845 make_icon_window(self, fetchmail_icon)
1847 # This is a scrolling text window
1848 textframe = Frame(self)
1849 scroll = Scrollbar(textframe)
1850 self.textwidget = Text(textframe, setgrid=TRUE)
1851 textframe.pack(side=TOP, expand=YES, fill=BOTH)
1852 self.textwidget.config(yscrollcommand=scroll.set)
1853 self.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
1854 scroll.config(command=self.textwidget.yview)
1855 scroll.pack(side=RIGHT, fill=BOTH)
1856 textframe.pack(side=TOP)
1858 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1860 self.update() # Draw widget before executing fetchmail
1862 # Always look for a runnable command in the directory we're running in
1863 # first. This avoids some obscure version-skew errors that can occur
1864 # if you pick up an old fetchmail from the standard system locations.
1865 os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"]
1866 child_stdout = os.popen(command + " 2>&1 </dev/null", "r")
1868 ch = child_stdout.read(1)
1871 self.textwidget.insert(END, ch)
1872 self.textwidget.insert(END, "Done.")
1873 self.textwidget.see(END);
1876 self.master.destroy()
1878 # Here's where we choose either configuration or launching
1880 class MainWindow(Frame):
1881 def __init__(self, outfile, master=None):
1882 Frame.__init__(self, master)
1883 self.outfile = outfile
1884 self.master.title('fetchmail launcher');
1885 self.master.iconname('fetchmail launcher');
1888 text='Fetchmailconf ' + version,
1889 bd=2).pack(side=TOP, pady=10)
1890 self.keepalive = [] # Use this to anchor the PhotoImage object
1891 make_icon_window(self, fetchmail_icon)
1894 ## Test icon display with the following:
1895 # icon_image = PhotoImage(data=fetchmail_icon)
1896 # Label(self, image=icon_image).pack(side=TOP, pady=10)
1897 # self.keepalive.append(icon_image)
1899 Message(self, text="""
1900 Use `Configure fetchmail' to tell fetchmail about the remote
1901 servers it should poll (the host name, your username there,
1902 whether to use POP or IMAP, and so forth).
1903 """, width=600).pack(side=TOP)
1904 self.configbutton = Button(self, text='Configure fetchmail',
1905 fg='blue', command=self.configure)
1906 self.configbutton.pack()
1908 Message(self, text="""
1909 Use `Run fetchmail' to run fetchmail with debugging enabled.
1910 This is a good way to test out a new configuration.
1911 """, width=600).pack(side=TOP)
1912 Button(self, text='Run fetchmail',fg='blue', command=self.test).pack()
1914 Message(self, text="""
1915 Use `Run fetchmail' to run fetchmail in foreground.
1916 Progress messages will be shown, but not debug messages.
1917 """, width=600).pack(side=TOP)
1918 Button(self, text='Run fetchmail', fg='blue', command=self.run).pack()
1920 Message(self, text="""
1921 Or you can just select `Quit' to exit the launcher now.
1922 """, width=600).pack(side=TOP)
1923 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1925 def configure(self):
1926 self.configbutton.configure(state=DISABLED)
1927 Configurator(self.outfile, Toplevel(),
1928 lambda self=self: self.configbutton.configure(state=NORMAL),
1931 cmd = "fetchmail -N -d0 --nosyslog -v"
1933 cmd = cmd + " -f " + rcfile
1934 RunWindow(cmd, Toplevel(), self)
1937 cmd = "fetchmail -N -d0"
1939 cmd = cmd + " -f " + rcfile
1940 RunWindow(cmd, Toplevel(), self)
1945 # Functions for turning a dictionary into an instantiated object tree.
1947 def intersect(list1, list2):
1948 # Compute set intersection of lists
1955 def setdiff(list1, list2):
1956 # Compute set difference of lists
1963 def copy_instance(toclass, fromdict):
1964 # Initialize a class object of given type from a conformant dictionary.
1965 for fld in fromdict.keys():
1966 if not fld in dictmembers:
1967 dictmembers.append(fld)
1968 # The `optional' fields are the ones we can ignore for purposes of
1969 # conformability checking; they'll still get copied if they are
1970 # present in the dictionary.
1971 optional = ('interface', 'monitor',
1972 'esmtpname', 'esmtppassword',
1973 'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
1974 'sslcertpath', 'sslcommonname', 'sslfingerprint', 'showdots')
1975 class_sig = setdiff(toclass.__dict__.keys(), optional)
1977 dict_keys = setdiff(fromdict.keys(), optional)
1979 common = intersect(class_sig, dict_keys)
1980 if 'typemap' in class_sig:
1981 class_sig.remove('typemap')
1982 if tuple(class_sig) != tuple(dict_keys):
1983 print "Fields don't match what fetchmailconf expected:"
1984 # print "Class signature: " + `class_sig`
1985 # print "Dictionary keys: " + `dict_keys`
1986 diff = setdiff(class_sig, common)
1988 print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
1989 diff = setdiff(dict_keys, common)
1991 print "Not matched in dictionary keys: " + `diff`
1994 for x in fromdict.keys():
1995 setattr(toclass, x, fromdict[x])
1998 # And this is the main sequence. How it works:
2000 # First, call `fetchmail --configdump' and trap the output in a tempfile.
2001 # This should fill it with a Python initializer for a variable `fetchmailrc'.
2002 # Run execfile on the file to pull fetchmailrc into Python global space.
2003 # You don't want static data, though; you want, instead, a tree of objects
2004 # with the same data members and added appropriate methods.
2006 # This is what the copy_instance function() is for. It tries to copy a
2007 # dictionary field by field into a class, aborting if the class and dictionary
2008 # have different data members (except for any typemap member in the class;
2009 # that one is strictly for use by the MyWidget supperclass).
2011 # Once the object tree is set up, require user to choose novice or expert
2012 # mode and instantiate an edit object for the configuration. Class methods
2013 # will take it all from there.
2015 # Options (not documented because they're for fetchmailconf debuggers only):
2016 # -d: Read the configuration and dump it to stdout before editing. Dump
2017 # the edited result to stdout as well.
2018 # -f: specify the run control file to read.
2020 if __name__ == '__main__':
2022 if not os.environ.has_key("DISPLAY"):
2023 print "fetchmailconf must be run under X"
2026 fetchmail_icon = """
2027 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
2028 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
2029 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
2030 CJR7Y/+UMdbOxnNrY97Ove/Wvd7GrZyEa961jL2Ua9alc86ca7WEUntSKcaMSqVjGNZ7GGM5CNa1
2031 jPfOnN6tc3taMffeve/WtWtaQv/OjGtSMYRzWv/erda1hM6te7WUY62MWs61jP/vzv/ntda9jL2l
2032 czEhAO/n1oyEc//elDEpGEo5EOfexpyUe+/epefevffvxnNrQpyUStbWzsbGvZyclN7ezmNjWv//
2033 5/f33qWllNbWve/vzv//1ufnve/vvf//xvf3vefnrf//taWlc0pKMf//pbW1Y///jKWlWq2tWsbG
2034 Y///c97eUvf3Ut7nc+/3a87We8bOjOfv1u/37/f//621tb3Gxtbn52Nra87n53uUlJTv/6W9xuf3
2035 /8bW3iExOXu11tbv/5TW/4TO/63e/zmt/1KUxlK1/2u9/wCM/73GzrXG1gBKjACE/87e72NzhCkx
2036 OaXO92OMtUql/xCE/wApUtbe57W9xnN7hHut52Ot/xBSnABKnABavQB7/2ul7zF71gBr77XO73Oc
2037 1lqc9yFSlBApSimE/wAYOQApY0J7zlKM5wAxhABS1gBj/6W95wAhWgA5nAAYSgBS7wBS/wBK9wAp
2038 jABC5wBK/wApnABC/wApxgAhtYSMtQAQYwAp/3OE74SMxgAYxlpjvWNr70pS/wgQ3sbGzs7O1qWl
2039 3qWl70pKe0JC/yEhlCkp/wgI/wAAEAAAIQAAKQAAOQAASgAAUgAAYwAAawAAlAAAnAAApQAArQAA
2040 zgAA1gAA5wAA9wAA/0pC/xgQ52Na9ykhe4R7zikhYxgQSjEpQgAAACwAAAAAPAAoAAAI/wABCBxI
2041 sKDBgwgTKiRIYKHDhxARIvgXsaLFhGgEUBSYoKPHjyBDihxJkuS/kwNLqlzJcuTJjQBaypxpEiVH
2042 mjhxvkyZs2fLnTd9ehxAtKjRo0ZrwhTasUsENhYHKOUpk1E3j11mxCBiQVLEBlJd2owp9iVRjwUs
2043 zMCQ5IcLD4saPVxjIKxIoGTvvqSoyFEFGTBeqEhyxAoSFR/USGKVcEGBAwDshsSr1OYTEyhQpJiS
2044 ZcoUKWOQtJDRJFSaggzUGBgoGSTlsjahlPCRIkWVKT16THHRIoqIISBIEUgAYIGBhgRbf3ytFygU
2045 FZp9UDmxQkkMCRwyZKDBQy4aApABhP8XqNwj88l7BVpQYZtF5iArWgwAgGZBq24HU7OeGhQ90PVA
2046 aKZZCiiUMJ9ArSTEwGqR8ZeXfzbV0MIIMQTBwoUdxDDfAm8sZFyDZVEF4UYSKBEBD0+k6IEFPMxH
2047 3FzldXSea+kBgANJSOWIlIMhXZXAXv+c1WM3PuJEpH8iuhbAkv+MdENPRHaTRkdF/jiWSKCAwlKW
2048 VbbkY5Q0LgUSKExgoYBKCjCxARpdltQNKHaUoYAddnR53lVRnJLKBWh4RIEGCZx5FSOv1OLNDUVe
2049 deZHaWiZAB35fIOGNtbEUeV5oGAByzPOrBPFGt3kwEgxITACSg5oLGGLMg60oQAjaNz/oAAcN4Ai
2050 a0c3kHFDK3jYsw4g9sRzBgPLXdkRrBrQ8gsWQUxCCRZX9IJNBQ1s8IgCdeBCzBYN6IBIN2TUsQYd
2051 dXhDBxdzlAHOHHKEcocZdWwDjx8MTCmjsR2FMAstw1RyiSzHqPLALaOwk8QmzCzDCSi0xJKMMk4E
2052 Yw8389iTDT32GAKOPf7YY0Aa9tATyD3w/EGsefgmgEYUtPiChLKWQDMBJtEUgYkzH2RiTgGfTMCI
2053 Mlu0Yc85hNiDziH2tMqOGL72QY47gshLb7Fi4roELcjoQIsxWpDwQyfS2OCJMkLI4YUmyhgxSTVg
2054 CP2FHPZ80UDcieBjStNPD5LPOyZT/y0iHGiMwswexDSzRiRq6KIMJBc4M8skwKAyChia2KPH3P24
2055 YU8/lFhOTj152OPOHuXMU4g48vCRiN/9rZGLMdS4csUu1JzDgxuipOMDHMKsAwEnq/ByzTrrZMNO
2056 OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
2057 xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
2058 gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
2060 # The base64 data in the string above was generated by the following procedure:
2063 # print base64.encodestring(open("fetchmail.gif", "rb").read())
2067 (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV", ["help",
2069 dump = rcfile = None;
2070 for (switch, val) in options:
2071 if (switch == '-d'):
2073 elif (switch == '-f'):
2075 elif (switch == '-h' or switch == '--help'):
2077 Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|--help|-V|--version}
2078 -d - dump configuration (for debugging)
2079 -f fmrc - read alternate fetchmailrc file
2080 --help, -h - print this help text and quit
2081 --version, -V - print fetchmailconf version and quit
2084 elif (switch == '-V' or switch == '--version'):
2085 print "fetchmailconf %s" % version
2087 Copyright (C) 1997 - 2003 Eric S. Raymond
2088 Copyright (C) 2005 - 2006 Matthias Andree
2089 fetchmailconf comes with ABSOLUTELY NO WARRANTY. This is free software, you are
2090 welcome to redistribute it under certain conditions. Please see the file
2091 COPYING in the source or documentation directory for details. """
2094 # Get client host's FQDN
2095 hostname = socket.gethostbyaddr(socket.gethostname())[0]
2098 ConfigurationDefaults = Configuration()
2099 ServerDefaults = Server()
2100 UserDefaults = User()
2102 # Read the existing configuration. We set the umask to 077 to make sure
2103 # that group & other read/write permissions are shut off -- we wouldn't
2104 # want crackers to snoop password information out of the tempfile.
2105 tmpfile = tempfile.mktemp()
2107 cmd = "umask 077 && fetchmail </dev/null -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2109 cmd = "umask 077 && fetchmail </dev/null --configdump --nosyslog >" + tmpfile
2114 print "`" + cmd + "' run failure, status " + `s`
2117 print "Unknown error while running fetchmail --configdump"
2124 print "Can't read configuration output of fetchmail --configdump."
2130 # The tricky part -- initializing objects from the configuration global
2131 # `Configuration' is the top level of the object tree we're going to mung.
2132 # The dictmembers list is used to track the set of fields the dictionary
2133 # contains; in particular, we can use it to tell whether things like the
2134 # monitor, interface, ssl, sslkey, or sslcert fields are present.
2136 Fetchmailrc = Configuration()
2137 copy_instance(Fetchmailrc, fetchmailrc)
2138 Fetchmailrc.servers = [];
2139 for server in fetchmailrc['servers']:
2141 copy_instance(Newsite, server)
2142 Fetchmailrc.servers.append(Newsite)
2144 for user in server['users']:
2146 copy_instance(Newuser, user)
2147 Newsite.users.append(Newuser)
2149 # We may want to display the configuration and quit
2151 print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2153 # The theory here is that -f alone sets the rcfile location,
2154 # but -d and -f together mean the new configuration should go to stdout.
2155 if not rcfile and not dump:
2156 rcfile = os.environ["HOME"] + "/.fetchmailrc"
2158 # OK, now run the configuration edit
2159 root = MainWindow(rcfile)
2162 # The following sets edit modes for GNU EMACS