3 # A GUI configurator for generating fetchmail configuration files.
4 # by Eric S. Raymond, <esr@snark.thyrsus.com>,
5 # Matthias Andree <matthias.andree@gmx.de>
6 # Requires Python with Tkinter, and the following OS-dependent services:
7 # posix, posixpath, socket
12 import sys, time, os, string, socket, getopt, tempfile
15 # Define the data structures the GUIs will be tossing around
19 self.poll_interval = 0 # Normally, run in foreground
20 self.logfile = None # No logfile, initially
21 self.idfile = os.environ["HOME"] + "/.fetchids" # Default idfile, initially
22 self.postmaster = None # No last-resort address, initially
23 self.bouncemail = TRUE # Bounce errors to users
24 self.spambounce = FALSE # Bounce spam errors
25 self.softbounce = TRUE # Treat permanent error as temporary
26 self.properties = None # No exiguous properties
27 self.invisible = FALSE # Suppress Received line & spoof?
28 self.syslog = FALSE # Use syslogd for logging?
29 self.servers = [] # List of included sites
30 Configuration.typemap = (
31 ('poll_interval', 'Int'),
32 ('logfile', 'String'),
34 ('postmaster', 'String'),
35 ('bouncemail', 'Boolean'),
36 ('spambounce', 'Boolean'),
37 ('softbounce', 'Boolean'),
38 ('properties', 'String'),
39 ('syslog', 'Boolean'),
40 ('invisible', 'Boolean'))
44 if self.syslog != ConfigurationDefaults.syslog:
45 str = str + ("set syslog\n")
47 str = str + ("set logfile \"%s\"\n" % (self.logfile,));
48 if self.idfile != ConfigurationDefaults.idfile:
49 str = str + ("set idfile \"%s\"\n" % (self.idfile,));
50 if self.postmaster != ConfigurationDefaults.postmaster:
51 str = str + ("set postmaster \"%s\"\n" % (self.postmaster,));
53 str = str + ("set bouncemail\n")
55 str = str + ("set nobouncemail\n")
57 str = str + ("set spambounce\n")
59 str = str + ("set no spambounce\n")
61 str = str + ("set softbounce\n")
63 str = str + ("set no softbounce\n")
64 if self.properties != ConfigurationDefaults.properties:
65 str = str + ("set properties \"%s\"\n" % (self.properties,));
66 if self.poll_interval > 0:
67 str = str + "set daemon " + `self.poll_interval` + "\n"
69 str = str + ("set invisible\n")
70 for site in self.servers:
71 str = str + repr(site)
74 def __delitem__(self, name):
75 for si in range(len(self.servers)):
76 if self.servers[si].pollname == name:
81 return "[Configuration: " + repr(self) + "]"
85 self.pollname = None # Poll label
86 self.via = None # True name of host
87 self.active = TRUE # Poll status
88 self.interval = 0 # Skip interval
89 self.protocol = 'auto' # Default to auto protocol
90 self.service = None # Service name to use
91 self.auth = 'any' # Default to password authentication
92 self.timeout = 300 # 5-minute timeout
93 self.envelope = 'Received' # Envelope-address header
94 self.envskip = 0 # Number of envelope headers to skip
95 self.qvirtual = None # Name prefix to strip
96 self.aka = [] # List of DNS aka names
97 self.dns = TRUE # Enable DNS lookup on multidrop
98 self.localdomains = [] # Domains to be considered local
99 self.interface = None # IP address and range
100 self.monitor = None # IP address and range
101 self.plugin = None # Plugin command for going to server
102 self.plugout = None # Plugin command for going to listener
103 self.principal = None # Kerberos principal
104 self.esmtpname = None # ESMTP 2554 name
105 self.esmtppassword = None # ESMTP 2554 password
106 self.tracepolls = FALSE # Add trace-poll info to headers
107 self.badheader = FALSE # Pass messages with bad headers on?
108 self.retrieveerror = 'abort' # Policy when message retrieval errors encountered
109 self.users = [] # List of user entries for site
111 ('pollname', 'String'),
113 ('active', 'Boolean'),
115 ('protocol', 'String'),
116 ('service', 'String'),
119 ('envelope', 'String'),
121 ('qvirtual', 'String'),
124 # leave localdomains out
125 ('interface', 'String'),
126 ('monitor', 'String'),
127 ('plugin', 'String'),
128 ('plugout', 'String'),
129 ('esmtpname', 'String'),
130 ('esmtppassword', 'String'),
131 ('principal', 'String'),
132 ('tracepolls','Boolean'),
133 ('badheader', 'Boolean'),
134 ('retrieveerror', 'String'))
136 def dump(self, folded):
138 if self.active: res = res + "poll"
139 else: res = res + "skip"
140 res = res + (" " + self.pollname)
142 res = res + (" via " + str(self.via) + "\n");
143 if self.protocol != ServerDefaults.protocol:
144 res = res + " with proto " + self.protocol
145 if self.service and self.protocol and self.service != defaultports[self.protocol] and defaultports[self.protocol] and self.service != ianaservices[defaultports[self.protocol]]:
146 res = res + " service " + self.service
147 if self.timeout != ServerDefaults.timeout:
148 res = res + " timeout " + `self.timeout`
149 if self.interval != ServerDefaults.interval:
150 res = res + " interval " + `self.interval`
151 if self.envelope != ServerDefaults.envelope or self.envskip != ServerDefaults.envskip:
153 res = res + " envelope " + `self.envskip` + " " + self.envelope
155 res = res + " envelope " + self.envelope
157 res = res + (" qvirtual " + str(self.qvirtual) + "\n");
158 if self.auth != ServerDefaults.auth:
159 res = res + " auth " + self.auth
160 if self.dns != ServerDefaults.dns:
161 res = res + " and options"
162 res = res + flag2str(self.dns, 'dns')
163 if folded: res = res + "\n "
164 else: res = res + " "
170 if self.aka and self.localdomains: res = res + " "
171 if self.localdomains:
172 res = res + ("localdomains")
173 for x in self.localdomains:
175 if (self.aka or self.localdomains):
182 res = res + "tracepolls\n"
185 res = res + " interface " + str(self.interface)
187 res = res + " monitor " + str(self.monitor)
189 res = res + " plugin " + `self.plugin`
191 res = res + " plugout " + `self.plugout`
193 res = res + " principal " + `self.principal`
195 res = res + " esmtpname " + `self.esmtpname`
196 if self.esmtppassword:
197 res = res + " esmtppassword " + `self.esmtppassword`
198 if self.interface or self.monitor or self.principal or self.plugin or self.plugout:
203 res = res + "bad-header accept "
204 if self.retrieveerror == 'continue':
205 res = res + "retrieve-error continue "
206 if self.retrieveerror == 'markseen':
207 res = res + "retrieve-error markseen "
208 if self.badheader or self.retrieveerror != ServerDefaults.retrieveerror:
212 if res[-1] == " ": res = res[0:-1]
214 for user in self.users:
215 res = res + repr(user)
219 def __delitem__(self, name):
220 for ui in range(len(self.users)):
221 if self.users[ui].remote == name:
226 return self.dump(TRUE)
229 return "[Server: " + self.dump(FALSE) + "]"
233 if os.environ.has_key("USER"):
234 self.remote = os.environ["USER"] # Remote username
235 elif os.environ.has_key("LOGNAME"):
236 self.remote = os.environ["LOGNAME"]
238 print "Can't get your username!"
240 self.localnames = [self.remote,]# Local names
241 self.password = None # Password for mail account access
242 self.mailboxes = [] # Remote folders to retrieve from
243 self.smtphunt = [] # Hosts to forward to
244 self.fetchdomains = [] # Domains to fetch from
245 self.smtpaddress = None # Append this to MAIL FROM line
246 self.smtpname = None # Use this for RCPT TO
247 self.preconnect = None # Connection setup
248 self.postconnect = None # Connection wrapup
249 self.mda = None # Mail Delivery Agent
250 self.bsmtp = None # BSMTP output file
251 self.lmtp = FALSE # Use LMTP rather than SMTP?
252 self.antispam = "" # Listener's spam-block code
253 self.keep = FALSE # Keep messages
254 self.flush = FALSE # Flush messages
255 self.limitflush = FALSE # Flush oversized messages
256 self.fetchall = FALSE # Fetch old messages
257 self.rewrite = TRUE # Rewrite message headers
258 self.forcecr = FALSE # Force LF -> CR/LF
259 self.stripcr = FALSE # Strip CR
260 self.pass8bits = FALSE # Force BODY=7BIT
261 self.mimedecode = FALSE # Undo MIME armoring
262 self.dropstatus = FALSE # Drop incoming Status lines
263 self.dropdelivered = FALSE # Drop incoming Delivered-To lines
264 self.idle = FALSE # IDLE after poll
265 self.limit = 0 # Message size limit
266 self.warnings = 3600 # Size warning interval (see tunable.h)
267 self.fetchlimit = 0 # Max messages fetched per batch
268 self.fetchsizelimit = 100 # Max message sizes fetched per transaction
269 self.fastuidl = 4 # Do fast uidl 3 out of 4 times
270 self.batchlimit = 0 # Max message forwarded per batch
271 self.expunge = 0 # Interval between expunges (IMAP)
272 self.ssl = 0 # Enable Seccure Socket Layer
273 self.sslkey = None # SSL key filename
274 self.sslcert = None # SSL certificate filename
275 self.sslproto = None # Force SSL?
276 self.sslcertck = 0 # Enable strict SSL cert checking
277 self.sslcertpath = None # Path to trusted certificates
278 self.sslcommonname = None # SSL CommonName to expect
279 self.sslfingerprint = None # SSL key fingerprint to check
280 self.properties = None # Extension properties
282 ('remote', 'String'),
283 # leave out mailboxes and localnames
284 ('password', 'String'),
285 # Leave out smtphunt, fetchdomains
286 ('smtpaddress', 'String'),
287 ('smtpname', 'String'),
288 ('preconnect', 'String'),
289 ('postconnect', 'String'),
293 ('antispam', 'String'),
295 ('flush', 'Boolean'),
296 ('limitflush', 'Boolean'),
297 ('fetchall', 'Boolean'),
298 ('rewrite', 'Boolean'),
299 ('forcecr', 'Boolean'),
300 ('stripcr', 'Boolean'),
301 ('pass8bits', 'Boolean'),
302 ('mimedecode', 'Boolean'),
303 ('dropstatus', 'Boolean'),
304 ('dropdelivered', 'Boolean'),
308 ('fetchlimit', 'Int'),
309 ('fetchsizelimit', 'Int'),
311 ('batchlimit', 'Int'),
314 ('sslkey', 'String'),
315 ('sslcert', 'String'),
316 ('sslcertck', 'Boolean'),
317 ('sslcertpath', 'String'),
318 ('sslcommonname', 'String'),
319 ('sslfingerprint', 'String'),
320 ('properties', 'String'))
324 res = res + "user " + `self.remote` + " there ";
326 res = res + "with password " + `self.password` + " "
329 for x in self.localnames:
330 res = res + " " + `x`
332 if (self.keep != UserDefaults.keep
333 or self.flush != UserDefaults.flush
334 or self.limitflush != UserDefaults.limitflush
335 or self.fetchall != UserDefaults.fetchall
336 or self.rewrite != UserDefaults.rewrite
337 or self.forcecr != UserDefaults.forcecr
338 or self.stripcr != UserDefaults.stripcr
339 or self.pass8bits != UserDefaults.pass8bits
340 or self.mimedecode != UserDefaults.mimedecode
341 or self.dropstatus != UserDefaults.dropstatus
342 or self.dropdelivered != UserDefaults.dropdelivered
343 or self.idle != UserDefaults.idle):
344 res = res + " options"
345 if self.keep != UserDefaults.keep:
346 res = res + flag2str(self.keep, 'keep')
347 if self.flush != UserDefaults.flush:
348 res = res + flag2str(self.flush, 'flush')
349 if self.limitflush != UserDefaults.limitflush:
350 res = res + flag2str(self.limitflush, 'limitflush')
351 if self.fetchall != UserDefaults.fetchall:
352 res = res + flag2str(self.fetchall, 'fetchall')
353 if self.rewrite != UserDefaults.rewrite:
354 res = res + flag2str(self.rewrite, 'rewrite')
355 if self.forcecr != UserDefaults.forcecr:
356 res = res + flag2str(self.forcecr, 'forcecr')
357 if self.stripcr != UserDefaults.stripcr:
358 res = res + flag2str(self.stripcr, 'stripcr')
359 if self.pass8bits != UserDefaults.pass8bits:
360 res = res + flag2str(self.pass8bits, 'pass8bits')
361 if self.mimedecode != UserDefaults.mimedecode:
362 res = res + flag2str(self.mimedecode, 'mimedecode')
363 if self.dropstatus != UserDefaults.dropstatus:
364 res = res + flag2str(self.dropstatus, 'dropstatus')
365 if self.dropdelivered != UserDefaults.dropdelivered:
366 res = res + flag2str(self.dropdelivered, 'dropdelivered')
367 if self.idle != UserDefaults.idle:
368 res = res + flag2str(self.idle, 'idle')
369 if self.limit != UserDefaults.limit:
370 res = res + " limit " + `self.limit`
371 if self.warnings != UserDefaults.warnings:
372 res = res + " warnings " + `self.warnings`
373 if self.fetchlimit != UserDefaults.fetchlimit:
374 res = res + " fetchlimit " + `self.fetchlimit`
375 if self.fetchsizelimit != UserDefaults.fetchsizelimit:
376 res = res + " fetchsizelimit " + `self.fetchsizelimit`
377 if self.fastuidl != UserDefaults.fastuidl:
378 res = res + " fastuidl " + `self.fastuidl`
379 if self.batchlimit != UserDefaults.batchlimit:
380 res = res + " batchlimit " + `self.batchlimit`
381 if self.ssl and self.ssl != UserDefaults.ssl:
382 res = res + flag2str(self.ssl, 'ssl')
383 if self.sslkey and self.sslkey != UserDefaults.sslkey:
384 res = res + " sslkey " + `self.sslkey`
385 if self.sslcert and self.sslcert != UserDefaults.sslcert:
386 res = res + " sslcert " + `self.sslcert`
387 if self.sslproto and self.sslproto != UserDefaults.sslproto:
388 res = res + " sslproto " + `self.sslproto`
389 if self.sslcertck and self.sslcertck != UserDefaults.sslcertck:
390 res = res + flag2str(self.sslcertck, 'sslcertck')
391 if self.sslcertpath and self.sslcertpath != UserDefaults.sslcertpath:
392 res = res + " sslcertpath " + `self.sslcertpath`
393 if self.sslcommonname and self.sslcommonname != UserDefaults.sslcommonname:
394 res = res + " sslcommonname " + `self.sslcommonname`
395 if self.sslfingerprint and self.sslfingerprint != UserDefaults.sslfingerprint:
396 res = res + " sslfingerprint " + `self.sslfingerprint`
397 if self.expunge != UserDefaults.expunge:
398 res = res + " expunge " + `self.expunge`
400 trimmed = self.smtphunt;
401 if trimmed != [] and trimmed[len(trimmed) - 1] == "localhost":
402 trimmed = trimmed[0:len(trimmed) - 1]
403 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
404 trimmed = trimmed[0:len(trimmed) - 1]
406 res = res + " smtphost "
410 trimmed = self.fetchdomains
411 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
412 trimmed = trimmed[0:len(trimmed) - 1]
414 res = res + " fetchdomains "
419 res = res + " folder"
420 for x in self.mailboxes:
421 res = res + ' "%s"' % x
423 for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'):
424 if getattr(self, fld):
425 res = res + " %s %s\n" % (fld, `getattr(self, fld)`)
426 if self.lmtp != UserDefaults.lmtp:
427 res = res + flag2str(self.lmtp, 'lmtp')
428 if self.antispam != UserDefaults.antispam:
429 res = res + " antispam " + self.antispam + "\n"
433 return "[User: " + repr(self) + "]"
439 # IANA port assignments and bogus 1109 entry
440 ianaservices = {"pop3":110,
446 # fetchmail protocol to IANA service name
447 defaultports = {"auto":None,
454 authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp",
455 "msn", "ntlm", "apop", "cram-md5")
458 'title' : 'List Selection Help',
459 'banner': 'List Selection',
461 You must select an item in the list box (by clicking on it).
464 def flag2str(value, string):
465 # make a string representation of a .fetchmailrc flag or negated flag
469 if value == FALSE: str = str + ("no ")
473 class LabeledEntry(Frame):
474 # widget consisting of entry field with caption to left
475 def bind(self, key, action):
476 self.E.bind(key, action)
479 def __init__(self, Master, text, textvar, lwidth, ewidth=12):
480 Frame.__init__(self, Master)
481 self.L = Label(self, {'text':text, 'width':lwidth, 'anchor':'w'})
482 self.E = Entry(self, {'textvar':textvar, 'width':ewidth})
483 self.L.pack({'side':'left'})
484 self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
486 def ButtonBar(frame, legend, ref, alternatives, depth, command):
487 # array of radio buttons, caption to left, picking from a string list
489 width = (len(alternatives)+1) / depth;
490 Label(bar, text=legend).pack(side=LEFT)
491 for column in range(width):
492 subframe = Frame(bar)
493 for row in range(depth):
494 ind = width * row + column
495 if ind < len(alternatives):
496 Radiobutton(subframe,
497 {'text':alternatives[ind],
499 'value':alternatives[ind],
500 'command':command}).pack(side=TOP, anchor=W)
502 # This is just a spacer
503 Radiobutton(subframe,
504 {'text':" ",'state':DISABLED}).pack(side=TOP, anchor=W)
505 subframe.pack(side=LEFT)
509 def helpwin(helpdict):
510 # help message window with a self-destruct button
512 helpwin.title(helpdict['title'])
513 helpwin.iconname(helpdict['title'])
514 Label(helpwin, text=helpdict['banner']).pack()
515 textframe = Frame(helpwin)
516 scroll = Scrollbar(textframe)
517 helpwin.textwidget = Text(textframe, setgrid=TRUE)
518 textframe.pack(side=TOP, expand=YES, fill=BOTH)
519 helpwin.textwidget.config(yscrollcommand=scroll.set)
520 helpwin.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
521 scroll.config(command=helpwin.textwidget.yview)
522 scroll.pack(side=RIGHT, fill=BOTH)
523 helpwin.textwidget.insert(END, helpdict['text']);
524 Button(helpwin, text='Done',
525 command=lambda x=helpwin: x.destroy(), bd=2).pack()
526 textframe.pack(side=TOP)
528 def make_icon_window(base, image):
530 # Some older pythons will error out on this
531 icon_image = PhotoImage(data=image)
532 icon_window = Toplevel()
533 Label(icon_window, image=icon_image, bg='black').pack()
534 base.master.iconwindow(icon_window)
535 # Avoid TkInter brain death. PhotoImage objects go out of
536 # scope when the enclosing function returns. Therefore
537 # we have to explicitly link them to something.
538 base.keepalive.append(icon_image)
542 class ListEdit(Frame):
543 # edit a list of values (duplicates not allowed) with a supplied editor hook
544 def __init__(self, newlegend, list, editor, deletor, master, helptxt):
546 self.deletor = deletor
549 # Set up a widget to accept new elements
550 self.newval = StringVar(master)
551 newwin = LabeledEntry(master, newlegend, self.newval, '12')
552 newwin.bind('<Double-1>', self.handleNew)
553 newwin.bind('<Return>', self.handleNew)
554 newwin.pack(side=TOP, fill=X, anchor=E)
556 # Edit the existing list
557 listframe = Frame(master)
558 scroll = Scrollbar(listframe)
559 self.listwidget = Listbox(listframe, height=0, selectmode='browse')
562 self.listwidget.insert(END, x)
563 listframe.pack(side=TOP, expand=YES, fill=BOTH)
564 self.listwidget.config(yscrollcommand=scroll.set)
565 self.listwidget.pack(side=LEFT, expand=YES, fill=BOTH)
566 scroll.config(command=self.listwidget.yview)
567 scroll.pack(side=RIGHT, fill=BOTH)
568 self.listwidget.config(selectmode=SINGLE, setgrid=TRUE)
569 self.listwidget.bind('<Double-1>', self.handleList);
570 self.listwidget.bind('<Return>', self.handleList);
574 Button(bf, text='Edit', command=self.editItem).pack(side=LEFT)
575 Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT)
577 self.helptxt = helptxt
578 Button(bf, text='Help', fg='blue',
579 command=self.help).pack(side=RIGHT)
583 helpwin(self.helptxt)
585 def handleList(self, event):
588 def handleNew(self, event):
589 item = self.newval.get()
591 entire = self.listwidget.get(0, self.listwidget.index('end'));
592 if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
593 self.listwidget.insert('end', item)
594 if self.list != None: self.list.append(item)
596 apply(self.editor, (item,))
600 select = self.listwidget.curselection()
605 if index and self.editor:
606 label = self.listwidget.get(index);
608 apply(self.editor, (label,))
610 def deleteItem(self):
611 select = self.listwidget.curselection()
615 index = string.atoi(select[0])
616 label = self.listwidget.get(index);
617 self.listwidget.delete(index)
618 if self.list != None:
620 if self.deletor != None:
621 apply(self.deletor, (label,))
623 def ConfirmQuit(frame, context):
626 text = 'Really quit ' + context + ' without saving?',
628 strings = ('Yes', 'No'),
632 def dispose_window(master, legend, help, savelegend='OK'):
633 dispose = Frame(master, relief=RAISED, bd=5)
634 Label(dispose, text=legend).pack(side=TOP,pady=10)
635 Button(dispose, text=savelegend, fg='blue',
636 command=master.save).pack(side=LEFT)
637 Button(dispose, text='Quit', fg='blue',
638 command=master.nosave).pack(side=LEFT)
639 Button(dispose, text='Help', fg='blue',
640 command=lambda x=help: helpwin(x)).pack(side=RIGHT)
645 # Common methods for Tkinter widgets -- deals with Tkinter declaration
646 def post(self, widgetclass, field):
647 for x in widgetclass.typemap:
648 if x[1] == 'Boolean':
649 setattr(self, x[0], BooleanVar(self))
650 elif x[1] == 'String':
651 setattr(self, x[0], StringVar(self))
653 setattr(self, x[0], IntVar(self))
654 source = getattr(getattr(self, field), x[0])
656 getattr(self, x[0]).set(source)
658 def fetch(self, widgetclass, field):
659 for x in widgetclass.typemap:
660 setattr(getattr(self, field), x[0], getattr(self, x[0]).get())
663 # First, code to set the global fetchmail run controls.
666 configure_novice_help = {
667 'title' : 'Fetchmail novice configurator help',
668 'banner': 'Novice configurator help',
670 In the `Novice Configurator Controls' panel, you can:
672 Press `Save' to save the new fetchmail configuration you have created.
673 Press `Quit' to exit without saving.
674 Press `Help' to bring up this help message.
676 In the `Novice Configuration' panels, you will set up the basic data
677 needed to create a simple fetchmail setup. These include:
679 1. The name of the remote site you want to query.
681 2. Your login name on that site.
683 3. Your password on that site.
685 4. A protocol to use (POP, IMAP, ETRN, etc.)
687 5. A poll interval in seconds.
688 If 0, fetchmail will run in the foreground once when started.
689 If > 0, fetchmail will run in the background and start a new poll
690 cycle after the interval has elapsed.
692 6. Options to fetch old messages as well as new, or to suppress
693 deletion of fetched message.
695 The novice-configuration code will assume that you want to forward mail
696 to a local sendmail listener with no special options.
699 configure_expert_help = {
700 'title' : 'Fetchmail expert configurator help',
701 'banner': 'Expert configurator help',
703 In the `Expert Configurator Controls' panel, you can:
705 Press `Save' to save the new fetchmail configuration you have edited.
706 Press `Quit' to exit without saving.
707 Press `Help' to bring up this help message.
709 In the `Run Controls' panel, you can set the following options that
710 control how fetchmail runs:
713 Number of seconds to wait between polls in the background.
714 If zero, fetchmail will run in foreground.
717 If empty, emit progress and error messages to stderr.
718 Otherwise this gives the name of the files to write to.
719 This field is ignored if the "Log to syslog?" option is on.
722 If empty, store seen-message IDs in .fetchids under user's home
723 directory. If nonempty, use given file name.
726 Who to send multidrop mail to as a last resort if no address can
727 be matched. Normally empty; in this case, fetchmail treats the
728 invoking user as the address of last resort unless that user is
729 root. If that user is root, fetchmail sends to `postmaster'.
732 If this option is on (the default) error mail goes to the sender.
733 Otherwise it goes to the postmaster.
736 If this option is on, spam bounces are sent to the sender or
737 postmaster (depending on the "Bounces to sender?" option. Otherwise,
738 spam bounces are not sent (the default).
741 If this option is on, permanent delivery errors are treated as
742 temporary, i. e. mail is kept on the upstream server. Useful
743 during testing and after configuration changes, and on by
745 If this option is off, permanent delivery errors delete
746 undeliverable mail from the upstream.
749 If false (the default) fetchmail generates a Received line into
750 each message and generates a HELO from the machine it is running on.
751 If true, fetchmail generates no Received line and HELOs as if it were
754 In the `Remote Mail Configurations' panel, you can:
756 1. Enter the name of a new remote mail server you want fetchmail to query.
758 To do this, simply enter a label for the poll configuration in the
759 `New Server:' box. The label should be a DNS name of the server (unless
760 you are using ssh or some other tunneling method and will fill in the `via'
761 option on the site configuration screen).
763 2. Change the configuration of an existing site.
765 To do this, find the site's label in the listbox and double-click it.
766 This will take you to a site configuration dialogue.
770 class ConfigurationEdit(Frame, MyWidget):
771 def __init__(self, configuration, outfile, master, onexit):
773 self.configuration = configuration
774 self.outfile = outfile
775 self.container = master
777 ConfigurationEdit.mode_to_help = {
778 'novice':configure_novice_help, 'expert':configure_expert_help
781 def server_edit(self, sitename):
782 self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
784 def server_delete(self, sitename):
786 for user in self.subwidgets.keys():
788 del self.configuration[sitename]
792 def edit(self, mode):
794 Frame.__init__(self, self.container)
795 self.master.title('fetchmail ' + self.mode + ' configurator');
796 self.master.iconname('fetchmail ' + self.mode + ' configurator');
797 self.master.protocol('WM_DELETE_WINDOW', self.nosave)
798 self.keepalive = [] # Use this to anchor the PhotoImage object
799 make_icon_window(self, fetchmail_icon)
801 self.post(Configuration, 'configuration')
804 'Configurator ' + self.mode + ' Controls',
805 ConfigurationEdit.mode_to_help[self.mode],
808 gf = Frame(self, relief=RAISED, bd = 5)
810 text='Fetchmail Run Controls',
811 bd=2).pack(side=TOP, pady=10)
816 if self.mode != 'novice':
818 log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
819 log.pack(side=RIGHT, anchor=E)
821 # Set the poll interval
822 de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
823 de.pack(side=RIGHT, anchor=E)
828 if self.mode != 'novice':
831 {'text':'Bounces to sender?',
832 'variable':self.bouncemail,
833 'relief':GROOVE}).pack(side=LEFT, anchor=W)
838 {'text':'Send spam bounces?',
839 'variable':self.spambounce,
840 'relief':GROOVE}).pack(side=LEFT, anchor=W)
845 {'text':'Treat permanent errors as temporary?',
846 'variable':self.softbounce,
847 'relief':GROOVE}).pack(side=LEFT, anchor=W)
852 {'text':'Log to syslog?',
853 'variable':self.syslog,
854 'relief':GROOVE}).pack(side=LEFT, anchor=W)
855 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
856 log.pack(side=RIGHT, anchor=E)
860 {'text':'Invisible mode?',
861 'variable':self.invisible,
862 'relief':GROOVE}).pack(side=LEFT, anchor=W)
864 log = LabeledEntry(gf, ' Idfile:', self.idfile, '14')
865 log.pack(side=RIGHT, anchor=E)
869 # Expert mode allows us to edit multiple sites
870 lf = Frame(self, relief=RAISED, bd=5)
872 text='Remote Mail Server Configurations',
873 bd=2).pack(side=TOP, pady=10)
874 ListEdit('New Server:',
875 map(lambda x: x.pollname, self.configuration.servers),
876 lambda site, self=self: self.server_edit(site),
877 lambda site, self=self: self.server_delete(site),
882 for sitename in self.subwidgets.keys():
883 self.subwidgets[sitename].destruct()
884 self.master.destroy()
888 if ConfirmQuit(self, self.mode + " configuration editor"):
892 for sitename in self.subwidgets.keys():
893 self.subwidgets[sitename].save()
894 self.fetch(Configuration, 'configuration')
898 elif not os.path.isfile(self.outfile) or Dialog(self,
899 title = 'Overwrite existing run control file?',
900 text = 'Really overwrite existing run control file?',
902 strings = ('Yes', 'No'),
903 default = 1).num == 0:
905 os.rename(self.outfile, self.outfile + "~")
906 # Pre-1.5.2 compatibility...
909 oldumask = os.umask(077)
910 fm = open(self.outfile, 'w')
915 os.chmod(self.outfile, 0600)
916 fm.write("# Configuration created %s by fetchmailconf %s\n" % (time.ctime(time.time()), version))
917 fm.write(`self.configuration`)
923 # Server editing stuff.
926 'title' : 'Remote site help',
927 'banner': 'Remote sites',
929 When you add a site name to the list here,
930 you initialize an entry telling fetchmail
931 how to poll a new site.
933 When you select a sitename (by double-
934 clicking it, or by single-clicking to
935 select and then clicking the Edit button),
936 you will open a window to configure that
941 'title' : 'Server options help',
942 'banner': 'Server Options',
944 The server options screen controls fetchmail
945 options that apply to one of your mailservers.
947 Once you have a mailserver configuration set
948 up as you like it, you can select `OK' to
949 store it in the server list maintained in
950 the main configuration window.
952 If you wish to discard changes to a server
953 configuration, select `Quit'.
957 'title' : 'Run Control help',
958 'banner': 'Run Controls',
960 If the `Poll normally' checkbox is on, the host is polled as part of
961 the normal operation of fetchmail when it is run with no arguments.
962 If it is off, fetchmail will only query this host when it is given as
963 a command-line argument.
965 The `Retrieve Error Policy' specifies how server errors during
966 message retrieval are handled. The default behaviour is to abort the
967 current session. Both the continue and markseen options will skip
968 the message with the error, but continue the session allowing for
969 downloading of subsequent messages. Additionally, the markseen
970 option will mark the skipped message as seen.
972 The `True name of server' box should specify the actual DNS name
973 to query. By default this is the same as the poll name.
975 Normally each host described in the file is queried once each
976 poll cycle. If `Cycles to skip between polls' is greater than 0,
977 that's the number of poll cycles that are skipped between the
978 times this post is actually polled.
980 The `Server timeout' is the number of seconds fetchmail will wait
981 for a reply from the mailserver before concluding it is hung and
986 'title' : 'Protocol and Port help',
987 'banner': 'Protocol and Port',
989 These options control the remote-mail protocol
990 and TCP/IP service port used to query this
993 If you click the `Probe for supported protocols'
994 button, fetchmail will try to find you the most
995 capable server on the selected host (this will
996 only work if you're conncted to the Internet).
997 The probe only checks for ordinary IMAP and POP
998 protocols; fortunately these are the most
999 frequently supported.
1001 The `Protocol' button bar offers you a choice of
1002 all the different protocols available. The `auto'
1003 protocol is the default mode; it probes the host
1004 ports for POP3 and IMAP to see if either is
1007 Normally the TCP/IP service port to use is
1008 dictated by the protocol choice. The `Service'
1009 field (only present in expert mode) lets you
1010 set a non-standard service (port).
1014 'title' : 'Security option help',
1015 'banner': 'Security',
1017 The 'authorization mode' allows you to choose the
1018 mode that fetchmail uses to log in to your server. You
1019 can usually leave this at 'any', but you will have to pick
1020 'NTLM' and 'MSN' manually for the nonce.
1022 The 'interface' option allows you to specify a range
1023 of IP addresses to monitor for activity. If these
1024 addresses are not active, fetchmail will not poll.
1025 Specifying this may protect you from a spoofing attack
1026 if your client machine has more than one IP gateway
1027 address and some of the gateways are to insecure nets.
1029 The `monitor' option, if given, specifies the only
1030 device through which fetchmail is permitted to connect
1031 to servers. This option may be used to prevent
1032 fetchmail from triggering an expensive dial-out if the
1033 interface is not already active.
1035 The `interface' and `monitor' options are available
1036 only for Linux and freeBSD systems. See the fetchmail
1037 manual page for details on these.
1039 The ssl option enables SSL communication with a mailserver
1040 supporting Secure Sockets Layer. The sslkey and sslcert options
1041 declare key and certificate files for use with SSL.
1042 The sslcertck option enables strict checking of SSL server
1043 certificates (and sslcertpath gives the trusted certificate
1044 directory). The sslcommonname option helps if the server is
1045 misconfigured and returning "Server CommonName mismatch"
1046 warnings. With sslfingerprint, you can specify a finger-
1047 print the server's key is checked against.
1051 'title' : 'Multidrop option help',
1052 'banner': 'Multidrop',
1054 These options are only useful with multidrop mode.
1055 See the manual page for extended discussion.
1059 'title' : 'User list help',
1060 'banner': 'User list',
1062 When you add a user name to the list here,
1063 you initialize an entry telling fetchmail
1064 to poll the site on behalf of the new user.
1066 When you select a username (by double-
1067 clicking it, or by single-clicking to
1068 select and then clicking the Edit button),
1069 you will open a window to configure the
1070 user's options on that site.
1073 class ServerEdit(Frame, MyWidget):
1074 def __init__(self, host, parent):
1075 self.parent = parent
1077 self.subwidgets = {}
1078 for site in parent.configuration.servers:
1079 if site.pollname == host:
1081 if (self.server == None):
1082 self.server = Server()
1083 self.server.pollname = host
1084 self.server.via = None
1085 parent.configuration.servers.append(self.server)
1087 def edit(self, mode, master=None):
1088 Frame.__init__(self, master)
1090 self.master.title('Fetchmail host ' + self.server.pollname);
1091 self.master.iconname('Fetchmail host ' + self.server.pollname);
1092 self.post(Server, 'server')
1093 self.makeWidgets(self.server.pollname, mode)
1094 self.keepalive = [] # Use this to anchor the PhotoImage object
1095 make_icon_window(self, fetchmail_icon)
1098 # self.wait_window()
1102 for username in self.subwidgets.keys():
1103 self.subwidgets[username].destruct()
1104 del self.parent.subwidgets[self.server.pollname]
1105 self.master.destroy()
1108 if ConfirmQuit(self, 'server option editing'):
1112 self.fetch(Server, 'server')
1113 for username in self.subwidgets.keys():
1114 self.subwidgets[username].save()
1117 def defaultPort(self):
1118 proto = self.protocol.get()
1119 # Callback to reset the port number whenever the protocol type changes.
1120 # We used to only reset the port if it had a default (zero) value.
1121 # This turns out to be a bad idea especially in Novice mode -- if
1122 # you set POP3 and then set IMAP, the port invisibly remained 110.
1123 # Now we reset unconditionally on the theory that if you're setting
1124 # a custom port number you should be in expert mode and playing
1125 # close enough attention to notice this...
1126 self.service.set(defaultports[proto])
1128 def user_edit(self, username, mode):
1129 self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1131 def user_delete(self, username):
1132 if self.subwidgets.has_key(username):
1133 self.subwidgets[username].destruct()
1134 del self.server[username]
1136 def makeWidgets(self, host, mode):
1137 topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1139 leftwin = Frame(self);
1142 if mode != 'novice':
1143 ctlwin = Frame(leftwin, relief=RAISED, bd=5)
1144 Label(ctlwin, text="Run Controls").pack(side=TOP)
1145 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
1146 Checkbutton(ctlwin, text='Pass messages with bad headers?',
1147 variable=self.badheader).pack(side=TOP)
1148 retrieveerrorlist = ['abort', 'continue', 'markseen']
1149 Label(ctlwin, text="Retrieve Error Policy").pack(side=TOP)
1150 ButtonBar(ctlwin, '', self.retrieveerror, retrieveerrorlist, 1, None)
1151 LabeledEntry(ctlwin, 'True name of ' + host + ':',
1152 self.via, leftwidth).pack(side=TOP, fill=X)
1153 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
1154 self.interval, leftwidth).pack(side=TOP, fill=X)
1155 LabeledEntry(ctlwin, 'Server timeout (seconds):',
1156 self.timeout, leftwidth).pack(side=TOP, fill=X)
1157 Button(ctlwin, text='Help', fg='blue',
1158 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
1161 # Compute the available protocols from the compile-time options
1162 protolist = ['auto']
1163 if 'pop3' in feature_options:
1164 protolist = protolist + ["POP3", "KPOP"]
1165 if 'sdps' in feature_options:
1166 protolist.append("SDPS")
1167 if 'imap' in feature_options:
1168 protolist.append("IMAP")
1169 if 'etrn' in feature_options:
1170 protolist.append("ETRN")
1171 if 'odmr' in feature_options:
1172 protolist.append("ODMR")
1174 protwin = Frame(leftwin, relief=RAISED, bd=5)
1175 Label(protwin, text="Protocol").pack(side=TOP)
1176 ButtonBar(protwin, '',
1177 self.protocol, protolist, 2,
1179 if mode != 'novice':
1180 LabeledEntry(protwin, 'On server TCP/IP service:',
1181 self.service, leftwidth).pack(side=TOP, fill=X)
1183 Button(protwin, text='Probe for supported protocols', fg='blue',
1184 command=self.autoprobe).pack(side=LEFT)
1185 Button(protwin, text='Help', fg='blue',
1186 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
1187 protwin.pack(fill=X)
1189 userwin = Frame(leftwin, relief=RAISED, bd=5)
1190 Label(userwin, text="User entries for " + host).pack(side=TOP)
1191 ListEdit("New user: ",
1192 map(lambda x: x.remote, self.server.users),
1193 lambda u, m=mode, s=self: s.user_edit(u, m),
1194 lambda u, s=self: s.user_delete(u),
1196 userwin.pack(fill=X)
1198 leftwin.pack(side=LEFT, anchor=N, fill=X);
1200 if mode != 'novice':
1201 rightwin = Frame(self);
1203 mdropwin = Frame(rightwin, relief=RAISED, bd=5)
1204 Label(mdropwin, text="Multidrop options").pack(side=TOP)
1205 LabeledEntry(mdropwin, 'Envelope address header:',
1206 self.envelope, '22').pack(side=TOP, fill=X)
1207 LabeledEntry(mdropwin, 'Envelope headers to skip:',
1208 self.envskip, '22').pack(side=TOP, fill=X)
1209 LabeledEntry(mdropwin, 'Name prefix to strip:',
1210 self.qvirtual, '22').pack(side=TOP, fill=X)
1211 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
1212 variable=self.dns).pack(side=TOP)
1213 Label(mdropwin, text="DNS aliases").pack(side=TOP)
1214 ListEdit("New alias: ", self.server.aka, None, None, mdropwin, None)
1215 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
1216 ListEdit("New domain: ",
1217 self.server.localdomains, None, None, mdropwin, multihelp)
1218 mdropwin.pack(fill=X)
1220 if os_type in ('linux', 'freebsd'):
1221 secwin = Frame(rightwin, relief=RAISED, bd=5)
1222 Label(secwin, text="Security").pack(side=TOP)
1223 # Don't actually let users set this. KPOP sets it implicitly
1224 ButtonBar(secwin, 'Authorization mode:',
1225 self.auth, authlist, 2, None).pack(side=TOP)
1226 if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
1227 LabeledEntry(secwin, 'IP range to check before poll:',
1228 self.interface, leftwidth).pack(side=TOP, fill=X)
1229 if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
1230 LabeledEntry(secwin, 'Interface to monitor:',
1231 self.monitor, leftwidth).pack(side=TOP, fill=X)
1232 # Someday this should handle Kerberos 5 too
1233 if 'kerberos' in feature_options:
1234 LabeledEntry(secwin, 'Principal:',
1235 self.principal, '12').pack(side=TOP, fill=X)
1236 # ESMTP authentication
1237 LabeledEntry(secwin, 'ESMTP name:',
1238 self.esmtpname, '12').pack(side=TOP, fill=X)
1239 LabeledEntry(secwin, 'ESMTP password:',
1240 self.esmtppassword, '12').pack(side=TOP, fill=X)
1241 Button(secwin, text='Help', fg='blue',
1242 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
1245 rightwin.pack(side=LEFT, anchor=N);
1247 def autoprobe(self):
1248 # Note: this only handles case (1) near fetchmail.c:1032
1249 # We're assuming people smart enough to set up ssh tunneling
1250 # won't need autoprobing.
1252 realhost = self.server.via
1254 realhost = self.server.pollname
1256 for protocol in ("IMAP","POP3"):
1257 service = defaultports[protocol]
1258 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1260 sock.connect((realhost, ianaservices[service]))
1261 greetline = sock.recv(1024)
1267 confwin = Toplevel()
1268 if greetline == None:
1269 title = "Autoprobe of " + realhost + " failed"
1271 Fetchmailconf didn't find any mailservers active.
1272 This could mean the host doesn't support any,
1273 or that your Internet connection is down, or
1274 that the host is so slow that the probe timed
1275 out before getting a response.
1279 # OK, now try to recognize potential problems
1281 ### POP3 servers start here
1283 if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
1284 warnings = warnings + """
1285 This appears to be an old version of the UC Davis POP server. These are
1286 dangerously unreliable (among other problems, they may drop your mailbox
1287 on the floor if your connection is interrupted during the session).
1289 It is strongly recommended that you find a better POP3 server. The fetchmail
1290 FAQ includes pointers to good ones.
1293 if string.find(greetline, "comcast.net") > 0:
1294 warnings = warnings + """
1295 The Comcast Maillennium POP3 server only returns the first 80K of a long
1296 message retrieved with TOP. Its response to RETR is normal, so use the
1300 # Steve VanDevender <stevev@efn.org> writes:
1301 # The only system I have seen this happen with is cucipop-1.31
1302 # under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
1303 # 2.x and probably quite a few other systems. It appears to be a
1304 # bug or bad interaction with the SunOS realloc() -- it turns out
1305 # that internally cucipop does allocate a certain data structure in
1306 # multiples of 16, using realloc() to bump it up to the next
1307 # multiple if it needs more.
1309 # The distinctive symptom is that when there are 16 messages in the
1310 # inbox, you can RETR and DELE all 16 messages successfully, but on
1311 # QUIT cucipop returns something like "-ERR Error locking your
1312 # mailbox" and aborts without updating it.
1314 # The cucipop banner looks like:
1316 # +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1318 if string.find(greetline, "Cubic Circle") > 0:
1319 warnings = warnings + """
1320 I see your server is running cucipop. Better make sure the server box
1321 isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
1322 under that version, and doesn't cope with the result gracefully. Newer
1323 SunOS and Solaris machines run cucipop OK.
1325 Also, some versions of cucipop don't assert an exclusive lock on your
1326 mailbox when it's being queried. This means that if you have more than
1327 one fetchmail query running against the same mailbox, bad things can happen.
1329 if string.find(greetline, "David POP3 Server") > 0:
1330 warnings = warnings + """
1331 This POP3 server is badly broken. You should get rid of it -- and the
1332 brain-dead Microsoft operating system it rode in on.
1335 # The greeting line on the server known to be buggy is:
1336 # +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
1338 if string.find(greetline, "FTGate") > 0:
1339 warnings = warnings + """
1340 This POP server has a weird bug; it says OK twice in response to TOP.
1341 Its response to RETR is normal, so use the `fetchall' option.
1344 if string.find(greetline, " geonet.de") > 0:
1345 warnings = warnings + """
1346 You appear to be using geonet. As of late 2002, the TOP command on
1347 geonet's POP3 is broken. Use the fetchall option.
1350 if string.find(greetline, "OpenMail") > 0:
1351 warnings = warnings + """
1352 You appear to be using some version of HP OpenMail. Many versions of
1353 OpenMail do not process the "TOP" command correctly; the symptom is that
1354 only the header and first line of each message is retrieved. To work
1355 around this bug, turn on `fetchall' on all user entries associated with
1359 if string.find(greetline, "Escape character is") > 0:
1360 warnings = warnings + """
1361 Your greeting line looks like it was written by a fetid pile of
1362 camel dung identified to me as `popa3d written by Solar Designer'.
1363 Beware! The UIDL support in this thing is known to be completely broken,
1364 and other things probably are too.
1367 if string.find(greetline, "MercuryP/NLM v1.48") > 0:
1368 warnings = warnings + """
1369 This is not a POP3 server. It has delusions of being one, but after
1370 RETR all messages are automatically marked to be deleted. The only
1371 way to prevent this is to issue an RSET before leaving the server.
1372 Fetchmail does this, but we suspect this is probably broken in lots
1376 if string.find(greetline, "POP-Max") > 0:
1377 warnings = warnings + """
1378 The Mail Max POP3 server screws up on mail with attachments. It
1379 reports the message size with attachments included, but doesn't
1380 download them on a RETR or TOP (this violates the IMAP RFCs). It also
1381 doesn't implement TOP correctly. You should get rid of it -- and the
1382 brain-dead NT server it rode in on.
1385 if string.find(greetline, "POP3 Server Ready") > 0:
1386 warnings = warnings + """
1387 Some server that uses this greeting line has been observed to choke on
1388 TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
1391 if string.find(greetline, "QPOP") > 0:
1392 warnings = warnings + """
1393 This appears to be a version of Eudora qpopper. That's good. Fetchmail
1394 knows all about qpopper. However, be aware that the 2.53 version of
1395 qpopper does something odd that causes fetchmail to hang with a socket
1396 error on very large messages. This is probably not a fetchmail bug, as
1397 it has been observed with fetchpop. The fix is to upgrade to qpopper
1398 3.0beta or a more recent version. Better yet, switch to IMAP.
1401 if string.find(greetline, " sprynet.com") > 0:
1402 warnings = warnings + """
1403 You appear to be using a SpryNet server. In mid-1999 it was reported that
1404 the SpryNet TOP command marks messages seen. Therefore, for proper error
1405 recovery in the event of a line drop, it is strongly recommended that you
1406 turn on `fetchall' on all user entries associated with this server.
1409 if string.find(greetline, "TEMS POP3") > 0:
1410 warnings = warnings + """
1411 Your POP3 server has "TEMS" in its header line. At least one such
1412 server does not process the "TOP" command correctly; the symptom is
1413 that fetchmail hangs when trying to retrieve mail. To work around
1414 this bug, turn on `fetchall' on all user entries associated with this
1418 if string.find(greetline, " spray.se") > 0:
1419 warnings = warnings + """
1420 Your POP3 server has "spray.se" in its header line. In May 2000 at
1421 least one such server did not process the "TOP" command correctly; the
1422 symptom is that messages are treated as headerless. To work around
1423 this bug, turn on `fetchall' on all user entries associated with this
1427 if string.find(greetline, " usa.net") > 0:
1428 warnings = warnings + """
1429 You appear to be using USA.NET's free mail service. Their POP3 servers
1430 (at least as of the 2.2 version in use mid-1998) are quite flaky, but
1431 fetchmail can compensate. They seem to require that fetchall be switched on
1432 (otherwise you won't necessarily see all your mail, not even new mail).
1433 They also botch the TOP command the fetchmail normally uses for retrieval
1434 (it only retrieves about 10 lines rather than the number specified).
1435 Turning on fetchall will disable the use of TOP.
1437 Therefore, it is strongly recommended that you turn on `fetchall' on all
1438 user entries associated with this server.
1441 if string.find(greetline, " Novonyx POP3") > 0:
1442 warnings = warnings + """
1443 Your mailserver is running Novonyx POP3. This server, at least as of
1444 version 2.17, seems to have problems handling and reporting seen bits.
1445 You may have to use the fetchall option.
1448 if string.find(greetline, " IMS POP3") > 0:
1449 warnings = warnings + """
1450 Some servers issuing the greeting line 'IMS POP3' have been known to
1451 do byte-stuffing incorrectly. This means that if a message you receive
1452 has a . (period) at start of line, fetchmail will become confused and
1453 probably wedge itself. (This bug was recorded on IMS POP3 0.86.)
1457 ### IMAP servers start here
1459 if string.find(greetline, "GroupWise") > 0:
1460 warnings = warnings + """
1461 The Novell GroupWise IMAP server would be better named GroupFoolish;
1462 it is (according to the designer of IMAP) unusably broken. Among
1463 other things, it doesn't include a required content length in its
1464 BODY[TEXT] response.<p>
1466 Fetchmail works around this problem, but we strongly recommend voting
1467 with your dollars for a server that isn't brain-dead. If you stick
1468 with code as shoddy as GroupWise seems to be, you will probably pay
1469 for it with other problems.<p>
1472 if string.find(greetline, "InterChange") > 0:
1473 warnings = warnings + """
1475 The InterChange IMAP server at release levels below 3.61.08 screws up
1476 on mail with attachments. It doesn't fetch them if you give it a
1477 BODY[TEXT] request, though it does if you request RFC822.TEXT.
1478 According to the IMAP RFCs and their maintainer these should be
1479 equivalent -- and we can't drop the BODY[TEXT] form because M$
1480 Exchange (quite legally under RFC2062) rejectsit. The InterChange
1481 folks claim to have fixed this bug in 3.61.08.
1484 if string.find(greetline, "Imail") > 0:
1485 warnings = warnings + """
1486 We've seen a bug report indicating that this IMAP server (at least as of
1487 version 5.0.7) returns an invalid body size for messages with MIME
1488 attachments; the effect is to drop the attachments on the floor. We
1489 recommend you upgrade to a non-broken IMAP server.
1492 if string.find(greetline, "Domino IMAP4") > 0:
1493 warnings = warnings + """
1494 Your IMAP server appears to be Lotus Domino. This server, at least up
1495 to version 5.0.2, has a bug in its generation of MIME boundaries (see
1496 the details in the fetchmail FAQ). As a result, even MIME aware MUAs
1497 will see attachments as part of the message text. If your Domino server's
1498 POP3 facility is enabled, we recommend you fall back on it.
1502 ### Checks for protocol variants start here
1504 closebrak = string.find(greetline, ">")
1505 if closebrak > 0 and greetline[closebrak+1] == "\r":
1506 warnings = warnings + """
1507 It looks like you could use APOP on this server and avoid sending it your
1508 password in clear. You should talk to the mailserver administrator about
1512 if string.find(greetline, "IMAP4rev1") > 0:
1513 warnings = warnings + """
1514 I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
1515 remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
1516 has therefore been extremely well tested with this class of server.
1520 warnings = warnings + """
1521 Fetchmail doesn't know anything special about this server type.
1525 # Display success window with warnings
1526 title = "Autoprobe of " + realhost + " succeeded"
1527 confirm = "The " + protocol + " server said:\n\n" + greetline + warnings
1528 self.protocol.set(protocol)
1529 self.service.set(defaultports[protocol])
1530 confwin.title(title)
1531 confwin.iconname(title)
1532 Label(confwin, text=title).pack()
1533 Message(confwin, text=confirm, width=600).pack()
1534 Button(confwin, text='Done',
1535 command=lambda x=confwin: x.destroy(), bd=2).pack()
1538 # User editing stuff
1542 'title' : 'User option help',
1543 'banner': 'User options',
1545 You may use this panel to set options
1546 that may differ between individual
1549 Once you have a user configuration set
1550 up as you like it, you can select `OK' to
1551 store it in the user list maintained in
1552 the site configuration window.
1554 If you wish to discard the changes you have
1555 made to user options, select `Quit'.
1559 'title' : 'Local name help',
1560 'banner': 'Local names',
1562 The local name(s) in a user entry are the
1563 people on the client machine who should
1564 receive mail from the poll described.
1566 Note: if a user entry has more than one
1567 local name, messages will be retrieved
1568 in multidrop mode. This complicates
1569 the configuration issues; see the manual
1570 page section on multidrop mode.
1572 Warning: Be careful with local names
1573 such as foo@bar.com, as that can cause
1574 the mail to be sent to foo@bar.com instead
1575 of sending it to your local system.
1578 class UserEdit(Frame, MyWidget):
1579 def __init__(self, username, parent):
1580 self.parent = parent
1582 for user in parent.server.users:
1583 if user.remote == username:
1585 if self.user == None:
1587 self.user.remote = username
1588 self.user.localnames = [username]
1589 parent.server.users.append(self.user)
1591 def edit(self, mode, master=None):
1592 Frame.__init__(self, master)
1594 self.master.title('Fetchmail user ' + self.user.remote
1595 + ' querying ' + self.parent.server.pollname);
1596 self.master.iconname('Fetchmail user ' + self.user.remote);
1597 self.post(User, 'user')
1598 self.makeWidgets(mode, self.parent.server.pollname)
1599 self.keepalive = [] # Use this to anchor the PhotoImage object
1600 make_icon_window(self, fetchmail_icon)
1603 # self.wait_window()
1607 # Yes, this test can fail -- if you delete the parent window.
1608 if self.parent.subwidgets.has_key(self.user.remote):
1609 del self.parent.subwidgets[self.user.remote]
1610 self.master.destroy()
1613 if ConfirmQuit(self, 'user option editing'):
1618 for x in self.user.localnames: ok = ok + (string.find(x, '@') != -1)
1619 if ok == 0 or Dialog(self,
1620 title = "Really accept an embedded '@' ?",
1621 text = "Local names with an embedded '@', such as in foo@bar "
1622 "might result in your mail being sent to foo@bar.com "
1623 "instead of your local system.\n Are you sure you want "
1624 "a local user name with an '@' in it?",
1625 bitmap = 'question',
1626 strings = ('Yes', 'No'),
1627 default = 1).num == 0:
1628 self.fetch(User, 'user')
1631 def makeWidgets(self, mode, servername):
1632 dispose_window(self,
1633 "User options for " + self.user.remote + " querying " + servername,
1636 if mode != 'novice':
1637 leftwin = Frame(self);
1641 secwin = Frame(leftwin, relief=RAISED, bd=5)
1642 Label(secwin, text="Authentication").pack(side=TOP)
1643 LabeledEntry(secwin, 'Password:',
1644 self.password, '12').pack(side=TOP, fill=X)
1645 secwin.pack(fill=X, anchor=N)
1647 if 'ssl' in feature_options or 'ssl' in dictmembers:
1648 sslwin = Frame(leftwin, relief=RAISED, bd=5)
1649 Checkbutton(sslwin, text="Use SSL?",
1650 variable=self.ssl).pack(side=TOP, fill=X)
1651 LabeledEntry(sslwin, 'SSL key:',
1652 self.sslkey, '14').pack(side=TOP, fill=X)
1653 LabeledEntry(sslwin, 'SSL certificate:',
1654 self.sslcert, '14').pack(side=TOP, fill=X)
1655 Checkbutton(sslwin, text="Check server SSL certificate?",
1656 variable=self.sslcertck).pack(side=TOP, fill=X)
1657 LabeledEntry(sslwin, 'SSL trusted certificate directory:',
1658 self.sslcertpath, '14').pack(side=TOP, fill=X)
1659 LabeledEntry(sslwin, 'SSL CommonName:',
1660 self.sslcommonname, '14').pack(side=TOP, fill=X)
1661 LabeledEntry(sslwin, 'SSL key fingerprint:',
1662 self.sslfingerprint, '14').pack(side=TOP, fill=X)
1663 sslwin.pack(fill=X, anchor=N)
1665 names = Frame(leftwin, relief=RAISED, bd=5)
1666 Label(names, text="Local names").pack(side=TOP)
1667 ListEdit("New name: ",
1668 self.user.localnames, None, None, names, localhelp)
1669 names.pack(fill=X, anchor=N)
1671 if mode != 'novice':
1672 targwin = Frame(leftwin, relief=RAISED, bd=5)
1673 Label(targwin, text="Forwarding Options").pack(side=TOP)
1674 Label(targwin, text="Listeners to forward to").pack(side=TOP)
1675 ListEdit("New listener:",
1676 self.user.smtphunt, None, None, targwin, None)
1677 Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
1678 ListEdit("Domains:",
1679 self.user.fetchdomains, None, None, targwin, None)
1680 LabeledEntry(targwin, 'Use domain on RCPT TO line:',
1681 self.smtpaddress, '26').pack(side=TOP, fill=X)
1682 LabeledEntry(targwin, 'Set fixed RCPT TO address:',
1683 self.smtpname, '26').pack(side=TOP, fill=X)
1684 LabeledEntry(targwin, 'Connection setup command:',
1685 self.preconnect, '26').pack(side=TOP, fill=X)
1686 LabeledEntry(targwin, 'Connection wrapup command:',
1687 self.postconnect, '26').pack(side=TOP, fill=X)
1688 LabeledEntry(targwin, 'Local delivery agent:',
1689 self.mda, '26').pack(side=TOP, fill=X)
1690 LabeledEntry(targwin, 'BSMTP output file:',
1691 self.bsmtp, '26').pack(side=TOP, fill=X)
1692 LabeledEntry(targwin, 'Listener spam-block codes:',
1693 self.antispam, '26').pack(side=TOP, fill=X)
1694 LabeledEntry(targwin, 'Pass-through properties:',
1695 self.properties, '26').pack(side=TOP, fill=X)
1696 Checkbutton(targwin, text="Use LMTP?",
1697 variable=self.lmtp).pack(side=TOP, fill=X)
1698 targwin.pack(fill=X, anchor=N)
1700 if mode != 'novice':
1701 leftwin.pack(side=LEFT, fill=X, anchor=N)
1702 rightwin = Frame(self)
1706 optwin = Frame(rightwin, relief=RAISED, bd=5)
1707 Label(optwin, text="Processing Options").pack(side=TOP)
1708 Checkbutton(optwin, text="Suppress deletion of messages after reading",
1709 variable=self.keep).pack(side=TOP, anchor=W)
1710 Checkbutton(optwin, text="Fetch old messages as well as new",
1711 variable=self.fetchall).pack(side=TOP, anchor=W)
1712 if mode != 'novice':
1713 Checkbutton(optwin, text="Flush seen messages before retrieval",
1714 variable=self.flush).pack(side=TOP, anchor=W)
1715 Checkbutton(optwin, text="Flush oversized messages before retrieval",
1716 variable=self.limitflush).pack(side=TOP, anchor=W)
1717 Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply",
1718 variable=self.rewrite).pack(side=TOP, anchor=W)
1719 Checkbutton(optwin, text="Force CR/LF at end of each line",
1720 variable=self.forcecr).pack(side=TOP, anchor=W)
1721 Checkbutton(optwin, text="Strip CR from end of each line",
1722 variable=self.stripcr).pack(side=TOP, anchor=W)
1723 Checkbutton(optwin, text="Pass 8 bits even though SMTP says 7BIT",
1724 variable=self.pass8bits).pack(side=TOP, anchor=W)
1725 Checkbutton(optwin, text="Undo MIME armoring on header and body",
1726 variable=self.mimedecode).pack(side=TOP, anchor=W)
1727 Checkbutton(optwin, text="Drop Status lines from forwarded messages",
1728 variable=self.dropstatus).pack(side=TOP, anchor=W)
1729 Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
1730 variable=self.dropdelivered).pack(side=TOP, anchor=W)
1733 if mode != 'novice':
1734 limwin = Frame(rightwin, relief=RAISED, bd=5)
1735 Label(limwin, text="Resource Limits").pack(side=TOP)
1736 LabeledEntry(limwin, 'Message size limit:',
1737 self.limit, '30').pack(side=TOP, fill=X)
1738 LabeledEntry(limwin, 'Size warning interval:',
1739 self.warnings, '30').pack(side=TOP, fill=X)
1740 LabeledEntry(limwin, 'Max messages to fetch per poll:',
1741 self.fetchlimit, '30').pack(side=TOP, fill=X)
1742 LabeledEntry(limwin, 'Max message sizes to fetch per transaction:',
1743 self.fetchsizelimit, '30').pack(side=TOP, fill=X)
1744 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1745 LabeledEntry(limwin, 'Use fast UIDL:',
1746 self.fastuidl, '30').pack(side=TOP, fill=X)
1747 LabeledEntry(limwin, 'Max messages to forward per poll:',
1748 self.batchlimit, '30').pack(side=TOP, fill=X)
1749 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1750 LabeledEntry(limwin, 'Interval between expunges:',
1751 self.expunge, '30').pack(side=TOP, fill=X)
1752 Checkbutton(limwin, text="Idle after each poll (IMAP only)",
1753 variable=self.idle).pack(side=TOP, anchor=W)
1756 if self.parent.server.protocol == 'IMAP':
1757 foldwin = Frame(rightwin, relief=RAISED, bd=5)
1758 Label(foldwin, text="Remote folders (IMAP only)").pack(side=TOP)
1759 ListEdit("New folder:", self.user.mailboxes,
1760 None, None, foldwin, None)
1761 foldwin.pack(fill=X, anchor=N)
1763 if mode != 'novice':
1764 rightwin.pack(side=LEFT)
1770 # Top-level window that offers either novice or expert mode
1771 # (but not both at once; it disappears when one is selected).
1774 class Configurator(Frame):
1775 def __init__(self, outfile, master, onexit, parent):
1776 Frame.__init__(self, master)
1777 self.outfile = outfile
1778 self.onexit = onexit
1779 self.parent = parent
1780 self.master.title('fetchmail configurator');
1781 self.master.iconname('fetchmail configurator');
1783 self.keepalive = [] # Use this to anchor the PhotoImage object
1784 make_icon_window(self, fetchmail_icon)
1786 Message(self, text="""
1787 Use `Novice Configuration' for basic fetchmail setup;
1788 with this, you can easily set up a single-drop connection
1789 to one remote mail server.
1790 """, width=600).pack(side=TOP)
1791 Button(self, text='Novice Configuration',
1792 fg='blue', command=self.novice).pack()
1794 Message(self, text="""
1795 Use `Expert Configuration' for advanced fetchmail setup,
1796 including multiple-site or multidrop connections.
1797 """, width=600).pack(side=TOP)
1798 Button(self, text='Expert Configuration',
1799 fg='blue', command=self.expert).pack()
1801 Message(self, text="""
1802 Or you can just select `Quit' to leave the configurator now and
1803 return to the main panel.
1804 """, width=600).pack(side=TOP)
1805 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1806 master.protocol("WM_DELETE_WINDOW", self.leave)
1809 self.master.destroy()
1810 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1813 self.master.destroy()
1814 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1817 self.master.destroy()
1820 # Run a command in a scrolling text widget, displaying its output
1822 class RunWindow(Frame):
1823 def __init__(self, command, master, parent):
1824 Frame.__init__(self, master)
1825 self.master = master
1826 self.master.title('fetchmail run window');
1827 self.master.iconname('fetchmail run window');
1830 text="Running "+command,
1831 bd=2).pack(side=TOP, pady=10)
1832 self.keepalive = [] # Use this to anchor the PhotoImage object
1833 make_icon_window(self, fetchmail_icon)
1835 # This is a scrolling text window
1836 textframe = Frame(self)
1837 scroll = Scrollbar(textframe)
1838 self.textwidget = Text(textframe, setgrid=TRUE)
1839 textframe.pack(side=TOP, expand=YES, fill=BOTH)
1840 self.textwidget.config(yscrollcommand=scroll.set)
1841 self.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
1842 scroll.config(command=self.textwidget.yview)
1843 scroll.pack(side=RIGHT, fill=BOTH)
1844 textframe.pack(side=TOP)
1846 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1848 self.update() # Draw widget before executing fetchmail
1850 # Always look for a runnable command in the directory we're running in
1851 # first. This avoids some obscure version-skew errors that can occur
1852 # if you pick up an old fetchmail from the standard system locations.
1853 os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"]
1854 child_stdout = os.popen(command + " 2>&1 </dev/null", "r")
1856 ch = child_stdout.read(1)
1859 self.textwidget.insert(END, ch)
1860 self.textwidget.insert(END, "Done.")
1861 self.textwidget.see(END);
1864 self.master.destroy()
1866 # Here's where we choose either configuration or launching
1868 class MainWindow(Frame):
1869 def __init__(self, outfile, master=None):
1870 Frame.__init__(self, master)
1871 self.outfile = outfile
1872 self.master.title('fetchmail launcher');
1873 self.master.iconname('fetchmail launcher');
1876 text='Fetchmailconf ' + version,
1877 bd=2).pack(side=TOP, pady=10)
1878 self.keepalive = [] # Use this to anchor the PhotoImage object
1879 make_icon_window(self, fetchmail_icon)
1882 ## Test icon display with the following:
1883 # icon_image = PhotoImage(data=fetchmail_icon)
1884 # Label(self, image=icon_image).pack(side=TOP, pady=10)
1885 # self.keepalive.append(icon_image)
1887 Message(self, text="""
1888 Use `Configure fetchmail' to tell fetchmail about the remote
1889 servers it should poll (the host name, your username there,
1890 whether to use POP or IMAP, and so forth).
1891 """, width=600).pack(side=TOP)
1892 self.configbutton = Button(self, text='Configure fetchmail',
1893 fg='blue', command=self.configure)
1894 self.configbutton.pack()
1896 Message(self, text="""
1897 Use `Run fetchmail' to run fetchmail with debugging enabled.
1898 This is a good way to test out a new configuration.
1899 """, width=600).pack(side=TOP)
1900 Button(self, text='Run fetchmail',fg='blue', command=self.test).pack()
1902 Message(self, text="""
1903 Use `Run fetchmail' to run fetchmail in foreground.
1904 Progress messages will be shown, but not debug messages.
1905 """, width=600).pack(side=TOP)
1906 Button(self, text='Run fetchmail', fg='blue', command=self.run).pack()
1908 Message(self, text="""
1909 Or you can just select `Quit' to exit the launcher now.
1910 """, width=600).pack(side=TOP)
1911 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1913 def configure(self):
1914 self.configbutton.configure(state=DISABLED)
1915 Configurator(self.outfile, Toplevel(),
1916 lambda self=self: self.configbutton.configure(state=NORMAL),
1919 cmd = "fetchmail -N -d0 --nosyslog -v"
1921 cmd = cmd + " -f " + rcfile
1922 RunWindow(cmd, Toplevel(), self)
1925 cmd = "fetchmail -N -d0"
1927 cmd = cmd + " -f " + rcfile
1928 RunWindow(cmd, Toplevel(), self)
1933 # Functions for turning a dictionary into an instantiated object tree.
1935 def intersect(list1, list2):
1936 # Compute set intersection of lists
1943 def setdiff(list1, list2):
1944 # Compute set difference of lists
1951 def copy_instance(toclass, fromdict):
1952 # Initialize a class object of given type from a conformant dictionary.
1953 for fld in fromdict.keys():
1954 if not fld in dictmembers:
1955 dictmembers.append(fld)
1956 # The `optional' fields are the ones we can ignore for purposes of
1957 # conformability checking; they'll still get copied if they are
1958 # present in the dictionary.
1959 optional = ('interface', 'monitor',
1960 'esmtpname', 'esmtppassword',
1961 'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
1962 'sslcertpath', 'sslcommonname', 'sslfingerprint', 'showdots')
1963 class_sig = setdiff(toclass.__dict__.keys(), optional)
1965 dict_keys = setdiff(fromdict.keys(), optional)
1967 common = intersect(class_sig, dict_keys)
1968 if 'typemap' in class_sig:
1969 class_sig.remove('typemap')
1970 if tuple(class_sig) != tuple(dict_keys):
1971 print "Fields don't match what fetchmailconf expected:"
1972 # print "Class signature: " + `class_sig`
1973 # print "Dictionary keys: " + `dict_keys`
1974 diff = setdiff(class_sig, common)
1976 print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
1977 diff = setdiff(dict_keys, common)
1979 print "Not matched in dictionary keys: " + `diff`
1982 for x in fromdict.keys():
1983 setattr(toclass, x, fromdict[x])
1986 # And this is the main sequence. How it works:
1988 # First, call `fetchmail --configdump' and trap the output in a tempfile.
1989 # This should fill it with a Python initializer for a variable `fetchmailrc'.
1990 # Run execfile on the file to pull fetchmailrc into Python global space.
1991 # You don't want static data, though; you want, instead, a tree of objects
1992 # with the same data members and added appropriate methods.
1994 # This is what the copy_instance function() is for. It tries to copy a
1995 # dictionary field by field into a class, aborting if the class and dictionary
1996 # have different data members (except for any typemap member in the class;
1997 # that one is strictly for use by the MyWidget supperclass).
1999 # Once the object tree is set up, require user to choose novice or expert
2000 # mode and instantiate an edit object for the configuration. Class methods
2001 # will take it all from there.
2003 # Options (not documented because they're for fetchmailconf debuggers only):
2004 # -d: Read the configuration and dump it to stdout before editing. Dump
2005 # the edited result to stdout as well.
2006 # -f: specify the run control file to read.
2008 if __name__ == '__main__':
2010 if not os.environ.has_key("DISPLAY"):
2011 print "fetchmailconf must be run under X"
2014 fetchmail_icon = """
2015 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
2016 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
2017 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
2018 CJR7Y/+UMdbOxnNrY97Ove/Wvd7GrZyEa961jL2Ua9alc86ca7WEUntSKcaMSqVjGNZ7GGM5CNa1
2019 jPfOnN6tc3taMffeve/WtWtaQv/OjGtSMYRzWv/erda1hM6te7WUY62MWs61jP/vzv/ntda9jL2l
2020 czEhAO/n1oyEc//elDEpGEo5EOfexpyUe+/epefevffvxnNrQpyUStbWzsbGvZyclN7ezmNjWv//
2021 5/f33qWllNbWve/vzv//1ufnve/vvf//xvf3vefnrf//taWlc0pKMf//pbW1Y///jKWlWq2tWsbG
2022 Y///c97eUvf3Ut7nc+/3a87We8bOjOfv1u/37/f//621tb3Gxtbn52Nra87n53uUlJTv/6W9xuf3
2023 /8bW3iExOXu11tbv/5TW/4TO/63e/zmt/1KUxlK1/2u9/wCM/73GzrXG1gBKjACE/87e72NzhCkx
2024 OaXO92OMtUql/xCE/wApUtbe57W9xnN7hHut52Ot/xBSnABKnABavQB7/2ul7zF71gBr77XO73Oc
2025 1lqc9yFSlBApSimE/wAYOQApY0J7zlKM5wAxhABS1gBj/6W95wAhWgA5nAAYSgBS7wBS/wBK9wAp
2026 jABC5wBK/wApnABC/wApxgAhtYSMtQAQYwAp/3OE74SMxgAYxlpjvWNr70pS/wgQ3sbGzs7O1qWl
2027 3qWl70pKe0JC/yEhlCkp/wgI/wAAEAAAIQAAKQAAOQAASgAAUgAAYwAAawAAlAAAnAAApQAArQAA
2028 zgAA1gAA5wAA9wAA/0pC/xgQ52Na9ykhe4R7zikhYxgQSjEpQgAAACwAAAAAPAAoAAAI/wABCBxI
2029 sKDBgwgTKiRIYKHDhxARIvgXsaLFhGgEUBSYoKPHjyBDihxJkuS/kwNLqlzJcuTJjQBaypxpEiVH
2030 mjhxvkyZs2fLnTd9ehxAtKjRo0ZrwhTasUsENhYHKOUpk1E3j11mxCBiQVLEBlJd2owp9iVRjwUs
2031 zMCQ5IcLD4saPVxjIKxIoGTvvqSoyFEFGTBeqEhyxAoSFR/USGKVcEGBAwDshsSr1OYTEyhQpJiS
2032 ZcoUKWOQtJDRJFSaggzUGBgoGSTlsjahlPCRIkWVKT16THHRIoqIISBIEUgAYIGBhgRbf3ytFygU
2033 FZp9UDmxQkkMCRwyZKDBQy4aApABhP8XqNwj88l7BVpQYZtF5iArWgwAgGZBq24HU7OeGhQ90PVA
2034 aKZZCiiUMJ9ArSTEwGqR8ZeXfzbV0MIIMQTBwoUdxDDfAm8sZFyDZVEF4UYSKBEBD0+k6IEFPMxH
2035 3FzldXSea+kBgANJSOWIlIMhXZXAXv+c1WM3PuJEpH8iuhbAkv+MdENPRHaTRkdF/jiWSKCAwlKW
2036 VbbkY5Q0LgUSKExgoYBKCjCxARpdltQNKHaUoYAddnR53lVRnJLKBWh4RIEGCZx5FSOv1OLNDUVe
2037 deZHaWiZAB35fIOGNtbEUeV5oGAByzPOrBPFGt3kwEgxITACSg5oLGGLMg60oQAjaNz/oAAcN4Ai
2038 a0c3kHFDK3jYsw4g9sRzBgPLXdkRrBrQ8gsWQUxCCRZX9IJNBQ1s8IgCdeBCzBYN6IBIN2TUsQYd
2039 dXhDBxdzlAHOHHKEcocZdWwDjx8MTCmjsR2FMAstw1RyiSzHqPLALaOwk8QmzCzDCSi0xJKMMk4E
2040 Yw8389iTDT32GAKOPf7YY0Aa9tATyD3w/EGsefgmgEYUtPiChLKWQDMBJtEUgYkzH2RiTgGfTMCI
2041 Mlu0Yc85hNiDziH2tMqOGL72QY47gshLb7Fi4roELcjoQIsxWpDwQyfS2OCJMkLI4YUmyhgxSTVg
2042 CP2FHPZ80UDcieBjStNPD5LPOyZT/y0iHGiMwswexDSzRiRq6KIMJBc4M8skwKAyChia2KPH3P24
2043 YU8/lFhOTj152OPOHuXMU4g48vCRiN/9rZGLMdS4csUu1JzDgxuipOMDHMKsAwEnq/ByzTrrZMNO
2044 OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
2045 xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
2046 gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
2048 # The base64 data in the string above was generated by the following procedure:
2051 # print base64.encodestring(open("fetchmail.gif", "rb").read())
2055 (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV", ["help",
2057 dump = rcfile = None;
2058 for (switch, val) in options:
2059 if (switch == '-d'):
2061 elif (switch == '-f'):
2063 elif (switch == '-h' or switch == '--help'):
2065 Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|--help|-V|--version}
2066 -d - dump configuration (for debugging)
2067 -f fmrc - read alternate fetchmailrc file
2068 --help, -h - print this help text and quit
2069 --version, -V - print fetchmailconf version and quit
2072 elif (switch == '-V' or switch == '--version'):
2073 print "fetchmailconf %s" % version
2075 Copyright (C) 1997 - 2003 Eric S. Raymond
2076 Copyright (C) 2005, 2006, 2008, 2009 Matthias Andree
2077 fetchmailconf comes with ABSOLUTELY NO WARRANTY. This is free software, you are
2078 welcome to redistribute it under certain conditions. Please see the file
2079 COPYING in the source or documentation directory for details."""
2082 # Get client host's FQDN
2083 hostname = socket.gethostbyaddr(socket.gethostname())[0]
2086 ConfigurationDefaults = Configuration()
2087 ServerDefaults = Server()
2088 UserDefaults = User()
2090 # Read the existing configuration. We set the umask to 077 to make sure
2091 # that group & other read/write permissions are shut off -- we wouldn't
2092 # want crackers to snoop password information out of the tempfile.
2093 tmpfile = tempfile.mktemp()
2095 cmd = "umask 077 && fetchmail </dev/null -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2097 cmd = "umask 077 && fetchmail </dev/null --configdump --nosyslog >" + tmpfile
2102 print "`" + cmd + "' run failure, status " + `s`
2105 print "Unknown error while running fetchmail --configdump"
2112 print "Can't read configuration output of fetchmail --configdump."
2118 # The tricky part -- initializing objects from the configuration global
2119 # `Configuration' is the top level of the object tree we're going to mung.
2120 # The dictmembers list is used to track the set of fields the dictionary
2121 # contains; in particular, we can use it to tell whether things like the
2122 # monitor, interface, ssl, sslkey, or sslcert fields are present.
2124 Fetchmailrc = Configuration()
2125 copy_instance(Fetchmailrc, fetchmailrc)
2126 Fetchmailrc.servers = [];
2127 for server in fetchmailrc['servers']:
2129 copy_instance(Newsite, server)
2130 Fetchmailrc.servers.append(Newsite)
2132 for user in server['users']:
2134 copy_instance(Newuser, user)
2135 Newsite.users.append(Newuser)
2137 # We may want to display the configuration and quit
2139 print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2141 # The theory here is that -f alone sets the rcfile location,
2142 # but -d and -f together mean the new configuration should go to stdout.
2143 if not rcfile and not dump:
2144 rcfile = os.environ["HOME"] + "/.fetchmailrc"
2146 # OK, now run the configuration edit
2147 root = MainWindow(rcfile)
2150 # The following sets edit modes for GNU EMACS