3 # A GUI configurator for generating fetchmail configuration files.
4 # by Eric S. Raymond, <esr@snark.thyrsus.com>,
5 # Matthias Andree <matthias.andree@gmx.de>
6 # Requires Python with Tkinter, and the following OS-dependent services:
7 # posix, posixpath, socket
12 import sys, time, os, string, socket, getopt, tempfile
15 # Define the data structures the GUIs will be tossing around
19 self.poll_interval = 0 # Normally, run in foreground
20 self.logfile = None # No logfile, initially
21 self.idfile = os.environ["HOME"] + "/.fetchids" # Default idfile, initially
22 self.postmaster = None # No last-resort address, initially
23 self.bouncemail = TRUE # Bounce errors to users
24 self.spambounce = FALSE # Bounce spam errors
25 self.softbounce = TRUE # Treat permanent error as temporary
26 self.properties = None # No exiguous properties
27 self.invisible = FALSE # Suppress Received line & spoof?
28 self.syslog = FALSE # Use syslogd for logging?
29 self.servers = [] # List of included sites
30 Configuration.typemap = (
31 ('poll_interval', 'Int'),
32 ('logfile', 'String'),
34 ('postmaster', 'String'),
35 ('bouncemail', 'Boolean'),
36 ('spambounce', 'Boolean'),
37 ('softbounce', 'Boolean'),
38 ('properties', 'String'),
39 ('syslog', 'Boolean'),
40 ('invisible', 'Boolean'))
44 if self.syslog != ConfigurationDefaults.syslog:
45 str = str + ("set syslog\n")
47 str = str + ("set logfile \"%s\"\n" % (self.logfile,));
48 if self.idfile != ConfigurationDefaults.idfile:
49 str = str + ("set idfile \"%s\"\n" % (self.idfile,));
50 if self.postmaster != ConfigurationDefaults.postmaster:
51 str = str + ("set postmaster \"%s\"\n" % (self.postmaster,));
53 str = str + ("set bouncemail\n")
55 str = str + ("set nobouncemail\n")
57 str = str + ("set spambounce\n")
59 str = str + ("set no spambounce\n")
61 str = str + ("set softbounce\n")
63 str = str + ("set no softbounce\n")
64 if self.properties != ConfigurationDefaults.properties:
65 str = str + ("set properties \"%s\"\n" % (self.properties,));
66 if self.poll_interval > 0:
67 str = str + "set daemon " + `self.poll_interval` + "\n"
69 str = str + ("set invisible\n")
70 for site in self.servers:
71 str = str + repr(site)
74 def __delitem__(self, name):
75 for si in range(len(self.servers)):
76 if self.servers[si].pollname == name:
81 return "[Configuration: " + repr(self) + "]"
85 self.pollname = None # Poll label
86 self.via = None # True name of host
87 self.active = TRUE # Poll status
88 self.interval = 0 # Skip interval
89 self.protocol = 'auto' # Default to auto protocol
90 self.service = None # Service name to use
91 self.uidl = FALSE # Don't use RFC1725 UIDLs by default
92 self.auth = 'any' # Default to password authentication
93 self.timeout = 300 # 5-minute timeout
94 self.envelope = 'Received' # Envelope-address header
95 self.envskip = 0 # Number of envelope headers to skip
96 self.qvirtual = None # Name prefix to strip
97 self.aka = [] # List of DNS aka names
98 self.dns = TRUE # Enable DNS lookup on multidrop
99 self.localdomains = [] # Domains to be considered local
100 self.interface = None # IP address and range
101 self.monitor = None # IP address and range
102 self.plugin = None # Plugin command for going to server
103 self.plugout = None # Plugin command for going to listener
104 self.principal = None # Kerberos principal
105 self.esmtpname = None # ESMTP 2554 name
106 self.esmtppassword = None # ESMTP 2554 password
107 self.tracepolls = FALSE # Add trace-poll info to headers
108 self.badheader = FALSE # Pass messages with bad headers on?
109 self.retrieveerror = 'abort' # Policy when message retrieval errors encountered
110 self.users = [] # List of user entries for site
112 ('pollname', 'String'),
114 ('active', 'Boolean'),
116 ('protocol', 'String'),
117 ('service', 'String'),
121 ('envelope', 'String'),
123 ('qvirtual', 'String'),
126 # leave localdomains out
127 ('interface', 'String'),
128 ('monitor', 'String'),
129 ('plugin', 'String'),
130 ('plugout', 'String'),
131 ('esmtpname', 'String'),
132 ('esmtppassword', 'String'),
133 ('principal', 'String'),
134 ('tracepolls','Boolean'),
135 ('badheader', 'Boolean'),
136 ('retrieveerror', 'String'))
138 def dump(self, folded):
140 if self.active: res = res + "poll"
141 else: res = res + "skip"
142 res = res + (" " + self.pollname)
144 res = res + (" via " + str(self.via) + "\n");
145 if self.protocol != ServerDefaults.protocol:
146 res = res + " with proto " + self.protocol
147 if self.service and self.protocol and self.service != defaultports[self.protocol] and defaultports[self.protocol] and self.service != ianaservices[defaultports[self.protocol]]:
148 res = res + " service " + self.service
149 if self.timeout != ServerDefaults.timeout:
150 res = res + " timeout " + `self.timeout`
151 if self.interval != ServerDefaults.interval:
152 res = res + " interval " + `self.interval`
153 if self.envelope != ServerDefaults.envelope or self.envskip != ServerDefaults.envskip:
155 res = res + " envelope " + `self.envskip` + " " + self.envelope
157 res = res + " envelope " + self.envelope
159 res = res + (" qvirtual " + str(self.qvirtual) + "\n");
160 if self.auth != ServerDefaults.auth:
161 res = res + " auth " + self.auth
162 if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl:
163 res = res + " and options"
164 if self.dns != ServerDefaults.dns:
165 res = res + flag2str(self.dns, 'dns')
166 if self.uidl != ServerDefaults.uidl:
167 res = res + flag2str(self.uidl, 'uidl')
168 if folded: res = res + "\n "
169 else: res = res + " "
175 if self.aka and self.localdomains: res = res + " "
176 if self.localdomains:
177 res = res + ("localdomains")
178 for x in self.localdomains:
180 if (self.aka or self.localdomains):
187 res = res + "tracepolls\n"
190 res = res + " interface " + str(self.interface)
192 res = res + " monitor " + str(self.monitor)
194 res = res + " plugin " + `self.plugin`
196 res = res + " plugout " + `self.plugout`
198 res = res + " principal " + `self.principal`
200 res = res + " esmtpname " + `self.esmtpname`
201 if self.esmtppassword:
202 res = res + " esmtppassword " + `self.esmtppassword`
203 if self.interface or self.monitor or self.principal or self.plugin or self.plugout:
208 res = res + "bad-header accept "
209 if self.retrieveerror == 'continue':
210 res = res + "retrieve-error continue "
211 if self.retrieveerror == 'markseen':
212 res = res + "retrieve-error markseen "
213 if self.badheader or self.retrieveerror != ServerDefaults.retrieveerror:
217 if res[-1] == " ": res = res[0:-1]
219 for user in self.users:
220 res = res + repr(user)
224 def __delitem__(self, name):
225 for ui in range(len(self.users)):
226 if self.users[ui].remote == name:
231 return self.dump(TRUE)
234 return "[Server: " + self.dump(FALSE) + "]"
238 if os.environ.has_key("USER"):
239 self.remote = os.environ["USER"] # Remote username
240 elif os.environ.has_key("LOGNAME"):
241 self.remote = os.environ["LOGNAME"]
243 print "Can't get your username!"
245 self.localnames = [self.remote,]# Local names
246 self.password = None # Password for mail account access
247 self.mailboxes = [] # Remote folders to retrieve from
248 self.smtphunt = [] # Hosts to forward to
249 self.fetchdomains = [] # Domains to fetch from
250 self.smtpaddress = None # Append this to MAIL FROM line
251 self.smtpname = None # Use this for RCPT TO
252 self.preconnect = None # Connection setup
253 self.postconnect = None # Connection wrapup
254 self.mda = None # Mail Delivery Agent
255 self.bsmtp = None # BSMTP output file
256 self.lmtp = FALSE # Use LMTP rather than SMTP?
257 self.antispam = "" # Listener's spam-block code
258 self.keep = FALSE # Keep messages
259 self.flush = FALSE # Flush messages
260 self.limitflush = FALSE # Flush oversized messages
261 self.fetchall = FALSE # Fetch old messages
262 self.rewrite = TRUE # Rewrite message headers
263 self.forcecr = FALSE # Force LF -> CR/LF
264 self.stripcr = FALSE # Strip CR
265 self.pass8bits = FALSE # Force BODY=7BIT
266 self.mimedecode = FALSE # Undo MIME armoring
267 self.dropstatus = FALSE # Drop incoming Status lines
268 self.dropdelivered = FALSE # Drop incoming Delivered-To lines
269 self.idle = FALSE # IDLE after poll
270 self.limit = 0 # Message size limit
271 self.warnings = 3600 # Size warning interval (see tunable.h)
272 self.fetchlimit = 0 # Max messages fetched per batch
273 self.fetchsizelimit = 100 # Max message sizes fetched per transaction
274 self.fastuidl = 4 # Do fast uidl 3 out of 4 times
275 self.batchlimit = 0 # Max message forwarded per batch
276 self.expunge = 0 # Interval between expunges (IMAP)
277 self.ssl = 0 # Enable Seccure Socket Layer
278 self.sslkey = None # SSL key filename
279 self.sslcert = None # SSL certificate filename
280 self.sslproto = None # Force SSL?
281 self.sslcertck = 0 # Enable strict SSL cert checking
282 self.sslcertpath = None # Path to trusted certificates
283 self.sslcommonname = None # SSL CommonName to expect
284 self.sslfingerprint = None # SSL key fingerprint to check
285 self.properties = None # Extension properties
287 ('remote', 'String'),
288 # leave out mailboxes and localnames
289 ('password', 'String'),
290 # Leave out smtphunt, fetchdomains
291 ('smtpaddress', 'String'),
292 ('smtpname', 'String'),
293 ('preconnect', 'String'),
294 ('postconnect', 'String'),
298 ('antispam', 'String'),
300 ('flush', 'Boolean'),
301 ('limitflush', 'Boolean'),
302 ('fetchall', 'Boolean'),
303 ('rewrite', 'Boolean'),
304 ('forcecr', 'Boolean'),
305 ('stripcr', 'Boolean'),
306 ('pass8bits', 'Boolean'),
307 ('mimedecode', 'Boolean'),
308 ('dropstatus', 'Boolean'),
309 ('dropdelivered', 'Boolean'),
313 ('fetchlimit', 'Int'),
314 ('fetchsizelimit', 'Int'),
316 ('batchlimit', 'Int'),
319 ('sslkey', 'String'),
320 ('sslcert', 'String'),
321 ('sslcertck', 'Boolean'),
322 ('sslcertpath', 'String'),
323 ('sslcommonname', 'String'),
324 ('sslfingerprint', 'String'),
325 ('properties', 'String'))
329 res = res + "user " + `self.remote` + " there ";
331 res = res + "with password " + `self.password` + " "
334 for x in self.localnames:
335 res = res + " " + `x`
337 if (self.keep != UserDefaults.keep
338 or self.flush != UserDefaults.flush
339 or self.limitflush != UserDefaults.limitflush
340 or self.fetchall != UserDefaults.fetchall
341 or self.rewrite != UserDefaults.rewrite
342 or self.forcecr != UserDefaults.forcecr
343 or self.stripcr != UserDefaults.stripcr
344 or self.pass8bits != UserDefaults.pass8bits
345 or self.mimedecode != UserDefaults.mimedecode
346 or self.dropstatus != UserDefaults.dropstatus
347 or self.dropdelivered != UserDefaults.dropdelivered
348 or self.idle != UserDefaults.idle):
349 res = res + " options"
350 if self.keep != UserDefaults.keep:
351 res = res + flag2str(self.keep, 'keep')
352 if self.flush != UserDefaults.flush:
353 res = res + flag2str(self.flush, 'flush')
354 if self.limitflush != UserDefaults.limitflush:
355 res = res + flag2str(self.limitflush, 'limitflush')
356 if self.fetchall != UserDefaults.fetchall:
357 res = res + flag2str(self.fetchall, 'fetchall')
358 if self.rewrite != UserDefaults.rewrite:
359 res = res + flag2str(self.rewrite, 'rewrite')
360 if self.forcecr != UserDefaults.forcecr:
361 res = res + flag2str(self.forcecr, 'forcecr')
362 if self.stripcr != UserDefaults.stripcr:
363 res = res + flag2str(self.stripcr, 'stripcr')
364 if self.pass8bits != UserDefaults.pass8bits:
365 res = res + flag2str(self.pass8bits, 'pass8bits')
366 if self.mimedecode != UserDefaults.mimedecode:
367 res = res + flag2str(self.mimedecode, 'mimedecode')
368 if self.dropstatus != UserDefaults.dropstatus:
369 res = res + flag2str(self.dropstatus, 'dropstatus')
370 if self.dropdelivered != UserDefaults.dropdelivered:
371 res = res + flag2str(self.dropdelivered, 'dropdelivered')
372 if self.idle != UserDefaults.idle:
373 res = res + flag2str(self.idle, 'idle')
374 if self.limit != UserDefaults.limit:
375 res = res + " limit " + `self.limit`
376 if self.warnings != UserDefaults.warnings:
377 res = res + " warnings " + `self.warnings`
378 if self.fetchlimit != UserDefaults.fetchlimit:
379 res = res + " fetchlimit " + `self.fetchlimit`
380 if self.fetchsizelimit != UserDefaults.fetchsizelimit:
381 res = res + " fetchsizelimit " + `self.fetchsizelimit`
382 if self.fastuidl != UserDefaults.fastuidl:
383 res = res + " fastuidl " + `self.fastuidl`
384 if self.batchlimit != UserDefaults.batchlimit:
385 res = res + " batchlimit " + `self.batchlimit`
386 if self.ssl and self.ssl != UserDefaults.ssl:
387 res = res + flag2str(self.ssl, 'ssl')
388 if self.sslkey and self.sslkey != UserDefaults.sslkey:
389 res = res + " sslkey " + `self.sslkey`
390 if self.sslcert and self.sslcert != UserDefaults.sslcert:
391 res = res + " sslcert " + `self.sslcert`
392 if self.sslproto and self.sslproto != UserDefaults.sslproto:
393 res = res + " sslproto " + `self.sslproto`
394 if self.sslcertck and self.sslcertck != UserDefaults.sslcertck:
395 res = res + flag2str(self.sslcertck, 'sslcertck')
396 if self.sslcertpath and self.sslcertpath != UserDefaults.sslcertpath:
397 res = res + " sslcertpath " + `self.sslcertpath`
398 if self.sslcommonname and self.sslcommonname != UserDefaults.sslcommonname:
399 res = res + " sslcommonname " + `self.sslcommonname`
400 if self.sslfingerprint and self.sslfingerprint != UserDefaults.sslfingerprint:
401 res = res + " sslfingerprint " + `self.sslfingerprint`
402 if self.expunge != UserDefaults.expunge:
403 res = res + " expunge " + `self.expunge`
405 trimmed = self.smtphunt;
406 if trimmed != [] and trimmed[len(trimmed) - 1] == "localhost":
407 trimmed = trimmed[0:len(trimmed) - 1]
408 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
409 trimmed = trimmed[0:len(trimmed) - 1]
411 res = res + " smtphost "
415 trimmed = self.fetchdomains
416 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
417 trimmed = trimmed[0:len(trimmed) - 1]
419 res = res + " fetchdomains "
424 res = res + " folder"
425 for x in self.mailboxes:
426 res = res + ' "%s"' % x
428 for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'):
429 if getattr(self, fld):
430 res = res + " %s %s\n" % (fld, `getattr(self, fld)`)
431 if self.lmtp != UserDefaults.lmtp:
432 res = res + flag2str(self.lmtp, 'lmtp')
433 if self.antispam != UserDefaults.antispam:
434 res = res + " antispam " + self.antispam + "\n"
438 return "[User: " + repr(self) + "]"
444 # IANA port assignments and bogus 1109 entry
445 ianaservices = {"pop3":110,
451 # fetchmail protocol to IANA service name
452 defaultports = {"auto":None,
460 authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp",
464 'title' : 'List Selection Help',
465 'banner': 'List Selection',
467 You must select an item in the list box (by clicking on it).
470 def flag2str(value, string):
471 # make a string representation of a .fetchmailrc flag or negated flag
475 if value == FALSE: str = str + ("no ")
479 class LabeledEntry(Frame):
480 # widget consisting of entry field with caption to left
481 def bind(self, key, action):
482 self.E.bind(key, action)
485 def __init__(self, Master, text, textvar, lwidth, ewidth=12):
486 Frame.__init__(self, Master)
487 self.L = Label(self, {'text':text, 'width':lwidth, 'anchor':'w'})
488 self.E = Entry(self, {'textvar':textvar, 'width':ewidth})
489 self.L.pack({'side':'left'})
490 self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
492 def ButtonBar(frame, legend, ref, alternatives, depth, command):
493 # array of radio buttons, caption to left, picking from a string list
495 width = (len(alternatives)+1) / depth;
496 Label(bar, text=legend).pack(side=LEFT)
497 for column in range(width):
498 subframe = Frame(bar)
499 for row in range(depth):
500 ind = width * row + column
501 if ind < len(alternatives):
502 Radiobutton(subframe,
503 {'text':alternatives[ind],
505 'value':alternatives[ind],
506 'command':command}).pack(side=TOP, anchor=W)
508 # This is just a spacer
509 Radiobutton(subframe,
510 {'text':" ",'state':DISABLED}).pack(side=TOP, anchor=W)
511 subframe.pack(side=LEFT)
515 def helpwin(helpdict):
516 # help message window with a self-destruct button
518 helpwin.title(helpdict['title'])
519 helpwin.iconname(helpdict['title'])
520 Label(helpwin, text=helpdict['banner']).pack()
521 textframe = Frame(helpwin)
522 scroll = Scrollbar(textframe)
523 helpwin.textwidget = Text(textframe, setgrid=TRUE)
524 textframe.pack(side=TOP, expand=YES, fill=BOTH)
525 helpwin.textwidget.config(yscrollcommand=scroll.set)
526 helpwin.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
527 scroll.config(command=helpwin.textwidget.yview)
528 scroll.pack(side=RIGHT, fill=BOTH)
529 helpwin.textwidget.insert(END, helpdict['text']);
530 Button(helpwin, text='Done',
531 command=lambda x=helpwin: x.destroy(), bd=2).pack()
532 textframe.pack(side=TOP)
534 def make_icon_window(base, image):
536 # Some older pythons will error out on this
537 icon_image = PhotoImage(data=image)
538 icon_window = Toplevel()
539 Label(icon_window, image=icon_image, bg='black').pack()
540 base.master.iconwindow(icon_window)
541 # Avoid TkInter brain death. PhotoImage objects go out of
542 # scope when the enclosing function returns. Therefore
543 # we have to explicitly link them to something.
544 base.keepalive.append(icon_image)
548 class ListEdit(Frame):
549 # edit a list of values (duplicates not allowed) with a supplied editor hook
550 def __init__(self, newlegend, list, editor, deletor, master, helptxt):
552 self.deletor = deletor
555 # Set up a widget to accept new elements
556 self.newval = StringVar(master)
557 newwin = LabeledEntry(master, newlegend, self.newval, '12')
558 newwin.bind('<Double-1>', self.handleNew)
559 newwin.bind('<Return>', self.handleNew)
560 newwin.pack(side=TOP, fill=X, anchor=E)
562 # Edit the existing list
563 listframe = Frame(master)
564 scroll = Scrollbar(listframe)
565 self.listwidget = Listbox(listframe, height=0, selectmode='browse')
568 self.listwidget.insert(END, x)
569 listframe.pack(side=TOP, expand=YES, fill=BOTH)
570 self.listwidget.config(yscrollcommand=scroll.set)
571 self.listwidget.pack(side=LEFT, expand=YES, fill=BOTH)
572 scroll.config(command=self.listwidget.yview)
573 scroll.pack(side=RIGHT, fill=BOTH)
574 self.listwidget.config(selectmode=SINGLE, setgrid=TRUE)
575 self.listwidget.bind('<Double-1>', self.handleList);
576 self.listwidget.bind('<Return>', self.handleList);
580 Button(bf, text='Edit', command=self.editItem).pack(side=LEFT)
581 Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT)
583 self.helptxt = helptxt
584 Button(bf, text='Help', fg='blue',
585 command=self.help).pack(side=RIGHT)
589 helpwin(self.helptxt)
591 def handleList(self, event):
594 def handleNew(self, event):
595 item = self.newval.get()
597 entire = self.listwidget.get(0, self.listwidget.index('end'));
598 if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
599 self.listwidget.insert('end', item)
600 if self.list != None: self.list.append(item)
602 apply(self.editor, (item,))
606 select = self.listwidget.curselection()
611 if index and self.editor:
612 label = self.listwidget.get(index);
614 apply(self.editor, (label,))
616 def deleteItem(self):
617 select = self.listwidget.curselection()
621 index = string.atoi(select[0])
622 label = self.listwidget.get(index);
623 self.listwidget.delete(index)
624 if self.list != None:
626 if self.deletor != None:
627 apply(self.deletor, (label,))
629 def ConfirmQuit(frame, context):
632 text = 'Really quit ' + context + ' without saving?',
634 strings = ('Yes', 'No'),
638 def dispose_window(master, legend, help, savelegend='OK'):
639 dispose = Frame(master, relief=RAISED, bd=5)
640 Label(dispose, text=legend).pack(side=TOP,pady=10)
641 Button(dispose, text=savelegend, fg='blue',
642 command=master.save).pack(side=LEFT)
643 Button(dispose, text='Quit', fg='blue',
644 command=master.nosave).pack(side=LEFT)
645 Button(dispose, text='Help', fg='blue',
646 command=lambda x=help: helpwin(x)).pack(side=RIGHT)
651 # Common methods for Tkinter widgets -- deals with Tkinter declaration
652 def post(self, widgetclass, field):
653 for x in widgetclass.typemap:
654 if x[1] == 'Boolean':
655 setattr(self, x[0], BooleanVar(self))
656 elif x[1] == 'String':
657 setattr(self, x[0], StringVar(self))
659 setattr(self, x[0], IntVar(self))
660 source = getattr(getattr(self, field), x[0])
662 getattr(self, x[0]).set(source)
664 def fetch(self, widgetclass, field):
665 for x in widgetclass.typemap:
666 setattr(getattr(self, field), x[0], getattr(self, x[0]).get())
669 # First, code to set the global fetchmail run controls.
672 configure_novice_help = {
673 'title' : 'Fetchmail novice configurator help',
674 'banner': 'Novice configurator help',
676 In the `Novice Configurator Controls' panel, you can:
678 Press `Save' to save the new fetchmail configuration you have created.
679 Press `Quit' to exit without saving.
680 Press `Help' to bring up this help message.
682 In the `Novice Configuration' panels, you will set up the basic data
683 needed to create a simple fetchmail setup. These include:
685 1. The name of the remote site you want to query.
687 2. Your login name on that site.
689 3. Your password on that site.
691 4. A protocol to use (POP, IMAP, ETRN, etc.)
693 5. A poll interval in seconds.
694 If 0, fetchmail will run in the foreground once when started.
695 If > 0, fetchmail will run in the background and start a new poll
696 cycle after the interval has elapsed.
698 6. Options to fetch old messages as well as new, or to suppress
699 deletion of fetched message.
701 The novice-configuration code will assume that you want to forward mail
702 to a local sendmail listener with no special options.
705 configure_expert_help = {
706 'title' : 'Fetchmail expert configurator help',
707 'banner': 'Expert configurator help',
709 In the `Expert Configurator Controls' panel, you can:
711 Press `Save' to save the new fetchmail configuration you have edited.
712 Press `Quit' to exit without saving.
713 Press `Help' to bring up this help message.
715 In the `Run Controls' panel, you can set the following options that
716 control how fetchmail runs:
719 Number of seconds to wait between polls in the background.
720 If zero, fetchmail will run in foreground.
723 If empty, emit progress and error messages to stderr.
724 Otherwise this gives the name of the files to write to.
725 This field is ignored if the "Log to syslog?" option is on.
728 If empty, store seen-message IDs in .fetchids under user's home
729 directory. If nonempty, use given file name.
732 Who to send multidrop mail to as a last resort if no address can
733 be matched. Normally empty; in this case, fetchmail treats the
734 invoking user as the address of last resort unless that user is
735 root. If that user is root, fetchmail sends to `postmaster'.
738 If this option is on (the default) error mail goes to the sender.
739 Otherwise it goes to the postmaster.
742 If this option is on, spam bounces are sent to the sender or
743 postmaster (depending on the "Bounces to sender?" option. Otherwise,
744 spam bounces are not sent (the default).
747 If this option is on, permanent delivery errors are treated as
748 temporary, i. e. mail is kept on the upstream server. Useful
749 during testing and after configuration changes, and on by
751 If this option is off, permanent delivery errors delete
752 undeliverable mail from the upstream.
755 If false (the default) fetchmail generates a Received line into
756 each message and generates a HELO from the machine it is running on.
757 If true, fetchmail generates no Received line and HELOs as if it were
760 In the `Remote Mail Configurations' panel, you can:
762 1. Enter the name of a new remote mail server you want fetchmail to query.
764 To do this, simply enter a label for the poll configuration in the
765 `New Server:' box. The label should be a DNS name of the server (unless
766 you are using ssh or some other tunneling method and will fill in the `via'
767 option on the site configuration screen).
769 2. Change the configuration of an existing site.
771 To do this, find the site's label in the listbox and double-click it.
772 This will take you to a site configuration dialogue.
776 class ConfigurationEdit(Frame, MyWidget):
777 def __init__(self, configuration, outfile, master, onexit):
779 self.configuration = configuration
780 self.outfile = outfile
781 self.container = master
783 ConfigurationEdit.mode_to_help = {
784 'novice':configure_novice_help, 'expert':configure_expert_help
787 def server_edit(self, sitename):
788 self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
790 def server_delete(self, sitename):
792 for user in self.subwidgets.keys():
794 del self.configuration[sitename]
798 def edit(self, mode):
800 Frame.__init__(self, self.container)
801 self.master.title('fetchmail ' + self.mode + ' configurator');
802 self.master.iconname('fetchmail ' + self.mode + ' configurator');
803 self.master.protocol('WM_DELETE_WINDOW', self.nosave)
804 self.keepalive = [] # Use this to anchor the PhotoImage object
805 make_icon_window(self, fetchmail_icon)
807 self.post(Configuration, 'configuration')
810 'Configurator ' + self.mode + ' Controls',
811 ConfigurationEdit.mode_to_help[self.mode],
814 gf = Frame(self, relief=RAISED, bd = 5)
816 text='Fetchmail Run Controls',
817 bd=2).pack(side=TOP, pady=10)
822 if self.mode != 'novice':
824 log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
825 log.pack(side=RIGHT, anchor=E)
827 # Set the poll interval
828 de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
829 de.pack(side=RIGHT, anchor=E)
834 if self.mode != 'novice':
837 {'text':'Bounces to sender?',
838 'variable':self.bouncemail,
839 'relief':GROOVE}).pack(side=LEFT, anchor=W)
844 {'text':'Send spam bounces?',
845 'variable':self.spambounce,
846 'relief':GROOVE}).pack(side=LEFT, anchor=W)
851 {'text':'Treat permanent errors as temporary?',
852 'variable':self.softbounce,
853 'relief':GROOVE}).pack(side=LEFT, anchor=W)
858 {'text':'Log to syslog?',
859 'variable':self.syslog,
860 'relief':GROOVE}).pack(side=LEFT, anchor=W)
861 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
862 log.pack(side=RIGHT, anchor=E)
866 {'text':'Invisible mode?',
867 'variable':self.invisible,
868 'relief':GROOVE}).pack(side=LEFT, anchor=W)
870 log = LabeledEntry(gf, ' Idfile:', self.idfile, '14')
871 log.pack(side=RIGHT, anchor=E)
875 # Expert mode allows us to edit multiple sites
876 lf = Frame(self, relief=RAISED, bd=5)
878 text='Remote Mail Server Configurations',
879 bd=2).pack(side=TOP, pady=10)
880 ListEdit('New Server:',
881 map(lambda x: x.pollname, self.configuration.servers),
882 lambda site, self=self: self.server_edit(site),
883 lambda site, self=self: self.server_delete(site),
888 for sitename in self.subwidgets.keys():
889 self.subwidgets[sitename].destruct()
890 self.master.destroy()
894 if ConfirmQuit(self, self.mode + " configuration editor"):
898 for sitename in self.subwidgets.keys():
899 self.subwidgets[sitename].save()
900 self.fetch(Configuration, 'configuration')
904 elif not os.path.isfile(self.outfile) or Dialog(self,
905 title = 'Overwrite existing run control file?',
906 text = 'Really overwrite existing run control file?',
908 strings = ('Yes', 'No'),
909 default = 1).num == 0:
911 os.rename(self.outfile, self.outfile + "~")
912 # Pre-1.5.2 compatibility...
915 oldumask = os.umask(077)
916 fm = open(self.outfile, 'w')
921 os.chmod(self.outfile, 0600)
922 fm.write("# Configuration created %s by fetchmailconf %s\n" % (time.ctime(time.time()), version))
923 fm.write(`self.configuration`)
929 # Server editing stuff.
932 'title' : 'Remote site help',
933 'banner': 'Remote sites',
935 When you add a site name to the list here,
936 you initialize an entry telling fetchmail
937 how to poll a new site.
939 When you select a sitename (by double-
940 clicking it, or by single-clicking to
941 select and then clicking the Edit button),
942 you will open a window to configure that
947 'title' : 'Server options help',
948 'banner': 'Server Options',
950 The server options screen controls fetchmail
951 options that apply to one of your mailservers.
953 Once you have a mailserver configuration set
954 up as you like it, you can select `OK' to
955 store it in the server list maintained in
956 the main configuration window.
958 If you wish to discard changes to a server
959 configuration, select `Quit'.
963 'title' : 'Run Control help',
964 'banner': 'Run Controls',
966 If the `Poll normally' checkbox is on, the host is polled as part of
967 the normal operation of fetchmail when it is run with no arguments.
968 If it is off, fetchmail will only query this host when it is given as
969 a command-line argument.
971 The `Retrieve Error Policy' specifies how server errors during
972 message retrieval are handled. The default behaviour is to abort the
973 current session. Both the continue and markseen options will skip
974 the message with the error, but continue the session allowing for
975 downloading of subsequent messages. Additionally, the markseen
976 option will mark the skipped message as seen.
978 The `True name of server' box should specify the actual DNS name
979 to query. By default this is the same as the poll name.
981 Normally each host described in the file is queried once each
982 poll cycle. If `Cycles to skip between polls' is greater than 0,
983 that's the number of poll cycles that are skipped between the
984 times this post is actually polled.
986 The `Server timeout' is the number of seconds fetchmail will wait
987 for a reply from the mailserver before concluding it is hung and
992 'title' : 'Protocol and Port help',
993 'banner': 'Protocol and Port',
995 These options control the remote-mail protocol
996 and TCP/IP service port used to query this
999 If you click the `Probe for supported protocols'
1000 button, fetchmail will try to find you the most
1001 capable server on the selected host (this will
1002 only work if you're conncted to the Internet).
1003 The probe only checks for ordinary IMAP and POP
1004 protocols; fortunately these are the most
1005 frequently supported.
1007 The `Protocol' button bar offers you a choice of
1008 all the different protocols available. The `auto'
1009 protocol is the default mode; it probes the host
1010 ports for POP3 and IMAP to see if either is
1013 Normally the TCP/IP service port to use is
1014 dictated by the protocol choice. The `Service'
1015 field (only present in expert mode) lets you
1016 set a non-standard service (port).
1020 'title' : 'Security option help',
1021 'banner': 'Security',
1023 The 'authorization mode' allows you to choose the
1024 mode that fetchmail uses to log in to your server. You
1025 can usually leave this at 'any', but you will have to pick
1026 'NTLM' and 'MSN' manually for the nonce.
1028 The 'interface' option allows you to specify a range
1029 of IP addresses to monitor for activity. If these
1030 addresses are not active, fetchmail will not poll.
1031 Specifying this may protect you from a spoofing attack
1032 if your client machine has more than one IP gateway
1033 address and some of the gateways are to insecure nets.
1035 The `monitor' option, if given, specifies the only
1036 device through which fetchmail is permitted to connect
1037 to servers. This option may be used to prevent
1038 fetchmail from triggering an expensive dial-out if the
1039 interface is not already active.
1041 The `interface' and `monitor' options are available
1042 only for Linux and freeBSD systems. See the fetchmail
1043 manual page for details on these.
1045 The ssl option enables SSL communication with a mailserver
1046 supporting Secure Sockets Layer. The sslkey and sslcert options
1047 declare key and certificate files for use with SSL.
1048 The sslcertck option enables strict checking of SSL server
1049 certificates (and sslcertpath gives the trusted certificate
1050 directory). The sslcommonname option helps if the server is
1051 misconfigured and returning "Server CommonName mismatch"
1052 warnings. With sslfingerprint, you can specify a finger-
1053 print the server's key is checked against.
1057 'title' : 'Multidrop option help',
1058 'banner': 'Multidrop',
1060 These options are only useful with multidrop mode.
1061 See the manual page for extended discussion.
1065 'title' : 'User list help',
1066 'banner': 'User list',
1068 When you add a user name to the list here,
1069 you initialize an entry telling fetchmail
1070 to poll the site on behalf of the new user.
1072 When you select a username (by double-
1073 clicking it, or by single-clicking to
1074 select and then clicking the Edit button),
1075 you will open a window to configure the
1076 user's options on that site.
1079 class ServerEdit(Frame, MyWidget):
1080 def __init__(self, host, parent):
1081 self.parent = parent
1083 self.subwidgets = {}
1084 for site in parent.configuration.servers:
1085 if site.pollname == host:
1087 if (self.server == None):
1088 self.server = Server()
1089 self.server.pollname = host
1090 self.server.via = None
1091 parent.configuration.servers.append(self.server)
1093 def edit(self, mode, master=None):
1094 Frame.__init__(self, master)
1096 self.master.title('Fetchmail host ' + self.server.pollname);
1097 self.master.iconname('Fetchmail host ' + self.server.pollname);
1098 self.post(Server, 'server')
1099 self.makeWidgets(self.server.pollname, mode)
1100 self.keepalive = [] # Use this to anchor the PhotoImage object
1101 make_icon_window(self, fetchmail_icon)
1104 # self.wait_window()
1108 for username in self.subwidgets.keys():
1109 self.subwidgets[username].destruct()
1110 del self.parent.subwidgets[self.server.pollname]
1111 self.master.destroy()
1114 if ConfirmQuit(self, 'server option editing'):
1118 self.fetch(Server, 'server')
1119 for username in self.subwidgets.keys():
1120 self.subwidgets[username].save()
1123 def defaultPort(self):
1124 proto = self.protocol.get()
1125 # Callback to reset the port number whenever the protocol type changes.
1126 # We used to only reset the port if it had a default (zero) value.
1127 # This turns out to be a bad idea especially in Novice mode -- if
1128 # you set POP3 and then set IMAP, the port invisibly remained 110.
1129 # Now we reset unconditionally on the theory that if you're setting
1130 # a custom port number you should be in expert mode and playing
1131 # close enough attention to notice this...
1132 self.service.set(defaultports[proto])
1133 if not proto in ("POP3", "APOP", "KPOP"): self.uidl.state = DISABLED
1135 def user_edit(self, username, mode):
1136 self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1138 def user_delete(self, username):
1139 if self.subwidgets.has_key(username):
1140 self.subwidgets[username].destruct()
1141 del self.server[username]
1143 def makeWidgets(self, host, mode):
1144 topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1146 leftwin = Frame(self);
1149 if mode != 'novice':
1150 ctlwin = Frame(leftwin, relief=RAISED, bd=5)
1151 Label(ctlwin, text="Run Controls").pack(side=TOP)
1152 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
1153 Checkbutton(ctlwin, text='Pass messages with bad headers?',
1154 variable=self.badheader).pack(side=TOP)
1155 retrieveerrorlist = ['abort', 'continue', 'markseen']
1156 Label(ctlwin, text="Retrieve Error Policy").pack(side=TOP)
1157 ButtonBar(ctlwin, '', self.retrieveerror, retrieveerrorlist, 1, None)
1158 LabeledEntry(ctlwin, 'True name of ' + host + ':',
1159 self.via, leftwidth).pack(side=TOP, fill=X)
1160 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
1161 self.interval, leftwidth).pack(side=TOP, fill=X)
1162 LabeledEntry(ctlwin, 'Server timeout (seconds):',
1163 self.timeout, leftwidth).pack(side=TOP, fill=X)
1164 Button(ctlwin, text='Help', fg='blue',
1165 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
1168 # Compute the available protocols from the compile-time options
1169 protolist = ['auto']
1170 if 'pop3' in feature_options:
1171 protolist = protolist + ["POP3", "APOP", "KPOP"]
1172 if 'sdps' in feature_options:
1173 protolist.append("SDPS")
1174 if 'imap' in feature_options:
1175 protolist.append("IMAP")
1176 if 'etrn' in feature_options:
1177 protolist.append("ETRN")
1178 if 'odmr' in feature_options:
1179 protolist.append("ODMR")
1181 protwin = Frame(leftwin, relief=RAISED, bd=5)
1182 Label(protwin, text="Protocol").pack(side=TOP)
1183 ButtonBar(protwin, '',
1184 self.protocol, protolist, 2,
1186 if mode != 'novice':
1187 LabeledEntry(protwin, 'On server TCP/IP service:',
1188 self.service, leftwidth).pack(side=TOP, fill=X)
1190 Checkbutton(protwin,
1191 text="POP3: track `seen' with client-side UIDLs?",
1192 variable=self.uidl).pack(side=TOP)
1193 Button(protwin, text='Probe for supported protocols', fg='blue',
1194 command=self.autoprobe).pack(side=LEFT)
1195 Button(protwin, text='Help', fg='blue',
1196 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
1197 protwin.pack(fill=X)
1199 userwin = Frame(leftwin, relief=RAISED, bd=5)
1200 Label(userwin, text="User entries for " + host).pack(side=TOP)
1201 ListEdit("New user: ",
1202 map(lambda x: x.remote, self.server.users),
1203 lambda u, m=mode, s=self: s.user_edit(u, m),
1204 lambda u, s=self: s.user_delete(u),
1206 userwin.pack(fill=X)
1208 leftwin.pack(side=LEFT, anchor=N, fill=X);
1210 if mode != 'novice':
1211 rightwin = Frame(self);
1213 mdropwin = Frame(rightwin, relief=RAISED, bd=5)
1214 Label(mdropwin, text="Multidrop options").pack(side=TOP)
1215 LabeledEntry(mdropwin, 'Envelope address header:',
1216 self.envelope, '22').pack(side=TOP, fill=X)
1217 LabeledEntry(mdropwin, 'Envelope headers to skip:',
1218 self.envskip, '22').pack(side=TOP, fill=X)
1219 LabeledEntry(mdropwin, 'Name prefix to strip:',
1220 self.qvirtual, '22').pack(side=TOP, fill=X)
1221 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
1222 variable=self.dns).pack(side=TOP)
1223 Label(mdropwin, text="DNS aliases").pack(side=TOP)
1224 ListEdit("New alias: ", self.server.aka, None, None, mdropwin, None)
1225 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
1226 ListEdit("New domain: ",
1227 self.server.localdomains, None, None, mdropwin, multihelp)
1228 mdropwin.pack(fill=X)
1230 if os_type in ('linux', 'freebsd'):
1231 secwin = Frame(rightwin, relief=RAISED, bd=5)
1232 Label(secwin, text="Security").pack(side=TOP)
1233 # Don't actually let users set this. KPOP sets it implicitly
1234 ButtonBar(secwin, 'Authorization mode:',
1235 self.auth, authlist, 2, None).pack(side=TOP)
1236 if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
1237 LabeledEntry(secwin, 'IP range to check before poll:',
1238 self.interface, leftwidth).pack(side=TOP, fill=X)
1239 if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
1240 LabeledEntry(secwin, 'Interface to monitor:',
1241 self.monitor, leftwidth).pack(side=TOP, fill=X)
1242 # Someday this should handle Kerberos 5 too
1243 if 'kerberos' in feature_options:
1244 LabeledEntry(secwin, 'Principal:',
1245 self.principal, '12').pack(side=TOP, fill=X)
1246 # ESMTP authentication
1247 LabeledEntry(secwin, 'ESMTP name:',
1248 self.esmtpname, '12').pack(side=TOP, fill=X)
1249 LabeledEntry(secwin, 'ESMTP password:',
1250 self.esmtppassword, '12').pack(side=TOP, fill=X)
1251 Button(secwin, text='Help', fg='blue',
1252 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
1255 rightwin.pack(side=LEFT, anchor=N);
1257 def autoprobe(self):
1258 # Note: this only handles case (1) near fetchmail.c:1032
1259 # We're assuming people smart enough to set up ssh tunneling
1260 # won't need autoprobing.
1262 realhost = self.server.via
1264 realhost = self.server.pollname
1266 for protocol in ("IMAP","POP3"):
1267 service = defaultports[protocol]
1268 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1270 sock.connect((realhost, ianaservices[service]))
1271 greetline = sock.recv(1024)
1277 confwin = Toplevel()
1278 if greetline == None:
1279 title = "Autoprobe of " + realhost + " failed"
1281 Fetchmailconf didn't find any mailservers active.
1282 This could mean the host doesn't support any,
1283 or that your Internet connection is down, or
1284 that the host is so slow that the probe timed
1285 out before getting a response.
1289 # OK, now try to recognize potential problems
1291 ### POP3 servers start here
1293 if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
1294 warnings = warnings + """
1295 This appears to be an old version of the UC Davis POP server. These are
1296 dangerously unreliable (among other problems, they may drop your mailbox
1297 on the floor if your connection is interrupted during the session).
1299 It is strongly recommended that you find a better POP3 server. The fetchmail
1300 FAQ includes pointers to good ones.
1303 if string.find(greetline, "comcast.net") > 0:
1304 warnings = warnings + """
1305 The Comcast Maillennium POP3 server only returns the first 80K of a long
1306 message retrieved with TOP. Its response to RETR is normal, so use the
1310 # Steve VanDevender <stevev@efn.org> writes:
1311 # The only system I have seen this happen with is cucipop-1.31
1312 # under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
1313 # 2.x and probably quite a few other systems. It appears to be a
1314 # bug or bad interaction with the SunOS realloc() -- it turns out
1315 # that internally cucipop does allocate a certain data structure in
1316 # multiples of 16, using realloc() to bump it up to the next
1317 # multiple if it needs more.
1319 # The distinctive symptom is that when there are 16 messages in the
1320 # inbox, you can RETR and DELE all 16 messages successfully, but on
1321 # QUIT cucipop returns something like "-ERR Error locking your
1322 # mailbox" and aborts without updating it.
1324 # The cucipop banner looks like:
1326 # +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1328 if string.find(greetline, "Cubic Circle") > 0:
1329 warnings = warnings + """
1330 I see your server is running cucipop. Better make sure the server box
1331 isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
1332 under that version, and doesn't cope with the result gracefully. Newer
1333 SunOS and Solaris machines run cucipop OK.
1335 Also, some versions of cucipop don't assert an exclusive lock on your
1336 mailbox when it's being queried. This means that if you have more than
1337 one fetchmail query running against the same mailbox, bad things can happen.
1339 if string.find(greetline, "David POP3 Server") > 0:
1340 warnings = warnings + """
1341 This POP3 server is badly broken. You should get rid of it -- and the
1342 brain-dead Microsoft operating system it rode in on.
1345 # The greeting line on the server known to be buggy is:
1346 # +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
1348 if string.find(greetline, "FTGate") > 0:
1349 warnings = warnings + """
1350 This POP server has a weird bug; it says OK twice in response to TOP.
1351 Its response to RETR is normal, so use the `fetchall' option.
1354 if string.find(greetline, " geonet.de") > 0:
1355 warnings = warnings + """
1356 You appear to be using geonet. As of late 2002, the TOP command on
1357 geonet's POP3 is broken. Use the fetchall option.
1360 if string.find(greetline, "OpenMail") > 0:
1361 warnings = warnings + """
1362 You appear to be using some version of HP OpenMail. Many versions of
1363 OpenMail do not process the "TOP" command correctly; the symptom is that
1364 only the header and first line of each message is retrieved. To work
1365 around this bug, turn on `fetchall' on all user entries associated with
1369 if string.find(greetline, "Escape character is") > 0:
1370 warnings = warnings + """
1371 Your greeting line looks like it was written by a fetid pile of
1372 camel dung identified to me as `popa3d written by Solar Designer'.
1373 Beware! The UIDL support in this thing is known to be completely broken,
1374 and other things probably are too.
1377 if string.find(greetline, "MercuryP/NLM v1.48") > 0:
1378 warnings = warnings + """
1379 This is not a POP3 server. It has delusions of being one, but after
1380 RETR all messages are automatically marked to be deleted. The only
1381 way to prevent this is to issue an RSET before leaving the server.
1382 Fetchmail does this, but we suspect this is probably broken in lots
1386 if string.find(greetline, "POP-Max") > 0:
1387 warnings = warnings + """
1388 The Mail Max POP3 server screws up on mail with attachments. It
1389 reports the message size with attachments included, but doesn't
1390 download them on a RETR or TOP (this violates the IMAP RFCs). It also
1391 doesn't implement TOP correctly. You should get rid of it -- and the
1392 brain-dead NT server it rode in on.
1395 if string.find(greetline, "POP3 Server Ready") > 0:
1396 warnings = warnings + """
1397 Some server that uses this greeting line has been observed to choke on
1398 TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
1401 if string.find(greetline, "QPOP") > 0:
1402 warnings = warnings + """
1403 This appears to be a version of Eudora qpopper. That's good. Fetchmail
1404 knows all about qpopper. However, be aware that the 2.53 version of
1405 qpopper does something odd that causes fetchmail to hang with a socket
1406 error on very large messages. This is probably not a fetchmail bug, as
1407 it has been observed with fetchpop. The fix is to upgrade to qpopper
1408 3.0beta or a more recent version. Better yet, switch to IMAP.
1411 if string.find(greetline, " sprynet.com") > 0:
1412 warnings = warnings + """
1413 You appear to be using a SpryNet server. In mid-1999 it was reported that
1414 the SpryNet TOP command marks messages seen. Therefore, for proper error
1415 recovery in the event of a line drop, it is strongly recommended that you
1416 turn on `fetchall' on all user entries associated with this server.
1419 if string.find(greetline, "TEMS POP3") > 0:
1420 warnings = warnings + """
1421 Your POP3 server has "TEMS" in its header line. At least one such
1422 server does not process the "TOP" command correctly; the symptom is
1423 that fetchmail hangs when trying to retrieve mail. To work around
1424 this bug, turn on `fetchall' on all user entries associated with this
1428 if string.find(greetline, " spray.se") > 0:
1429 warnings = warnings + """
1430 Your POP3 server has "spray.se" in its header line. In May 2000 at
1431 least one such server did not process the "TOP" command correctly; the
1432 symptom is that messages are treated as headerless. To work around
1433 this bug, turn on `fetchall' on all user entries associated with this
1437 if string.find(greetline, " usa.net") > 0:
1438 warnings = warnings + """
1439 You appear to be using USA.NET's free mail service. Their POP3 servers
1440 (at least as of the 2.2 version in use mid-1998) are quite flaky, but
1441 fetchmail can compensate. They seem to require that fetchall be switched on
1442 (otherwise you won't necessarily see all your mail, not even new mail).
1443 They also botch the TOP command the fetchmail normally uses for retrieval
1444 (it only retrieves about 10 lines rather than the number specified).
1445 Turning on fetchall will disable the use of TOP.
1447 Therefore, it is strongly recommended that you turn on `fetchall' on all
1448 user entries associated with this server.
1451 if string.find(greetline, " Novonyx POP3") > 0:
1452 warnings = warnings + """
1453 Your mailserver is running Novonyx POP3. This server, at least as of
1454 version 2.17, seems to have problems handling and reporting seen bits.
1455 You may have to use the fetchall option.
1458 if string.find(greetline, " IMS POP3") > 0:
1459 warnings = warnings + """
1460 Some servers issuing the greeting line 'IMS POP3' have been known to
1461 do byte-stuffing incorrectly. This means that if a message you receive
1462 has a . (period) at start of line, fetchmail will become confused and
1463 probably wedge itself. (This bug was recorded on IMS POP3 0.86.)
1467 ### IMAP servers start here
1469 if string.find(greetline, "GroupWise") > 0:
1470 warnings = warnings + """
1471 The Novell GroupWise IMAP server would be better named GroupFoolish;
1472 it is (according to the designer of IMAP) unusably broken. Among
1473 other things, it doesn't include a required content length in its
1474 BODY[TEXT] response.<p>
1476 Fetchmail works around this problem, but we strongly recommend voting
1477 with your dollars for a server that isn't brain-dead. If you stick
1478 with code as shoddy as GroupWise seems to be, you will probably pay
1479 for it with other problems.<p>
1482 if string.find(greetline, "InterChange") > 0:
1483 warnings = warnings + """
1485 The InterChange IMAP server at release levels below 3.61.08 screws up
1486 on mail with attachments. It doesn't fetch them if you give it a
1487 BODY[TEXT] request, though it does if you request RFC822.TEXT.
1488 According to the IMAP RFCs and their maintainer these should be
1489 equivalent -- and we can't drop the BODY[TEXT] form because M$
1490 Exchange (quite legally under RFC2062) rejectsit. The InterChange
1491 folks claim to have fixed this bug in 3.61.08.
1494 if string.find(greetline, "Imail") > 0:
1495 warnings = warnings + """
1496 We've seen a bug report indicating that this IMAP server (at least as of
1497 version 5.0.7) returns an invalid body size for messages with MIME
1498 attachments; the effect is to drop the attachments on the floor. We
1499 recommend you upgrade to a non-broken IMAP server.
1502 if string.find(greetline, "Domino IMAP4") > 0:
1503 warnings = warnings + """
1504 Your IMAP server appears to be Lotus Domino. This server, at least up
1505 to version 4.6.2a, has a bug in its generation of MIME boundaries (see
1506 the details in the fetchmail FAQ). As a result, even MIME aware MUAs
1507 will see attachments as part of the message text. If your Domino server's
1508 POP3 facility is enabled, we recommend you fall back on it.
1512 ### Checks for protocol variants start here
1514 closebrak = string.find(greetline, ">")
1515 if closebrak > 0 and greetline[closebrak+1] == "\r":
1516 warnings = warnings + """
1517 It looks like you could use APOP on this server and avoid sending it your
1518 password in clear. You should talk to the mailserver administrator about
1522 if string.find(greetline, "IMAP2bis") > 0:
1523 warnings = warnings + """
1524 IMAP2bis servers have a minor problem; they can't peek at messages without
1525 marking them seen. If you take a line hit during the retrieval, the
1526 interrupted message may get left on the server, marked seen.
1528 To work around this, it is recommended that you set the `fetchall'
1529 option on all user entries associated with this server, so any stuck
1530 mail will be retrieved next time around.
1532 To fix this bug, upgrade to an IMAP4 server. The fetchmail FAQ includes
1533 a pointer to an open-source implementation.
1536 if string.find(greetline, "IMAP4rev1") > 0:
1537 warnings = warnings + """
1538 I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
1539 remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
1540 has therefore been extremely well tested with this class of server.
1544 warnings = warnings + """
1545 Fetchmail doesn't know anything special about this server type.
1549 # Display success window with warnings
1550 title = "Autoprobe of " + realhost + " succeeded"
1551 confirm = "The " + protocol + " server said:\n\n" + greetline + warnings
1552 self.protocol.set(protocol)
1553 self.service.set(defaultports[protocol])
1554 confwin.title(title)
1555 confwin.iconname(title)
1556 Label(confwin, text=title).pack()
1557 Message(confwin, text=confirm, width=600).pack()
1558 Button(confwin, text='Done',
1559 command=lambda x=confwin: x.destroy(), bd=2).pack()
1562 # User editing stuff
1566 'title' : 'User option help',
1567 'banner': 'User options',
1569 You may use this panel to set options
1570 that may differ between individual
1573 Once you have a user configuration set
1574 up as you like it, you can select `OK' to
1575 store it in the user list maintained in
1576 the site configuration window.
1578 If you wish to discard the changes you have
1579 made to user options, select `Quit'.
1583 'title' : 'Local name help',
1584 'banner': 'Local names',
1586 The local name(s) in a user entry are the
1587 people on the client machine who should
1588 receive mail from the poll described.
1590 Note: if a user entry has more than one
1591 local name, messages will be retrieved
1592 in multidrop mode. This complicates
1593 the configuration issues; see the manual
1594 page section on multidrop mode.
1596 Warning: Be careful with local names
1597 such as foo@bar.com, as that can cause
1598 the mail to be sent to foo@bar.com instead
1599 of sending it to your local system.
1602 class UserEdit(Frame, MyWidget):
1603 def __init__(self, username, parent):
1604 self.parent = parent
1606 for user in parent.server.users:
1607 if user.remote == username:
1609 if self.user == None:
1611 self.user.remote = username
1612 self.user.localnames = [username]
1613 parent.server.users.append(self.user)
1615 def edit(self, mode, master=None):
1616 Frame.__init__(self, master)
1618 self.master.title('Fetchmail user ' + self.user.remote
1619 + ' querying ' + self.parent.server.pollname);
1620 self.master.iconname('Fetchmail user ' + self.user.remote);
1621 self.post(User, 'user')
1622 self.makeWidgets(mode, self.parent.server.pollname)
1623 self.keepalive = [] # Use this to anchor the PhotoImage object
1624 make_icon_window(self, fetchmail_icon)
1627 # self.wait_window()
1631 # Yes, this test can fail -- if you delete the parent window.
1632 if self.parent.subwidgets.has_key(self.user.remote):
1633 del self.parent.subwidgets[self.user.remote]
1634 self.master.destroy()
1637 if ConfirmQuit(self, 'user option editing'):
1642 for x in self.user.localnames: ok = ok + (string.find(x, '@') != -1)
1643 if ok == 0 or Dialog(self,
1644 title = "Really accept an embedded '@' ?",
1645 text = "Local names with an embedded '@', such as in foo@bar "
1646 "might result in your mail being sent to foo@bar.com "
1647 "instead of your local system.\n Are you sure you want "
1648 "a local user name with an '@' in it?",
1649 bitmap = 'question',
1650 strings = ('Yes', 'No'),
1651 default = 1).num == 0:
1652 self.fetch(User, 'user')
1655 def makeWidgets(self, mode, servername):
1656 dispose_window(self,
1657 "User options for " + self.user.remote + " querying " + servername,
1660 if mode != 'novice':
1661 leftwin = Frame(self);
1665 secwin = Frame(leftwin, relief=RAISED, bd=5)
1666 Label(secwin, text="Authentication").pack(side=TOP)
1667 LabeledEntry(secwin, 'Password:',
1668 self.password, '12').pack(side=TOP, fill=X)
1669 secwin.pack(fill=X, anchor=N)
1671 if 'ssl' in feature_options or 'ssl' in dictmembers:
1672 sslwin = Frame(leftwin, relief=RAISED, bd=5)
1673 Checkbutton(sslwin, text="Use SSL?",
1674 variable=self.ssl).pack(side=TOP, fill=X)
1675 LabeledEntry(sslwin, 'SSL key:',
1676 self.sslkey, '14').pack(side=TOP, fill=X)
1677 LabeledEntry(sslwin, 'SSL certificate:',
1678 self.sslcert, '14').pack(side=TOP, fill=X)
1679 Checkbutton(sslwin, text="Check server SSL certificate?",
1680 variable=self.sslcertck).pack(side=TOP, fill=X)
1681 LabeledEntry(sslwin, 'SSL trusted certificate directory:',
1682 self.sslcertpath, '14').pack(side=TOP, fill=X)
1683 LabeledEntry(sslwin, 'SSL CommonName:',
1684 self.sslcommonname, '14').pack(side=TOP, fill=X)
1685 LabeledEntry(sslwin, 'SSL key fingerprint:',
1686 self.sslfingerprint, '14').pack(side=TOP, fill=X)
1687 sslwin.pack(fill=X, anchor=N)
1689 names = Frame(leftwin, relief=RAISED, bd=5)
1690 Label(names, text="Local names").pack(side=TOP)
1691 ListEdit("New name: ",
1692 self.user.localnames, None, None, names, localhelp)
1693 names.pack(fill=X, anchor=N)
1695 if mode != 'novice':
1696 targwin = Frame(leftwin, relief=RAISED, bd=5)
1697 Label(targwin, text="Forwarding Options").pack(side=TOP)
1698 Label(targwin, text="Listeners to forward to").pack(side=TOP)
1699 ListEdit("New listener:",
1700 self.user.smtphunt, None, None, targwin, None)
1701 Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
1702 ListEdit("Domains:",
1703 self.user.fetchdomains, None, None, targwin, None)
1704 LabeledEntry(targwin, 'Use domain on RCPT TO line:',
1705 self.smtpaddress, '26').pack(side=TOP, fill=X)
1706 LabeledEntry(targwin, 'Set fixed RCPT TO address:',
1707 self.smtpname, '26').pack(side=TOP, fill=X)
1708 LabeledEntry(targwin, 'Connection setup command:',
1709 self.preconnect, '26').pack(side=TOP, fill=X)
1710 LabeledEntry(targwin, 'Connection wrapup command:',
1711 self.postconnect, '26').pack(side=TOP, fill=X)
1712 LabeledEntry(targwin, 'Local delivery agent:',
1713 self.mda, '26').pack(side=TOP, fill=X)
1714 LabeledEntry(targwin, 'BSMTP output file:',
1715 self.bsmtp, '26').pack(side=TOP, fill=X)
1716 LabeledEntry(targwin, 'Listener spam-block codes:',
1717 self.antispam, '26').pack(side=TOP, fill=X)
1718 LabeledEntry(targwin, 'Pass-through properties:',
1719 self.properties, '26').pack(side=TOP, fill=X)
1720 Checkbutton(targwin, text="Use LMTP?",
1721 variable=self.lmtp).pack(side=TOP, fill=X)
1722 targwin.pack(fill=X, anchor=N)
1724 if mode != 'novice':
1725 leftwin.pack(side=LEFT, fill=X, anchor=N)
1726 rightwin = Frame(self)
1730 optwin = Frame(rightwin, relief=RAISED, bd=5)
1731 Label(optwin, text="Processing Options").pack(side=TOP)
1732 Checkbutton(optwin, text="Suppress deletion of messages after reading",
1733 variable=self.keep).pack(side=TOP, anchor=W)
1734 Checkbutton(optwin, text="Fetch old messages as well as new",
1735 variable=self.fetchall).pack(side=TOP, anchor=W)
1736 if mode != 'novice':
1737 Checkbutton(optwin, text="Flush seen messages before retrieval",
1738 variable=self.flush).pack(side=TOP, anchor=W)
1739 Checkbutton(optwin, text="Flush oversized messages before retrieval",
1740 variable=self.limitflush).pack(side=TOP, anchor=W)
1741 Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply",
1742 variable=self.rewrite).pack(side=TOP, anchor=W)
1743 Checkbutton(optwin, text="Force CR/LF at end of each line",
1744 variable=self.forcecr).pack(side=TOP, anchor=W)
1745 Checkbutton(optwin, text="Strip CR from end of each line",
1746 variable=self.stripcr).pack(side=TOP, anchor=W)
1747 Checkbutton(optwin, text="Pass 8 bits even though SMTP says 7BIT",
1748 variable=self.pass8bits).pack(side=TOP, anchor=W)
1749 Checkbutton(optwin, text="Undo MIME armoring on header and body",
1750 variable=self.mimedecode).pack(side=TOP, anchor=W)
1751 Checkbutton(optwin, text="Drop Status lines from forwarded messages",
1752 variable=self.dropstatus).pack(side=TOP, anchor=W)
1753 Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
1754 variable=self.dropdelivered).pack(side=TOP, anchor=W)
1757 if mode != 'novice':
1758 limwin = Frame(rightwin, relief=RAISED, bd=5)
1759 Label(limwin, text="Resource Limits").pack(side=TOP)
1760 LabeledEntry(limwin, 'Message size limit:',
1761 self.limit, '30').pack(side=TOP, fill=X)
1762 LabeledEntry(limwin, 'Size warning interval:',
1763 self.warnings, '30').pack(side=TOP, fill=X)
1764 LabeledEntry(limwin, 'Max messages to fetch per poll:',
1765 self.fetchlimit, '30').pack(side=TOP, fill=X)
1766 LabeledEntry(limwin, 'Max message sizes to fetch per transaction:',
1767 self.fetchsizelimit, '30').pack(side=TOP, fill=X)
1768 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1769 LabeledEntry(limwin, 'Use fast UIDL:',
1770 self.fastuidl, '30').pack(side=TOP, fill=X)
1771 LabeledEntry(limwin, 'Max messages to forward per poll:',
1772 self.batchlimit, '30').pack(side=TOP, fill=X)
1773 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1774 LabeledEntry(limwin, 'Interval between expunges:',
1775 self.expunge, '30').pack(side=TOP, fill=X)
1776 Checkbutton(limwin, text="Idle after each poll (IMAP only)",
1777 variable=self.idle).pack(side=TOP, anchor=W)
1780 if self.parent.server.protocol == 'IMAP':
1781 foldwin = Frame(rightwin, relief=RAISED, bd=5)
1782 Label(foldwin, text="Remote folders (IMAP only)").pack(side=TOP)
1783 ListEdit("New folder:", self.user.mailboxes,
1784 None, None, foldwin, None)
1785 foldwin.pack(fill=X, anchor=N)
1787 if mode != 'novice':
1788 rightwin.pack(side=LEFT)
1794 # Top-level window that offers either novice or expert mode
1795 # (but not both at once; it disappears when one is selected).
1798 class Configurator(Frame):
1799 def __init__(self, outfile, master, onexit, parent):
1800 Frame.__init__(self, master)
1801 self.outfile = outfile
1802 self.onexit = onexit
1803 self.parent = parent
1804 self.master.title('fetchmail configurator');
1805 self.master.iconname('fetchmail configurator');
1807 self.keepalive = [] # Use this to anchor the PhotoImage object
1808 make_icon_window(self, fetchmail_icon)
1810 Message(self, text="""
1811 Use `Novice Configuration' for basic fetchmail setup;
1812 with this, you can easily set up a single-drop connection
1813 to one remote mail server.
1814 """, width=600).pack(side=TOP)
1815 Button(self, text='Novice Configuration',
1816 fg='blue', command=self.novice).pack()
1818 Message(self, text="""
1819 Use `Expert Configuration' for advanced fetchmail setup,
1820 including multiple-site or multidrop connections.
1821 """, width=600).pack(side=TOP)
1822 Button(self, text='Expert Configuration',
1823 fg='blue', command=self.expert).pack()
1825 Message(self, text="""
1826 Or you can just select `Quit' to leave the configurator now and
1827 return to the main panel.
1828 """, width=600).pack(side=TOP)
1829 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1830 master.protocol("WM_DELETE_WINDOW", self.leave)
1833 self.master.destroy()
1834 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1837 self.master.destroy()
1838 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1841 self.master.destroy()
1844 # Run a command in a scrolling text widget, displaying its output
1846 class RunWindow(Frame):
1847 def __init__(self, command, master, parent):
1848 Frame.__init__(self, master)
1849 self.master = master
1850 self.master.title('fetchmail run window');
1851 self.master.iconname('fetchmail run window');
1854 text="Running "+command,
1855 bd=2).pack(side=TOP, pady=10)
1856 self.keepalive = [] # Use this to anchor the PhotoImage object
1857 make_icon_window(self, fetchmail_icon)
1859 # This is a scrolling text window
1860 textframe = Frame(self)
1861 scroll = Scrollbar(textframe)
1862 self.textwidget = Text(textframe, setgrid=TRUE)
1863 textframe.pack(side=TOP, expand=YES, fill=BOTH)
1864 self.textwidget.config(yscrollcommand=scroll.set)
1865 self.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
1866 scroll.config(command=self.textwidget.yview)
1867 scroll.pack(side=RIGHT, fill=BOTH)
1868 textframe.pack(side=TOP)
1870 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1872 self.update() # Draw widget before executing fetchmail
1874 # Always look for a runnable command in the directory we're running in
1875 # first. This avoids some obscure version-skew errors that can occur
1876 # if you pick up an old fetchmail from the standard system locations.
1877 os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"]
1878 child_stdout = os.popen(command + " 2>&1 </dev/null", "r")
1880 ch = child_stdout.read(1)
1883 self.textwidget.insert(END, ch)
1884 self.textwidget.insert(END, "Done.")
1885 self.textwidget.see(END);
1888 self.master.destroy()
1890 # Here's where we choose either configuration or launching
1892 class MainWindow(Frame):
1893 def __init__(self, outfile, master=None):
1894 Frame.__init__(self, master)
1895 self.outfile = outfile
1896 self.master.title('fetchmail launcher');
1897 self.master.iconname('fetchmail launcher');
1900 text='Fetchmailconf ' + version,
1901 bd=2).pack(side=TOP, pady=10)
1902 self.keepalive = [] # Use this to anchor the PhotoImage object
1903 make_icon_window(self, fetchmail_icon)
1906 ## Test icon display with the following:
1907 # icon_image = PhotoImage(data=fetchmail_icon)
1908 # Label(self, image=icon_image).pack(side=TOP, pady=10)
1909 # self.keepalive.append(icon_image)
1911 Message(self, text="""
1912 Use `Configure fetchmail' to tell fetchmail about the remote
1913 servers it should poll (the host name, your username there,
1914 whether to use POP or IMAP, and so forth).
1915 """, width=600).pack(side=TOP)
1916 self.configbutton = Button(self, text='Configure fetchmail',
1917 fg='blue', command=self.configure)
1918 self.configbutton.pack()
1920 Message(self, text="""
1921 Use `Run fetchmail' to run fetchmail with debugging enabled.
1922 This is a good way to test out a new configuration.
1923 """, width=600).pack(side=TOP)
1924 Button(self, text='Run fetchmail',fg='blue', command=self.test).pack()
1926 Message(self, text="""
1927 Use `Run fetchmail' to run fetchmail in foreground.
1928 Progress messages will be shown, but not debug messages.
1929 """, width=600).pack(side=TOP)
1930 Button(self, text='Run fetchmail', fg='blue', command=self.run).pack()
1932 Message(self, text="""
1933 Or you can just select `Quit' to exit the launcher now.
1934 """, width=600).pack(side=TOP)
1935 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1937 def configure(self):
1938 self.configbutton.configure(state=DISABLED)
1939 Configurator(self.outfile, Toplevel(),
1940 lambda self=self: self.configbutton.configure(state=NORMAL),
1943 cmd = "fetchmail -N -d0 --nosyslog -v"
1945 cmd = cmd + " -f " + rcfile
1946 RunWindow(cmd, Toplevel(), self)
1949 cmd = "fetchmail -N -d0"
1951 cmd = cmd + " -f " + rcfile
1952 RunWindow(cmd, Toplevel(), self)
1957 # Functions for turning a dictionary into an instantiated object tree.
1959 def intersect(list1, list2):
1960 # Compute set intersection of lists
1967 def setdiff(list1, list2):
1968 # Compute set difference of lists
1975 def copy_instance(toclass, fromdict):
1976 # Initialize a class object of given type from a conformant dictionary.
1977 for fld in fromdict.keys():
1978 if not fld in dictmembers:
1979 dictmembers.append(fld)
1980 # The `optional' fields are the ones we can ignore for purposes of
1981 # conformability checking; they'll still get copied if they are
1982 # present in the dictionary.
1983 optional = ('interface', 'monitor',
1984 'esmtpname', 'esmtppassword',
1985 'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
1986 'sslcertpath', 'sslcommonname', 'sslfingerprint', 'showdots')
1987 class_sig = setdiff(toclass.__dict__.keys(), optional)
1989 dict_keys = setdiff(fromdict.keys(), optional)
1991 common = intersect(class_sig, dict_keys)
1992 if 'typemap' in class_sig:
1993 class_sig.remove('typemap')
1994 if tuple(class_sig) != tuple(dict_keys):
1995 print "Fields don't match what fetchmailconf expected:"
1996 # print "Class signature: " + `class_sig`
1997 # print "Dictionary keys: " + `dict_keys`
1998 diff = setdiff(class_sig, common)
2000 print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
2001 diff = setdiff(dict_keys, common)
2003 print "Not matched in dictionary keys: " + `diff`
2006 for x in fromdict.keys():
2007 setattr(toclass, x, fromdict[x])
2010 # And this is the main sequence. How it works:
2012 # First, call `fetchmail --configdump' and trap the output in a tempfile.
2013 # This should fill it with a Python initializer for a variable `fetchmailrc'.
2014 # Run execfile on the file to pull fetchmailrc into Python global space.
2015 # You don't want static data, though; you want, instead, a tree of objects
2016 # with the same data members and added appropriate methods.
2018 # This is what the copy_instance function() is for. It tries to copy a
2019 # dictionary field by field into a class, aborting if the class and dictionary
2020 # have different data members (except for any typemap member in the class;
2021 # that one is strictly for use by the MyWidget supperclass).
2023 # Once the object tree is set up, require user to choose novice or expert
2024 # mode and instantiate an edit object for the configuration. Class methods
2025 # will take it all from there.
2027 # Options (not documented because they're for fetchmailconf debuggers only):
2028 # -d: Read the configuration and dump it to stdout before editing. Dump
2029 # the edited result to stdout as well.
2030 # -f: specify the run control file to read.
2032 if __name__ == '__main__':
2034 if not os.environ.has_key("DISPLAY"):
2035 print "fetchmailconf must be run under X"
2038 fetchmail_icon = """
2039 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
2040 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
2041 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
2042 CJR7Y/+UMdbOxnNrY97Ove/Wvd7GrZyEa961jL2Ua9alc86ca7WEUntSKcaMSqVjGNZ7GGM5CNa1
2043 jPfOnN6tc3taMffeve/WtWtaQv/OjGtSMYRzWv/erda1hM6te7WUY62MWs61jP/vzv/ntda9jL2l
2044 czEhAO/n1oyEc//elDEpGEo5EOfexpyUe+/epefevffvxnNrQpyUStbWzsbGvZyclN7ezmNjWv//
2045 5/f33qWllNbWve/vzv//1ufnve/vvf//xvf3vefnrf//taWlc0pKMf//pbW1Y///jKWlWq2tWsbG
2046 Y///c97eUvf3Ut7nc+/3a87We8bOjOfv1u/37/f//621tb3Gxtbn52Nra87n53uUlJTv/6W9xuf3
2047 /8bW3iExOXu11tbv/5TW/4TO/63e/zmt/1KUxlK1/2u9/wCM/73GzrXG1gBKjACE/87e72NzhCkx
2048 OaXO92OMtUql/xCE/wApUtbe57W9xnN7hHut52Ot/xBSnABKnABavQB7/2ul7zF71gBr77XO73Oc
2049 1lqc9yFSlBApSimE/wAYOQApY0J7zlKM5wAxhABS1gBj/6W95wAhWgA5nAAYSgBS7wBS/wBK9wAp
2050 jABC5wBK/wApnABC/wApxgAhtYSMtQAQYwAp/3OE74SMxgAYxlpjvWNr70pS/wgQ3sbGzs7O1qWl
2051 3qWl70pKe0JC/yEhlCkp/wgI/wAAEAAAIQAAKQAAOQAASgAAUgAAYwAAawAAlAAAnAAApQAArQAA
2052 zgAA1gAA5wAA9wAA/0pC/xgQ52Na9ykhe4R7zikhYxgQSjEpQgAAACwAAAAAPAAoAAAI/wABCBxI
2053 sKDBgwgTKiRIYKHDhxARIvgXsaLFhGgEUBSYoKPHjyBDihxJkuS/kwNLqlzJcuTJjQBaypxpEiVH
2054 mjhxvkyZs2fLnTd9ehxAtKjRo0ZrwhTasUsENhYHKOUpk1E3j11mxCBiQVLEBlJd2owp9iVRjwUs
2055 zMCQ5IcLD4saPVxjIKxIoGTvvqSoyFEFGTBeqEhyxAoSFR/USGKVcEGBAwDshsSr1OYTEyhQpJiS
2056 ZcoUKWOQtJDRJFSaggzUGBgoGSTlsjahlPCRIkWVKT16THHRIoqIISBIEUgAYIGBhgRbf3ytFygU
2057 FZp9UDmxQkkMCRwyZKDBQy4aApABhP8XqNwj88l7BVpQYZtF5iArWgwAgGZBq24HU7OeGhQ90PVA
2058 aKZZCiiUMJ9ArSTEwGqR8ZeXfzbV0MIIMQTBwoUdxDDfAm8sZFyDZVEF4UYSKBEBD0+k6IEFPMxH
2059 3FzldXSea+kBgANJSOWIlIMhXZXAXv+c1WM3PuJEpH8iuhbAkv+MdENPRHaTRkdF/jiWSKCAwlKW
2060 VbbkY5Q0LgUSKExgoYBKCjCxARpdltQNKHaUoYAddnR53lVRnJLKBWh4RIEGCZx5FSOv1OLNDUVe
2061 deZHaWiZAB35fIOGNtbEUeV5oGAByzPOrBPFGt3kwEgxITACSg5oLGGLMg60oQAjaNz/oAAcN4Ai
2062 a0c3kHFDK3jYsw4g9sRzBgPLXdkRrBrQ8gsWQUxCCRZX9IJNBQ1s8IgCdeBCzBYN6IBIN2TUsQYd
2063 dXhDBxdzlAHOHHKEcocZdWwDjx8MTCmjsR2FMAstw1RyiSzHqPLALaOwk8QmzCzDCSi0xJKMMk4E
2064 Yw8389iTDT32GAKOPf7YY0Aa9tATyD3w/EGsefgmgEYUtPiChLKWQDMBJtEUgYkzH2RiTgGfTMCI
2065 Mlu0Yc85hNiDziH2tMqOGL72QY47gshLb7Fi4roELcjoQIsxWpDwQyfS2OCJMkLI4YUmyhgxSTVg
2066 CP2FHPZ80UDcieBjStNPD5LPOyZT/y0iHGiMwswexDSzRiRq6KIMJBc4M8skwKAyChia2KPH3P24
2067 YU8/lFhOTj152OPOHuXMU4g48vCRiN/9rZGLMdS4csUu1JzDgxuipOMDHMKsAwEnq/ByzTrrZMNO
2068 OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
2069 xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
2070 gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
2072 # The base64 data in the string above was generated by the following procedure:
2075 # print base64.encodestring(open("fetchmail.gif", "rb").read())
2079 (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV", ["help",
2081 dump = rcfile = None;
2082 for (switch, val) in options:
2083 if (switch == '-d'):
2085 elif (switch == '-f'):
2087 elif (switch == '-h' or switch == '--help'):
2089 Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|--help|-V|--version}
2090 -d - dump configuration (for debugging)
2091 -f fmrc - read alternate fetchmailrc file
2092 --help, -h - print this help text and quit
2093 --version, -V - print fetchmailconf version and quit
2096 elif (switch == '-V' or switch == '--version'):
2097 print "fetchmailconf %s" % version
2099 Copyright (C) 1997 - 2003 Eric S. Raymond
2100 Copyright (C) 2005, 2006, 2008, 2009 Matthias Andree
2101 fetchmailconf comes with ABSOLUTELY NO WARRANTY. This is free software, you are
2102 welcome to redistribute it under certain conditions. Please see the file
2103 COPYING in the source or documentation directory for details."""
2106 # Get client host's FQDN
2107 hostname = socket.gethostbyaddr(socket.gethostname())[0]
2110 ConfigurationDefaults = Configuration()
2111 ServerDefaults = Server()
2112 UserDefaults = User()
2114 # Read the existing configuration. We set the umask to 077 to make sure
2115 # that group & other read/write permissions are shut off -- we wouldn't
2116 # want crackers to snoop password information out of the tempfile.
2117 tmpfile = tempfile.mktemp()
2119 cmd = "umask 077 && fetchmail </dev/null -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2121 cmd = "umask 077 && fetchmail </dev/null --configdump --nosyslog >" + tmpfile
2126 print "`" + cmd + "' run failure, status " + `s`
2129 print "Unknown error while running fetchmail --configdump"
2136 print "Can't read configuration output of fetchmail --configdump."
2142 # The tricky part -- initializing objects from the configuration global
2143 # `Configuration' is the top level of the object tree we're going to mung.
2144 # The dictmembers list is used to track the set of fields the dictionary
2145 # contains; in particular, we can use it to tell whether things like the
2146 # monitor, interface, ssl, sslkey, or sslcert fields are present.
2148 Fetchmailrc = Configuration()
2149 copy_instance(Fetchmailrc, fetchmailrc)
2150 Fetchmailrc.servers = [];
2151 for server in fetchmailrc['servers']:
2153 copy_instance(Newsite, server)
2154 Fetchmailrc.servers.append(Newsite)
2156 for user in server['users']:
2158 copy_instance(Newuser, user)
2159 Newsite.users.append(Newuser)
2161 # We may want to display the configuration and quit
2163 print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2165 # The theory here is that -f alone sets the rcfile location,
2166 # but -d and -f together mean the new configuration should go to stdout.
2167 if not rcfile and not dump:
2168 rcfile = os.environ["HOME"] + "/.fetchmailrc"
2170 # OK, now run the configuration edit
2171 root = MainWindow(rcfile)
2174 # The following sets edit modes for GNU EMACS