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