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,
455 authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp",
459 'title' : 'List Selection Help',
460 'banner': 'List Selection',
462 You must select an item in the list box (by clicking on it).
465 def flag2str(value, string):
466 # make a string representation of a .fetchmailrc flag or negated flag
470 if value == FALSE: str = str + ("no ")
474 class LabeledEntry(Frame):
475 # widget consisting of entry field with caption to left
476 def bind(self, key, action):
477 self.E.bind(key, action)
480 def __init__(self, Master, text, textvar, lwidth, ewidth=12):
481 Frame.__init__(self, Master)
482 self.L = Label(self, {'text':text, 'width':lwidth, 'anchor':'w'})
483 self.E = Entry(self, {'textvar':textvar, 'width':ewidth})
484 self.L.pack({'side':'left'})
485 self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
487 def ButtonBar(frame, legend, ref, alternatives, depth, command):
488 # array of radio buttons, caption to left, picking from a string list
490 width = (len(alternatives)+1) / depth;
491 Label(bar, text=legend).pack(side=LEFT)
492 for column in range(width):
493 subframe = Frame(bar)
494 for row in range(depth):
495 ind = width * row + column
496 if ind < len(alternatives):
497 Radiobutton(subframe,
498 {'text':alternatives[ind],
500 'value':alternatives[ind],
501 'command':command}).pack(side=TOP, anchor=W)
503 # This is just a spacer
504 Radiobutton(subframe,
505 {'text':" ",'state':DISABLED}).pack(side=TOP, anchor=W)
506 subframe.pack(side=LEFT)
510 def helpwin(helpdict):
511 # help message window with a self-destruct button
513 helpwin.title(helpdict['title'])
514 helpwin.iconname(helpdict['title'])
515 Label(helpwin, text=helpdict['banner']).pack()
516 textframe = Frame(helpwin)
517 scroll = Scrollbar(textframe)
518 helpwin.textwidget = Text(textframe, setgrid=TRUE)
519 textframe.pack(side=TOP, expand=YES, fill=BOTH)
520 helpwin.textwidget.config(yscrollcommand=scroll.set)
521 helpwin.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
522 scroll.config(command=helpwin.textwidget.yview)
523 scroll.pack(side=RIGHT, fill=BOTH)
524 helpwin.textwidget.insert(END, helpdict['text']);
525 Button(helpwin, text='Done',
526 command=lambda x=helpwin: x.destroy(), bd=2).pack()
527 textframe.pack(side=TOP)
529 def make_icon_window(base, image):
531 # Some older pythons will error out on this
532 icon_image = PhotoImage(data=image)
533 icon_window = Toplevel()
534 Label(icon_window, image=icon_image, bg='black').pack()
535 base.master.iconwindow(icon_window)
536 # Avoid TkInter brain death. PhotoImage objects go out of
537 # scope when the enclosing function returns. Therefore
538 # we have to explicitly link them to something.
539 base.keepalive.append(icon_image)
543 class ListEdit(Frame):
544 # edit a list of values (duplicates not allowed) with a supplied editor hook
545 def __init__(self, newlegend, list, editor, deletor, master, helptxt):
547 self.deletor = deletor
550 # Set up a widget to accept new elements
551 self.newval = StringVar(master)
552 newwin = LabeledEntry(master, newlegend, self.newval, '12')
553 newwin.bind('<Double-1>', self.handleNew)
554 newwin.bind('<Return>', self.handleNew)
555 newwin.pack(side=TOP, fill=X, anchor=E)
557 # Edit the existing list
558 listframe = Frame(master)
559 scroll = Scrollbar(listframe)
560 self.listwidget = Listbox(listframe, height=0, selectmode='browse')
563 self.listwidget.insert(END, x)
564 listframe.pack(side=TOP, expand=YES, fill=BOTH)
565 self.listwidget.config(yscrollcommand=scroll.set)
566 self.listwidget.pack(side=LEFT, expand=YES, fill=BOTH)
567 scroll.config(command=self.listwidget.yview)
568 scroll.pack(side=RIGHT, fill=BOTH)
569 self.listwidget.config(selectmode=SINGLE, setgrid=TRUE)
570 self.listwidget.bind('<Double-1>', self.handleList);
571 self.listwidget.bind('<Return>', self.handleList);
575 Button(bf, text='Edit', command=self.editItem).pack(side=LEFT)
576 Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT)
578 self.helptxt = helptxt
579 Button(bf, text='Help', fg='blue',
580 command=self.help).pack(side=RIGHT)
584 helpwin(self.helptxt)
586 def handleList(self, event):
589 def handleNew(self, event):
590 item = self.newval.get()
592 entire = self.listwidget.get(0, self.listwidget.index('end'));
593 if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
594 self.listwidget.insert('end', item)
595 if self.list != None: self.list.append(item)
597 apply(self.editor, (item,))
601 select = self.listwidget.curselection()
606 if index and self.editor:
607 label = self.listwidget.get(index);
609 apply(self.editor, (label,))
611 def deleteItem(self):
612 select = self.listwidget.curselection()
616 index = string.atoi(select[0])
617 label = self.listwidget.get(index);
618 self.listwidget.delete(index)
619 if self.list != None:
621 if self.deletor != None:
622 apply(self.deletor, (label,))
624 def ConfirmQuit(frame, context):
627 text = 'Really quit ' + context + ' without saving?',
629 strings = ('Yes', 'No'),
633 def dispose_window(master, legend, help, savelegend='OK'):
634 dispose = Frame(master, relief=RAISED, bd=5)
635 Label(dispose, text=legend).pack(side=TOP,pady=10)
636 Button(dispose, text=savelegend, fg='blue',
637 command=master.save).pack(side=LEFT)
638 Button(dispose, text='Quit', fg='blue',
639 command=master.nosave).pack(side=LEFT)
640 Button(dispose, text='Help', fg='blue',
641 command=lambda x=help: helpwin(x)).pack(side=RIGHT)
646 # Common methods for Tkinter widgets -- deals with Tkinter declaration
647 def post(self, widgetclass, field):
648 for x in widgetclass.typemap:
649 if x[1] == 'Boolean':
650 setattr(self, x[0], BooleanVar(self))
651 elif x[1] == 'String':
652 setattr(self, x[0], StringVar(self))
654 setattr(self, x[0], IntVar(self))
655 source = getattr(getattr(self, field), x[0])
657 getattr(self, x[0]).set(source)
659 def fetch(self, widgetclass, field):
660 for x in widgetclass.typemap:
661 setattr(getattr(self, field), x[0], getattr(self, x[0]).get())
664 # First, code to set the global fetchmail run controls.
667 configure_novice_help = {
668 'title' : 'Fetchmail novice configurator help',
669 'banner': 'Novice configurator help',
671 In the `Novice Configurator Controls' panel, you can:
673 Press `Save' to save the new fetchmail configuration you have created.
674 Press `Quit' to exit without saving.
675 Press `Help' to bring up this help message.
677 In the `Novice Configuration' panels, you will set up the basic data
678 needed to create a simple fetchmail setup. These include:
680 1. The name of the remote site you want to query.
682 2. Your login name on that site.
684 3. Your password on that site.
686 4. A protocol to use (POP, IMAP, ETRN, etc.)
688 5. A poll interval in seconds.
689 If 0, fetchmail will run in the foreground once when started.
690 If > 0, fetchmail will run in the background and start a new poll
691 cycle after the interval has elapsed.
693 6. Options to fetch old messages as well as new, or to suppress
694 deletion of fetched message.
696 The novice-configuration code will assume that you want to forward mail
697 to a local sendmail listener with no special options.
700 configure_expert_help = {
701 'title' : 'Fetchmail expert configurator help',
702 'banner': 'Expert configurator help',
704 In the `Expert Configurator Controls' panel, you can:
706 Press `Save' to save the new fetchmail configuration you have edited.
707 Press `Quit' to exit without saving.
708 Press `Help' to bring up this help message.
710 In the `Run Controls' panel, you can set the following options that
711 control how fetchmail runs:
714 Number of seconds to wait between polls in the background.
715 If zero, fetchmail will run in foreground.
718 If empty, emit progress and error messages to stderr.
719 Otherwise this gives the name of the files to write to.
720 This field is ignored if the "Log to syslog?" option is on.
723 If empty, store seen-message IDs in .fetchids under user's home
724 directory. If nonempty, use given file name.
727 Who to send multidrop mail to as a last resort if no address can
728 be matched. Normally empty; in this case, fetchmail treats the
729 invoking user as the address of last resort unless that user is
730 root. If that user is root, fetchmail sends to `postmaster'.
733 If this option is on (the default) error mail goes to the sender.
734 Otherwise it goes to the postmaster.
737 If this option is on, spam bounces are sent to the sender or
738 postmaster (depending on the "Bounces to sender?" option. Otherwise,
739 spam bounces are not sent (the default).
742 If this option is on, permanent delivery errors are treated as
743 temporary, i. e. mail is kept on the upstream server. Useful
744 during testing and after configuration changes, and on by
746 If this option is off, permanent delivery errors delete
747 undeliverable mail from the upstream.
750 If false (the default) fetchmail generates a Received line into
751 each message and generates a HELO from the machine it is running on.
752 If true, fetchmail generates no Received line and HELOs as if it were
755 In the `Remote Mail Configurations' panel, you can:
757 1. Enter the name of a new remote mail server you want fetchmail to query.
759 To do this, simply enter a label for the poll configuration in the
760 `New Server:' box. The label should be a DNS name of the server (unless
761 you are using ssh or some other tunneling method and will fill in the `via'
762 option on the site configuration screen).
764 2. Change the configuration of an existing site.
766 To do this, find the site's label in the listbox and double-click it.
767 This will take you to a site configuration dialogue.
771 class ConfigurationEdit(Frame, MyWidget):
772 def __init__(self, configuration, outfile, master, onexit):
774 self.configuration = configuration
775 self.outfile = outfile
776 self.container = master
778 ConfigurationEdit.mode_to_help = {
779 'novice':configure_novice_help, 'expert':configure_expert_help
782 def server_edit(self, sitename):
783 self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
785 def server_delete(self, sitename):
787 for user in self.subwidgets.keys():
789 del self.configuration[sitename]
793 def edit(self, mode):
795 Frame.__init__(self, self.container)
796 self.master.title('fetchmail ' + self.mode + ' configurator');
797 self.master.iconname('fetchmail ' + self.mode + ' configurator');
798 self.master.protocol('WM_DELETE_WINDOW', self.nosave)
799 self.keepalive = [] # Use this to anchor the PhotoImage object
800 make_icon_window(self, fetchmail_icon)
802 self.post(Configuration, 'configuration')
805 'Configurator ' + self.mode + ' Controls',
806 ConfigurationEdit.mode_to_help[self.mode],
809 gf = Frame(self, relief=RAISED, bd = 5)
811 text='Fetchmail Run Controls',
812 bd=2).pack(side=TOP, pady=10)
817 if self.mode != 'novice':
819 log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
820 log.pack(side=RIGHT, anchor=E)
822 # Set the poll interval
823 de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
824 de.pack(side=RIGHT, anchor=E)
829 if self.mode != 'novice':
832 {'text':'Bounces to sender?',
833 'variable':self.bouncemail,
834 'relief':GROOVE}).pack(side=LEFT, anchor=W)
839 {'text':'Send spam bounces?',
840 'variable':self.spambounce,
841 'relief':GROOVE}).pack(side=LEFT, anchor=W)
846 {'text':'Treat permanent errors as temporary?',
847 'variable':self.softbounce,
848 'relief':GROOVE}).pack(side=LEFT, anchor=W)
853 {'text':'Log to syslog?',
854 'variable':self.syslog,
855 'relief':GROOVE}).pack(side=LEFT, anchor=W)
856 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
857 log.pack(side=RIGHT, anchor=E)
861 {'text':'Invisible mode?',
862 'variable':self.invisible,
863 'relief':GROOVE}).pack(side=LEFT, anchor=W)
865 log = LabeledEntry(gf, ' Idfile:', self.idfile, '14')
866 log.pack(side=RIGHT, anchor=E)
870 # Expert mode allows us to edit multiple sites
871 lf = Frame(self, relief=RAISED, bd=5)
873 text='Remote Mail Server Configurations',
874 bd=2).pack(side=TOP, pady=10)
875 ListEdit('New Server:',
876 map(lambda x: x.pollname, self.configuration.servers),
877 lambda site, self=self: self.server_edit(site),
878 lambda site, self=self: self.server_delete(site),
883 for sitename in self.subwidgets.keys():
884 self.subwidgets[sitename].destruct()
885 self.master.destroy()
889 if ConfirmQuit(self, self.mode + " configuration editor"):
893 for sitename in self.subwidgets.keys():
894 self.subwidgets[sitename].save()
895 self.fetch(Configuration, 'configuration')
899 elif not os.path.isfile(self.outfile) or Dialog(self,
900 title = 'Overwrite existing run control file?',
901 text = 'Really overwrite existing run control file?',
903 strings = ('Yes', 'No'),
904 default = 1).num == 0:
906 os.rename(self.outfile, self.outfile + "~")
907 # Pre-1.5.2 compatibility...
910 oldumask = os.umask(077)
911 fm = open(self.outfile, 'w')
916 os.chmod(self.outfile, 0600)
917 fm.write("# Configuration created %s by fetchmailconf %s\n" % (time.ctime(time.time()), version))
918 fm.write(`self.configuration`)
924 # Server editing stuff.
927 'title' : 'Remote site help',
928 'banner': 'Remote sites',
930 When you add a site name to the list here,
931 you initialize an entry telling fetchmail
932 how to poll a new site.
934 When you select a sitename (by double-
935 clicking it, or by single-clicking to
936 select and then clicking the Edit button),
937 you will open a window to configure that
942 'title' : 'Server options help',
943 'banner': 'Server Options',
945 The server options screen controls fetchmail
946 options that apply to one of your mailservers.
948 Once you have a mailserver configuration set
949 up as you like it, you can select `OK' to
950 store it in the server list maintained in
951 the main configuration window.
953 If you wish to discard changes to a server
954 configuration, select `Quit'.
958 'title' : 'Run Control help',
959 'banner': 'Run Controls',
961 If the `Poll normally' checkbox is on, the host is polled as part of
962 the normal operation of fetchmail when it is run with no arguments.
963 If it is off, fetchmail will only query this host when it is given as
964 a command-line argument.
966 The `Retrieve Error Policy' specifies how server errors during
967 message retrieval are handled. The default behaviour is to abort the
968 current session. Both the continue and markseen options will skip
969 the message with the error, but continue the session allowing for
970 downloading of subsequent messages. Additionally, the markseen
971 option will mark the skipped message as seen.
973 The `True name of server' box should specify the actual DNS name
974 to query. By default this is the same as the poll name.
976 Normally each host described in the file is queried once each
977 poll cycle. If `Cycles to skip between polls' is greater than 0,
978 that's the number of poll cycles that are skipped between the
979 times this post is actually polled.
981 The `Server timeout' is the number of seconds fetchmail will wait
982 for a reply from the mailserver before concluding it is hung and
987 'title' : 'Protocol and Port help',
988 'banner': 'Protocol and Port',
990 These options control the remote-mail protocol
991 and TCP/IP service port used to query this
994 If you click the `Probe for supported protocols'
995 button, fetchmail will try to find you the most
996 capable server on the selected host (this will
997 only work if you're conncted to the Internet).
998 The probe only checks for ordinary IMAP and POP
999 protocols; fortunately these are the most
1000 frequently supported.
1002 The `Protocol' button bar offers you a choice of
1003 all the different protocols available. The `auto'
1004 protocol is the default mode; it probes the host
1005 ports for POP3 and IMAP to see if either is
1008 Normally the TCP/IP service port to use is
1009 dictated by the protocol choice. The `Service'
1010 field (only present in expert mode) lets you
1011 set a non-standard service (port).
1015 'title' : 'Security option help',
1016 'banner': 'Security',
1018 The 'authorization mode' allows you to choose the
1019 mode that fetchmail uses to log in to your server. You
1020 can usually leave this at 'any', but you will have to pick
1021 'NTLM' and 'MSN' manually for the nonce.
1023 The 'interface' option allows you to specify a range
1024 of IP addresses to monitor for activity. If these
1025 addresses are not active, fetchmail will not poll.
1026 Specifying this may protect you from a spoofing attack
1027 if your client machine has more than one IP gateway
1028 address and some of the gateways are to insecure nets.
1030 The `monitor' option, if given, specifies the only
1031 device through which fetchmail is permitted to connect
1032 to servers. This option may be used to prevent
1033 fetchmail from triggering an expensive dial-out if the
1034 interface is not already active.
1036 The `interface' and `monitor' options are available
1037 only for Linux and freeBSD systems. See the fetchmail
1038 manual page for details on these.
1040 The ssl option enables SSL communication with a mailserver
1041 supporting Secure Sockets Layer. The sslkey and sslcert options
1042 declare key and certificate files for use with SSL.
1043 The sslcertck option enables strict checking of SSL server
1044 certificates (and sslcertpath gives the trusted certificate
1045 directory). The sslcommonname option helps if the server is
1046 misconfigured and returning "Server CommonName mismatch"
1047 warnings. With sslfingerprint, you can specify a finger-
1048 print the server's key is checked against.
1052 'title' : 'Multidrop option help',
1053 'banner': 'Multidrop',
1055 These options are only useful with multidrop mode.
1056 See the manual page for extended discussion.
1060 'title' : 'User list help',
1061 'banner': 'User list',
1063 When you add a user name to the list here,
1064 you initialize an entry telling fetchmail
1065 to poll the site on behalf of the new user.
1067 When you select a username (by double-
1068 clicking it, or by single-clicking to
1069 select and then clicking the Edit button),
1070 you will open a window to configure the
1071 user's options on that site.
1074 class ServerEdit(Frame, MyWidget):
1075 def __init__(self, host, parent):
1076 self.parent = parent
1078 self.subwidgets = {}
1079 for site in parent.configuration.servers:
1080 if site.pollname == host:
1082 if (self.server == None):
1083 self.server = Server()
1084 self.server.pollname = host
1085 self.server.via = None
1086 parent.configuration.servers.append(self.server)
1088 def edit(self, mode, master=None):
1089 Frame.__init__(self, master)
1091 self.master.title('Fetchmail host ' + self.server.pollname);
1092 self.master.iconname('Fetchmail host ' + self.server.pollname);
1093 self.post(Server, 'server')
1094 self.makeWidgets(self.server.pollname, mode)
1095 self.keepalive = [] # Use this to anchor the PhotoImage object
1096 make_icon_window(self, fetchmail_icon)
1099 # self.wait_window()
1103 for username in self.subwidgets.keys():
1104 self.subwidgets[username].destruct()
1105 del self.parent.subwidgets[self.server.pollname]
1106 self.master.destroy()
1109 if ConfirmQuit(self, 'server option editing'):
1113 self.fetch(Server, 'server')
1114 for username in self.subwidgets.keys():
1115 self.subwidgets[username].save()
1118 def defaultPort(self):
1119 proto = self.protocol.get()
1120 # Callback to reset the port number whenever the protocol type changes.
1121 # We used to only reset the port if it had a default (zero) value.
1122 # This turns out to be a bad idea especially in Novice mode -- if
1123 # you set POP3 and then set IMAP, the port invisibly remained 110.
1124 # Now we reset unconditionally on the theory that if you're setting
1125 # a custom port number you should be in expert mode and playing
1126 # close enough attention to notice this...
1127 self.service.set(defaultports[proto])
1129 def user_edit(self, username, mode):
1130 self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1132 def user_delete(self, username):
1133 if self.subwidgets.has_key(username):
1134 self.subwidgets[username].destruct()
1135 del self.server[username]
1137 def makeWidgets(self, host, mode):
1138 topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1140 leftwin = Frame(self);
1143 if mode != 'novice':
1144 ctlwin = Frame(leftwin, relief=RAISED, bd=5)
1145 Label(ctlwin, text="Run Controls").pack(side=TOP)
1146 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
1147 Checkbutton(ctlwin, text='Pass messages with bad headers?',
1148 variable=self.badheader).pack(side=TOP)
1149 retrieveerrorlist = ['abort', 'continue', 'markseen']
1150 Label(ctlwin, text="Retrieve Error Policy").pack(side=TOP)
1151 ButtonBar(ctlwin, '', self.retrieveerror, retrieveerrorlist, 1, None)
1152 LabeledEntry(ctlwin, 'True name of ' + host + ':',
1153 self.via, leftwidth).pack(side=TOP, fill=X)
1154 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
1155 self.interval, leftwidth).pack(side=TOP, fill=X)
1156 LabeledEntry(ctlwin, 'Server timeout (seconds):',
1157 self.timeout, leftwidth).pack(side=TOP, fill=X)
1158 Button(ctlwin, text='Help', fg='blue',
1159 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
1162 # Compute the available protocols from the compile-time options
1163 protolist = ['auto']
1164 if 'pop3' in feature_options:
1165 protolist = protolist + ["POP3", "APOP", "KPOP"]
1166 if 'sdps' in feature_options:
1167 protolist.append("SDPS")
1168 if 'imap' in feature_options:
1169 protolist.append("IMAP")
1170 if 'etrn' in feature_options:
1171 protolist.append("ETRN")
1172 if 'odmr' in feature_options:
1173 protolist.append("ODMR")
1175 protwin = Frame(leftwin, relief=RAISED, bd=5)
1176 Label(protwin, text="Protocol").pack(side=TOP)
1177 ButtonBar(protwin, '',
1178 self.protocol, protolist, 2,
1180 if mode != 'novice':
1181 LabeledEntry(protwin, 'On server TCP/IP service:',
1182 self.service, leftwidth).pack(side=TOP, fill=X)
1184 Button(protwin, text='Probe for supported protocols', fg='blue',
1185 command=self.autoprobe).pack(side=LEFT)
1186 Button(protwin, text='Help', fg='blue',
1187 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
1188 protwin.pack(fill=X)
1190 userwin = Frame(leftwin, relief=RAISED, bd=5)
1191 Label(userwin, text="User entries for " + host).pack(side=TOP)
1192 ListEdit("New user: ",
1193 map(lambda x: x.remote, self.server.users),
1194 lambda u, m=mode, s=self: s.user_edit(u, m),
1195 lambda u, s=self: s.user_delete(u),
1197 userwin.pack(fill=X)
1199 leftwin.pack(side=LEFT, anchor=N, fill=X);
1201 if mode != 'novice':
1202 rightwin = Frame(self);
1204 mdropwin = Frame(rightwin, relief=RAISED, bd=5)
1205 Label(mdropwin, text="Multidrop options").pack(side=TOP)
1206 LabeledEntry(mdropwin, 'Envelope address header:',
1207 self.envelope, '22').pack(side=TOP, fill=X)
1208 LabeledEntry(mdropwin, 'Envelope headers to skip:',
1209 self.envskip, '22').pack(side=TOP, fill=X)
1210 LabeledEntry(mdropwin, 'Name prefix to strip:',
1211 self.qvirtual, '22').pack(side=TOP, fill=X)
1212 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
1213 variable=self.dns).pack(side=TOP)
1214 Label(mdropwin, text="DNS aliases").pack(side=TOP)
1215 ListEdit("New alias: ", self.server.aka, None, None, mdropwin, None)
1216 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
1217 ListEdit("New domain: ",
1218 self.server.localdomains, None, None, mdropwin, multihelp)
1219 mdropwin.pack(fill=X)
1221 if os_type in ('linux', 'freebsd'):
1222 secwin = Frame(rightwin, relief=RAISED, bd=5)
1223 Label(secwin, text="Security").pack(side=TOP)
1224 # Don't actually let users set this. KPOP sets it implicitly
1225 ButtonBar(secwin, 'Authorization mode:',
1226 self.auth, authlist, 2, None).pack(side=TOP)
1227 if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
1228 LabeledEntry(secwin, 'IP range to check before poll:',
1229 self.interface, leftwidth).pack(side=TOP, fill=X)
1230 if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
1231 LabeledEntry(secwin, 'Interface to monitor:',
1232 self.monitor, leftwidth).pack(side=TOP, fill=X)
1233 # Someday this should handle Kerberos 5 too
1234 if 'kerberos' in feature_options:
1235 LabeledEntry(secwin, 'Principal:',
1236 self.principal, '12').pack(side=TOP, fill=X)
1237 # ESMTP authentication
1238 LabeledEntry(secwin, 'ESMTP name:',
1239 self.esmtpname, '12').pack(side=TOP, fill=X)
1240 LabeledEntry(secwin, 'ESMTP password:',
1241 self.esmtppassword, '12').pack(side=TOP, fill=X)
1242 Button(secwin, text='Help', fg='blue',
1243 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
1246 rightwin.pack(side=LEFT, anchor=N);
1248 def autoprobe(self):
1249 # Note: this only handles case (1) near fetchmail.c:1032
1250 # We're assuming people smart enough to set up ssh tunneling
1251 # won't need autoprobing.
1253 realhost = self.server.via
1255 realhost = self.server.pollname
1257 for protocol in ("IMAP","POP3"):
1258 service = defaultports[protocol]
1259 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1261 sock.connect((realhost, ianaservices[service]))
1262 greetline = sock.recv(1024)
1268 confwin = Toplevel()
1269 if greetline == None:
1270 title = "Autoprobe of " + realhost + " failed"
1272 Fetchmailconf didn't find any mailservers active.
1273 This could mean the host doesn't support any,
1274 or that your Internet connection is down, or
1275 that the host is so slow that the probe timed
1276 out before getting a response.
1280 # OK, now try to recognize potential problems
1282 ### POP3 servers start here
1284 if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
1285 warnings = warnings + """
1286 This appears to be an old version of the UC Davis POP server. These are
1287 dangerously unreliable (among other problems, they may drop your mailbox
1288 on the floor if your connection is interrupted during the session).
1290 It is strongly recommended that you find a better POP3 server. The fetchmail
1291 FAQ includes pointers to good ones.
1294 if string.find(greetline, "comcast.net") > 0:
1295 warnings = warnings + """
1296 The Comcast Maillennium POP3 server only returns the first 80K of a long
1297 message retrieved with TOP. Its response to RETR is normal, so use the
1301 # Steve VanDevender <stevev@efn.org> writes:
1302 # The only system I have seen this happen with is cucipop-1.31
1303 # under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
1304 # 2.x and probably quite a few other systems. It appears to be a
1305 # bug or bad interaction with the SunOS realloc() -- it turns out
1306 # that internally cucipop does allocate a certain data structure in
1307 # multiples of 16, using realloc() to bump it up to the next
1308 # multiple if it needs more.
1310 # The distinctive symptom is that when there are 16 messages in the
1311 # inbox, you can RETR and DELE all 16 messages successfully, but on
1312 # QUIT cucipop returns something like "-ERR Error locking your
1313 # mailbox" and aborts without updating it.
1315 # The cucipop banner looks like:
1317 # +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1319 if string.find(greetline, "Cubic Circle") > 0:
1320 warnings = warnings + """
1321 I see your server is running cucipop. Better make sure the server box
1322 isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
1323 under that version, and doesn't cope with the result gracefully. Newer
1324 SunOS and Solaris machines run cucipop OK.
1326 Also, some versions of cucipop don't assert an exclusive lock on your
1327 mailbox when it's being queried. This means that if you have more than
1328 one fetchmail query running against the same mailbox, bad things can happen.
1330 if string.find(greetline, "David POP3 Server") > 0:
1331 warnings = warnings + """
1332 This POP3 server is badly broken. You should get rid of it -- and the
1333 brain-dead Microsoft operating system it rode in on.
1336 # The greeting line on the server known to be buggy is:
1337 # +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
1339 if string.find(greetline, "FTGate") > 0:
1340 warnings = warnings + """
1341 This POP server has a weird bug; it says OK twice in response to TOP.
1342 Its response to RETR is normal, so use the `fetchall' option.
1345 if string.find(greetline, " geonet.de") > 0:
1346 warnings = warnings + """
1347 You appear to be using geonet. As of late 2002, the TOP command on
1348 geonet's POP3 is broken. Use the fetchall option.
1351 if string.find(greetline, "OpenMail") > 0:
1352 warnings = warnings + """
1353 You appear to be using some version of HP OpenMail. Many versions of
1354 OpenMail do not process the "TOP" command correctly; the symptom is that
1355 only the header and first line of each message is retrieved. To work
1356 around this bug, turn on `fetchall' on all user entries associated with
1360 if string.find(greetline, "Escape character is") > 0:
1361 warnings = warnings + """
1362 Your greeting line looks like it was written by a fetid pile of
1363 camel dung identified to me as `popa3d written by Solar Designer'.
1364 Beware! The UIDL support in this thing is known to be completely broken,
1365 and other things probably are too.
1368 if string.find(greetline, "MercuryP/NLM v1.48") > 0:
1369 warnings = warnings + """
1370 This is not a POP3 server. It has delusions of being one, but after
1371 RETR all messages are automatically marked to be deleted. The only
1372 way to prevent this is to issue an RSET before leaving the server.
1373 Fetchmail does this, but we suspect this is probably broken in lots
1377 if string.find(greetline, "POP-Max") > 0:
1378 warnings = warnings + """
1379 The Mail Max POP3 server screws up on mail with attachments. It
1380 reports the message size with attachments included, but doesn't
1381 download them on a RETR or TOP (this violates the IMAP RFCs). It also
1382 doesn't implement TOP correctly. You should get rid of it -- and the
1383 brain-dead NT server it rode in on.
1386 if string.find(greetline, "POP3 Server Ready") > 0:
1387 warnings = warnings + """
1388 Some server that uses this greeting line has been observed to choke on
1389 TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
1392 if string.find(greetline, "QPOP") > 0:
1393 warnings = warnings + """
1394 This appears to be a version of Eudora qpopper. That's good. Fetchmail
1395 knows all about qpopper. However, be aware that the 2.53 version of
1396 qpopper does something odd that causes fetchmail to hang with a socket
1397 error on very large messages. This is probably not a fetchmail bug, as
1398 it has been observed with fetchpop. The fix is to upgrade to qpopper
1399 3.0beta or a more recent version. Better yet, switch to IMAP.
1402 if string.find(greetline, " sprynet.com") > 0:
1403 warnings = warnings + """
1404 You appear to be using a SpryNet server. In mid-1999 it was reported that
1405 the SpryNet TOP command marks messages seen. Therefore, for proper error
1406 recovery in the event of a line drop, it is strongly recommended that you
1407 turn on `fetchall' on all user entries associated with this server.
1410 if string.find(greetline, "TEMS POP3") > 0:
1411 warnings = warnings + """
1412 Your POP3 server has "TEMS" in its header line. At least one such
1413 server does not process the "TOP" command correctly; the symptom is
1414 that fetchmail hangs when trying to retrieve mail. To work around
1415 this bug, turn on `fetchall' on all user entries associated with this
1419 if string.find(greetline, " spray.se") > 0:
1420 warnings = warnings + """
1421 Your POP3 server has "spray.se" in its header line. In May 2000 at
1422 least one such server did not process the "TOP" command correctly; the
1423 symptom is that messages are treated as headerless. To work around
1424 this bug, turn on `fetchall' on all user entries associated with this
1428 if string.find(greetline, " usa.net") > 0:
1429 warnings = warnings + """
1430 You appear to be using USA.NET's free mail service. Their POP3 servers
1431 (at least as of the 2.2 version in use mid-1998) are quite flaky, but
1432 fetchmail can compensate. They seem to require that fetchall be switched on
1433 (otherwise you won't necessarily see all your mail, not even new mail).
1434 They also botch the TOP command the fetchmail normally uses for retrieval
1435 (it only retrieves about 10 lines rather than the number specified).
1436 Turning on fetchall will disable the use of TOP.
1438 Therefore, it is strongly recommended that you turn on `fetchall' on all
1439 user entries associated with this server.
1442 if string.find(greetline, " Novonyx POP3") > 0:
1443 warnings = warnings + """
1444 Your mailserver is running Novonyx POP3. This server, at least as of
1445 version 2.17, seems to have problems handling and reporting seen bits.
1446 You may have to use the fetchall option.
1449 if string.find(greetline, " IMS POP3") > 0:
1450 warnings = warnings + """
1451 Some servers issuing the greeting line 'IMS POP3' have been known to
1452 do byte-stuffing incorrectly. This means that if a message you receive
1453 has a . (period) at start of line, fetchmail will become confused and
1454 probably wedge itself. (This bug was recorded on IMS POP3 0.86.)
1458 ### IMAP servers start here
1460 if string.find(greetline, "GroupWise") > 0:
1461 warnings = warnings + """
1462 The Novell GroupWise IMAP server would be better named GroupFoolish;
1463 it is (according to the designer of IMAP) unusably broken. Among
1464 other things, it doesn't include a required content length in its
1465 BODY[TEXT] response.<p>
1467 Fetchmail works around this problem, but we strongly recommend voting
1468 with your dollars for a server that isn't brain-dead. If you stick
1469 with code as shoddy as GroupWise seems to be, you will probably pay
1470 for it with other problems.<p>
1473 if string.find(greetline, "InterChange") > 0:
1474 warnings = warnings + """
1476 The InterChange IMAP server at release levels below 3.61.08 screws up
1477 on mail with attachments. It doesn't fetch them if you give it a
1478 BODY[TEXT] request, though it does if you request RFC822.TEXT.
1479 According to the IMAP RFCs and their maintainer these should be
1480 equivalent -- and we can't drop the BODY[TEXT] form because M$
1481 Exchange (quite legally under RFC2062) rejectsit. The InterChange
1482 folks claim to have fixed this bug in 3.61.08.
1485 if string.find(greetline, "Imail") > 0:
1486 warnings = warnings + """
1487 We've seen a bug report indicating that this IMAP server (at least as of
1488 version 5.0.7) returns an invalid body size for messages with MIME
1489 attachments; the effect is to drop the attachments on the floor. We
1490 recommend you upgrade to a non-broken IMAP server.
1493 if string.find(greetline, "Domino IMAP4") > 0:
1494 warnings = warnings + """
1495 Your IMAP server appears to be Lotus Domino. This server, at least up
1496 to version 5.0.2, has a bug in its generation of MIME boundaries (see
1497 the details in the fetchmail FAQ). As a result, even MIME aware MUAs
1498 will see attachments as part of the message text. If your Domino server's
1499 POP3 facility is enabled, we recommend you fall back on it.
1503 ### Checks for protocol variants start here
1505 closebrak = string.find(greetline, ">")
1506 if closebrak > 0 and greetline[closebrak+1] == "\r":
1507 warnings = warnings + """
1508 It looks like you could use APOP on this server and avoid sending it your
1509 password in clear. You should talk to the mailserver administrator about
1513 if string.find(greetline, "IMAP4rev1") > 0:
1514 warnings = warnings + """
1515 I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
1516 remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
1517 has therefore been extremely well tested with this class of server.
1521 warnings = warnings + """
1522 Fetchmail doesn't know anything special about this server type.
1526 # Display success window with warnings
1527 title = "Autoprobe of " + realhost + " succeeded"
1528 confirm = "The " + protocol + " server said:\n\n" + greetline + warnings
1529 self.protocol.set(protocol)
1530 self.service.set(defaultports[protocol])
1531 confwin.title(title)
1532 confwin.iconname(title)
1533 Label(confwin, text=title).pack()
1534 Message(confwin, text=confirm, width=600).pack()
1535 Button(confwin, text='Done',
1536 command=lambda x=confwin: x.destroy(), bd=2).pack()
1539 # User editing stuff
1543 'title' : 'User option help',
1544 'banner': 'User options',
1546 You may use this panel to set options
1547 that may differ between individual
1550 Once you have a user configuration set
1551 up as you like it, you can select `OK' to
1552 store it in the user list maintained in
1553 the site configuration window.
1555 If you wish to discard the changes you have
1556 made to user options, select `Quit'.
1560 'title' : 'Local name help',
1561 'banner': 'Local names',
1563 The local name(s) in a user entry are the
1564 people on the client machine who should
1565 receive mail from the poll described.
1567 Note: if a user entry has more than one
1568 local name, messages will be retrieved
1569 in multidrop mode. This complicates
1570 the configuration issues; see the manual
1571 page section on multidrop mode.
1573 Warning: Be careful with local names
1574 such as foo@bar.com, as that can cause
1575 the mail to be sent to foo@bar.com instead
1576 of sending it to your local system.
1579 class UserEdit(Frame, MyWidget):
1580 def __init__(self, username, parent):
1581 self.parent = parent
1583 for user in parent.server.users:
1584 if user.remote == username:
1586 if self.user == None:
1588 self.user.remote = username
1589 self.user.localnames = [username]
1590 parent.server.users.append(self.user)
1592 def edit(self, mode, master=None):
1593 Frame.__init__(self, master)
1595 self.master.title('Fetchmail user ' + self.user.remote
1596 + ' querying ' + self.parent.server.pollname);
1597 self.master.iconname('Fetchmail user ' + self.user.remote);
1598 self.post(User, 'user')
1599 self.makeWidgets(mode, self.parent.server.pollname)
1600 self.keepalive = [] # Use this to anchor the PhotoImage object
1601 make_icon_window(self, fetchmail_icon)
1604 # self.wait_window()
1608 # Yes, this test can fail -- if you delete the parent window.
1609 if self.parent.subwidgets.has_key(self.user.remote):
1610 del self.parent.subwidgets[self.user.remote]
1611 self.master.destroy()
1614 if ConfirmQuit(self, 'user option editing'):
1619 for x in self.user.localnames: ok = ok + (string.find(x, '@') != -1)
1620 if ok == 0 or Dialog(self,
1621 title = "Really accept an embedded '@' ?",
1622 text = "Local names with an embedded '@', such as in foo@bar "
1623 "might result in your mail being sent to foo@bar.com "
1624 "instead of your local system.\n Are you sure you want "
1625 "a local user name with an '@' in it?",
1626 bitmap = 'question',
1627 strings = ('Yes', 'No'),
1628 default = 1).num == 0:
1629 self.fetch(User, 'user')
1632 def makeWidgets(self, mode, servername):
1633 dispose_window(self,
1634 "User options for " + self.user.remote + " querying " + servername,
1637 if mode != 'novice':
1638 leftwin = Frame(self);
1642 secwin = Frame(leftwin, relief=RAISED, bd=5)
1643 Label(secwin, text="Authentication").pack(side=TOP)
1644 LabeledEntry(secwin, 'Password:',
1645 self.password, '12').pack(side=TOP, fill=X)
1646 secwin.pack(fill=X, anchor=N)
1648 if 'ssl' in feature_options or 'ssl' in dictmembers:
1649 sslwin = Frame(leftwin, relief=RAISED, bd=5)
1650 Checkbutton(sslwin, text="Use SSL?",
1651 variable=self.ssl).pack(side=TOP, fill=X)
1652 LabeledEntry(sslwin, 'SSL key:',
1653 self.sslkey, '14').pack(side=TOP, fill=X)
1654 LabeledEntry(sslwin, 'SSL certificate:',
1655 self.sslcert, '14').pack(side=TOP, fill=X)
1656 Checkbutton(sslwin, text="Check server SSL certificate?",
1657 variable=self.sslcertck).pack(side=TOP, fill=X)
1658 LabeledEntry(sslwin, 'SSL trusted certificate directory:',
1659 self.sslcertpath, '14').pack(side=TOP, fill=X)
1660 LabeledEntry(sslwin, 'SSL CommonName:',
1661 self.sslcommonname, '14').pack(side=TOP, fill=X)
1662 LabeledEntry(sslwin, 'SSL key fingerprint:',
1663 self.sslfingerprint, '14').pack(side=TOP, fill=X)
1664 sslwin.pack(fill=X, anchor=N)
1666 names = Frame(leftwin, relief=RAISED, bd=5)
1667 Label(names, text="Local names").pack(side=TOP)
1668 ListEdit("New name: ",
1669 self.user.localnames, None, None, names, localhelp)
1670 names.pack(fill=X, anchor=N)
1672 if mode != 'novice':
1673 targwin = Frame(leftwin, relief=RAISED, bd=5)
1674 Label(targwin, text="Forwarding Options").pack(side=TOP)
1675 Label(targwin, text="Listeners to forward to").pack(side=TOP)
1676 ListEdit("New listener:",
1677 self.user.smtphunt, None, None, targwin, None)
1678 Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
1679 ListEdit("Domains:",
1680 self.user.fetchdomains, None, None, targwin, None)
1681 LabeledEntry(targwin, 'Use domain on RCPT TO line:',
1682 self.smtpaddress, '26').pack(side=TOP, fill=X)
1683 LabeledEntry(targwin, 'Set fixed RCPT TO address:',
1684 self.smtpname, '26').pack(side=TOP, fill=X)
1685 LabeledEntry(targwin, 'Connection setup command:',
1686 self.preconnect, '26').pack(side=TOP, fill=X)
1687 LabeledEntry(targwin, 'Connection wrapup command:',
1688 self.postconnect, '26').pack(side=TOP, fill=X)
1689 LabeledEntry(targwin, 'Local delivery agent:',
1690 self.mda, '26').pack(side=TOP, fill=X)
1691 LabeledEntry(targwin, 'BSMTP output file:',
1692 self.bsmtp, '26').pack(side=TOP, fill=X)
1693 LabeledEntry(targwin, 'Listener spam-block codes:',
1694 self.antispam, '26').pack(side=TOP, fill=X)
1695 LabeledEntry(targwin, 'Pass-through properties:',
1696 self.properties, '26').pack(side=TOP, fill=X)
1697 Checkbutton(targwin, text="Use LMTP?",
1698 variable=self.lmtp).pack(side=TOP, fill=X)
1699 targwin.pack(fill=X, anchor=N)
1701 if mode != 'novice':
1702 leftwin.pack(side=LEFT, fill=X, anchor=N)
1703 rightwin = Frame(self)
1707 optwin = Frame(rightwin, relief=RAISED, bd=5)
1708 Label(optwin, text="Processing Options").pack(side=TOP)
1709 Checkbutton(optwin, text="Suppress deletion of messages after reading",
1710 variable=self.keep).pack(side=TOP, anchor=W)
1711 Checkbutton(optwin, text="Fetch old messages as well as new",
1712 variable=self.fetchall).pack(side=TOP, anchor=W)
1713 if mode != 'novice':
1714 Checkbutton(optwin, text="Flush seen messages before retrieval",
1715 variable=self.flush).pack(side=TOP, anchor=W)
1716 Checkbutton(optwin, text="Flush oversized messages before retrieval",
1717 variable=self.limitflush).pack(side=TOP, anchor=W)
1718 Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply",
1719 variable=self.rewrite).pack(side=TOP, anchor=W)
1720 Checkbutton(optwin, text="Force CR/LF at end of each line",
1721 variable=self.forcecr).pack(side=TOP, anchor=W)
1722 Checkbutton(optwin, text="Strip CR from end of each line",
1723 variable=self.stripcr).pack(side=TOP, anchor=W)
1724 Checkbutton(optwin, text="Pass 8 bits even though SMTP says 7BIT",
1725 variable=self.pass8bits).pack(side=TOP, anchor=W)
1726 Checkbutton(optwin, text="Undo MIME armoring on header and body",
1727 variable=self.mimedecode).pack(side=TOP, anchor=W)
1728 Checkbutton(optwin, text="Drop Status lines from forwarded messages",
1729 variable=self.dropstatus).pack(side=TOP, anchor=W)
1730 Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
1731 variable=self.dropdelivered).pack(side=TOP, anchor=W)
1734 if mode != 'novice':
1735 limwin = Frame(rightwin, relief=RAISED, bd=5)
1736 Label(limwin, text="Resource Limits").pack(side=TOP)
1737 LabeledEntry(limwin, 'Message size limit:',
1738 self.limit, '30').pack(side=TOP, fill=X)
1739 LabeledEntry(limwin, 'Size warning interval:',
1740 self.warnings, '30').pack(side=TOP, fill=X)
1741 LabeledEntry(limwin, 'Max messages to fetch per poll:',
1742 self.fetchlimit, '30').pack(side=TOP, fill=X)
1743 LabeledEntry(limwin, 'Max message sizes to fetch per transaction:',
1744 self.fetchsizelimit, '30').pack(side=TOP, fill=X)
1745 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1746 LabeledEntry(limwin, 'Use fast UIDL:',
1747 self.fastuidl, '30').pack(side=TOP, fill=X)
1748 LabeledEntry(limwin, 'Max messages to forward per poll:',
1749 self.batchlimit, '30').pack(side=TOP, fill=X)
1750 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1751 LabeledEntry(limwin, 'Interval between expunges:',
1752 self.expunge, '30').pack(side=TOP, fill=X)
1753 Checkbutton(limwin, text="Idle after each poll (IMAP only)",
1754 variable=self.idle).pack(side=TOP, anchor=W)
1757 if self.parent.server.protocol == 'IMAP':
1758 foldwin = Frame(rightwin, relief=RAISED, bd=5)
1759 Label(foldwin, text="Remote folders (IMAP only)").pack(side=TOP)
1760 ListEdit("New folder:", self.user.mailboxes,
1761 None, None, foldwin, None)
1762 foldwin.pack(fill=X, anchor=N)
1764 if mode != 'novice':
1765 rightwin.pack(side=LEFT)
1771 # Top-level window that offers either novice or expert mode
1772 # (but not both at once; it disappears when one is selected).
1775 class Configurator(Frame):
1776 def __init__(self, outfile, master, onexit, parent):
1777 Frame.__init__(self, master)
1778 self.outfile = outfile
1779 self.onexit = onexit
1780 self.parent = parent
1781 self.master.title('fetchmail configurator');
1782 self.master.iconname('fetchmail configurator');
1784 self.keepalive = [] # Use this to anchor the PhotoImage object
1785 make_icon_window(self, fetchmail_icon)
1787 Message(self, text="""
1788 Use `Novice Configuration' for basic fetchmail setup;
1789 with this, you can easily set up a single-drop connection
1790 to one remote mail server.
1791 """, width=600).pack(side=TOP)
1792 Button(self, text='Novice Configuration',
1793 fg='blue', command=self.novice).pack()
1795 Message(self, text="""
1796 Use `Expert Configuration' for advanced fetchmail setup,
1797 including multiple-site or multidrop connections.
1798 """, width=600).pack(side=TOP)
1799 Button(self, text='Expert Configuration',
1800 fg='blue', command=self.expert).pack()
1802 Message(self, text="""
1803 Or you can just select `Quit' to leave the configurator now and
1804 return to the main panel.
1805 """, width=600).pack(side=TOP)
1806 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1807 master.protocol("WM_DELETE_WINDOW", self.leave)
1810 self.master.destroy()
1811 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1814 self.master.destroy()
1815 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1818 self.master.destroy()
1821 # Run a command in a scrolling text widget, displaying its output
1823 class RunWindow(Frame):
1824 def __init__(self, command, master, parent):
1825 Frame.__init__(self, master)
1826 self.master = master
1827 self.master.title('fetchmail run window');
1828 self.master.iconname('fetchmail run window');
1831 text="Running "+command,
1832 bd=2).pack(side=TOP, pady=10)
1833 self.keepalive = [] # Use this to anchor the PhotoImage object
1834 make_icon_window(self, fetchmail_icon)
1836 # This is a scrolling text window
1837 textframe = Frame(self)
1838 scroll = Scrollbar(textframe)
1839 self.textwidget = Text(textframe, setgrid=TRUE)
1840 textframe.pack(side=TOP, expand=YES, fill=BOTH)
1841 self.textwidget.config(yscrollcommand=scroll.set)
1842 self.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
1843 scroll.config(command=self.textwidget.yview)
1844 scroll.pack(side=RIGHT, fill=BOTH)
1845 textframe.pack(side=TOP)
1847 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1849 self.update() # Draw widget before executing fetchmail
1851 # Always look for a runnable command in the directory we're running in
1852 # first. This avoids some obscure version-skew errors that can occur
1853 # if you pick up an old fetchmail from the standard system locations.
1854 os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"]
1855 child_stdout = os.popen(command + " 2>&1 </dev/null", "r")
1857 ch = child_stdout.read(1)
1860 self.textwidget.insert(END, ch)
1861 self.textwidget.insert(END, "Done.")
1862 self.textwidget.see(END);
1865 self.master.destroy()
1867 # Here's where we choose either configuration or launching
1869 class MainWindow(Frame):
1870 def __init__(self, outfile, master=None):
1871 Frame.__init__(self, master)
1872 self.outfile = outfile
1873 self.master.title('fetchmail launcher');
1874 self.master.iconname('fetchmail launcher');
1877 text='Fetchmailconf ' + version,
1878 bd=2).pack(side=TOP, pady=10)
1879 self.keepalive = [] # Use this to anchor the PhotoImage object
1880 make_icon_window(self, fetchmail_icon)
1883 ## Test icon display with the following:
1884 # icon_image = PhotoImage(data=fetchmail_icon)
1885 # Label(self, image=icon_image).pack(side=TOP, pady=10)
1886 # self.keepalive.append(icon_image)
1888 Message(self, text="""
1889 Use `Configure fetchmail' to tell fetchmail about the remote
1890 servers it should poll (the host name, your username there,
1891 whether to use POP or IMAP, and so forth).
1892 """, width=600).pack(side=TOP)
1893 self.configbutton = Button(self, text='Configure fetchmail',
1894 fg='blue', command=self.configure)
1895 self.configbutton.pack()
1897 Message(self, text="""
1898 Use `Run fetchmail' to run fetchmail with debugging enabled.
1899 This is a good way to test out a new configuration.
1900 """, width=600).pack(side=TOP)
1901 Button(self, text='Run fetchmail',fg='blue', command=self.test).pack()
1903 Message(self, text="""
1904 Use `Run fetchmail' to run fetchmail in foreground.
1905 Progress messages will be shown, but not debug messages.
1906 """, width=600).pack(side=TOP)
1907 Button(self, text='Run fetchmail', fg='blue', command=self.run).pack()
1909 Message(self, text="""
1910 Or you can just select `Quit' to exit the launcher now.
1911 """, width=600).pack(side=TOP)
1912 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1914 def configure(self):
1915 self.configbutton.configure(state=DISABLED)
1916 Configurator(self.outfile, Toplevel(),
1917 lambda self=self: self.configbutton.configure(state=NORMAL),
1920 cmd = "fetchmail -N -d0 --nosyslog -v"
1922 cmd = cmd + " -f " + rcfile
1923 RunWindow(cmd, Toplevel(), self)
1926 cmd = "fetchmail -N -d0"
1928 cmd = cmd + " -f " + rcfile
1929 RunWindow(cmd, Toplevel(), self)
1934 # Functions for turning a dictionary into an instantiated object tree.
1936 def intersect(list1, list2):
1937 # Compute set intersection of lists
1944 def setdiff(list1, list2):
1945 # Compute set difference of lists
1952 def copy_instance(toclass, fromdict):
1953 # Initialize a class object of given type from a conformant dictionary.
1954 for fld in fromdict.keys():
1955 if not fld in dictmembers:
1956 dictmembers.append(fld)
1957 # The `optional' fields are the ones we can ignore for purposes of
1958 # conformability checking; they'll still get copied if they are
1959 # present in the dictionary.
1960 optional = ('interface', 'monitor',
1961 'esmtpname', 'esmtppassword',
1962 'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
1963 'sslcertpath', 'sslcommonname', 'sslfingerprint', 'showdots')
1964 class_sig = setdiff(toclass.__dict__.keys(), optional)
1966 dict_keys = setdiff(fromdict.keys(), optional)
1968 common = intersect(class_sig, dict_keys)
1969 if 'typemap' in class_sig:
1970 class_sig.remove('typemap')
1971 if tuple(class_sig) != tuple(dict_keys):
1972 print "Fields don't match what fetchmailconf expected:"
1973 # print "Class signature: " + `class_sig`
1974 # print "Dictionary keys: " + `dict_keys`
1975 diff = setdiff(class_sig, common)
1977 print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
1978 diff = setdiff(dict_keys, common)
1980 print "Not matched in dictionary keys: " + `diff`
1983 for x in fromdict.keys():
1984 setattr(toclass, x, fromdict[x])
1987 # And this is the main sequence. How it works:
1989 # First, call `fetchmail --configdump' and trap the output in a tempfile.
1990 # This should fill it with a Python initializer for a variable `fetchmailrc'.
1991 # Run execfile on the file to pull fetchmailrc into Python global space.
1992 # You don't want static data, though; you want, instead, a tree of objects
1993 # with the same data members and added appropriate methods.
1995 # This is what the copy_instance function() is for. It tries to copy a
1996 # dictionary field by field into a class, aborting if the class and dictionary
1997 # have different data members (except for any typemap member in the class;
1998 # that one is strictly for use by the MyWidget supperclass).
2000 # Once the object tree is set up, require user to choose novice or expert
2001 # mode and instantiate an edit object for the configuration. Class methods
2002 # will take it all from there.
2004 # Options (not documented because they're for fetchmailconf debuggers only):
2005 # -d: Read the configuration and dump it to stdout before editing. Dump
2006 # the edited result to stdout as well.
2007 # -f: specify the run control file to read.
2009 if __name__ == '__main__':
2011 if not os.environ.has_key("DISPLAY"):
2012 print "fetchmailconf must be run under X"
2015 fetchmail_icon = """
2016 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
2017 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
2018 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
2019 CJR7Y/+UMdbOxnNrY97Ove/Wvd7GrZyEa961jL2Ua9alc86ca7WEUntSKcaMSqVjGNZ7GGM5CNa1
2020 jPfOnN6tc3taMffeve/WtWtaQv/OjGtSMYRzWv/erda1hM6te7WUY62MWs61jP/vzv/ntda9jL2l
2021 czEhAO/n1oyEc//elDEpGEo5EOfexpyUe+/epefevffvxnNrQpyUStbWzsbGvZyclN7ezmNjWv//
2022 5/f33qWllNbWve/vzv//1ufnve/vvf//xvf3vefnrf//taWlc0pKMf//pbW1Y///jKWlWq2tWsbG
2023 Y///c97eUvf3Ut7nc+/3a87We8bOjOfv1u/37/f//621tb3Gxtbn52Nra87n53uUlJTv/6W9xuf3
2024 /8bW3iExOXu11tbv/5TW/4TO/63e/zmt/1KUxlK1/2u9/wCM/73GzrXG1gBKjACE/87e72NzhCkx
2025 OaXO92OMtUql/xCE/wApUtbe57W9xnN7hHut52Ot/xBSnABKnABavQB7/2ul7zF71gBr77XO73Oc
2026 1lqc9yFSlBApSimE/wAYOQApY0J7zlKM5wAxhABS1gBj/6W95wAhWgA5nAAYSgBS7wBS/wBK9wAp
2027 jABC5wBK/wApnABC/wApxgAhtYSMtQAQYwAp/3OE74SMxgAYxlpjvWNr70pS/wgQ3sbGzs7O1qWl
2028 3qWl70pKe0JC/yEhlCkp/wgI/wAAEAAAIQAAKQAAOQAASgAAUgAAYwAAawAAlAAAnAAApQAArQAA
2029 zgAA1gAA5wAA9wAA/0pC/xgQ52Na9ykhe4R7zikhYxgQSjEpQgAAACwAAAAAPAAoAAAI/wABCBxI
2030 sKDBgwgTKiRIYKHDhxARIvgXsaLFhGgEUBSYoKPHjyBDihxJkuS/kwNLqlzJcuTJjQBaypxpEiVH
2031 mjhxvkyZs2fLnTd9ehxAtKjRo0ZrwhTasUsENhYHKOUpk1E3j11mxCBiQVLEBlJd2owp9iVRjwUs
2032 zMCQ5IcLD4saPVxjIKxIoGTvvqSoyFEFGTBeqEhyxAoSFR/USGKVcEGBAwDshsSr1OYTEyhQpJiS
2033 ZcoUKWOQtJDRJFSaggzUGBgoGSTlsjahlPCRIkWVKT16THHRIoqIISBIEUgAYIGBhgRbf3ytFygU
2034 FZp9UDmxQkkMCRwyZKDBQy4aApABhP8XqNwj88l7BVpQYZtF5iArWgwAgGZBq24HU7OeGhQ90PVA
2035 aKZZCiiUMJ9ArSTEwGqR8ZeXfzbV0MIIMQTBwoUdxDDfAm8sZFyDZVEF4UYSKBEBD0+k6IEFPMxH
2036 3FzldXSea+kBgANJSOWIlIMhXZXAXv+c1WM3PuJEpH8iuhbAkv+MdENPRHaTRkdF/jiWSKCAwlKW
2037 VbbkY5Q0LgUSKExgoYBKCjCxARpdltQNKHaUoYAddnR53lVRnJLKBWh4RIEGCZx5FSOv1OLNDUVe
2038 deZHaWiZAB35fIOGNtbEUeV5oGAByzPOrBPFGt3kwEgxITACSg5oLGGLMg60oQAjaNz/oAAcN4Ai
2039 a0c3kHFDK3jYsw4g9sRzBgPLXdkRrBrQ8gsWQUxCCRZX9IJNBQ1s8IgCdeBCzBYN6IBIN2TUsQYd
2040 dXhDBxdzlAHOHHKEcocZdWwDjx8MTCmjsR2FMAstw1RyiSzHqPLALaOwk8QmzCzDCSi0xJKMMk4E
2041 Yw8389iTDT32GAKOPf7YY0Aa9tATyD3w/EGsefgmgEYUtPiChLKWQDMBJtEUgYkzH2RiTgGfTMCI
2042 Mlu0Yc85hNiDziH2tMqOGL72QY47gshLb7Fi4roELcjoQIsxWpDwQyfS2OCJMkLI4YUmyhgxSTVg
2043 CP2FHPZ80UDcieBjStNPD5LPOyZT/y0iHGiMwswexDSzRiRq6KIMJBc4M8skwKAyChia2KPH3P24
2044 YU8/lFhOTj152OPOHuXMU4g48vCRiN/9rZGLMdS4csUu1JzDgxuipOMDHMKsAwEnq/ByzTrrZMNO
2045 OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
2046 xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
2047 gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
2049 # The base64 data in the string above was generated by the following procedure:
2052 # print base64.encodestring(open("fetchmail.gif", "rb").read())
2056 (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV", ["help",
2058 dump = rcfile = None;
2059 for (switch, val) in options:
2060 if (switch == '-d'):
2062 elif (switch == '-f'):
2064 elif (switch == '-h' or switch == '--help'):
2066 Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|--help|-V|--version}
2067 -d - dump configuration (for debugging)
2068 -f fmrc - read alternate fetchmailrc file
2069 --help, -h - print this help text and quit
2070 --version, -V - print fetchmailconf version and quit
2073 elif (switch == '-V' or switch == '--version'):
2074 print "fetchmailconf %s" % version
2076 Copyright (C) 1997 - 2003 Eric S. Raymond
2077 Copyright (C) 2005, 2006, 2008, 2009 Matthias Andree
2078 fetchmailconf comes with ABSOLUTELY NO WARRANTY. This is free software, you are
2079 welcome to redistribute it under certain conditions. Please see the file
2080 COPYING in the source or documentation directory for details."""
2083 # Get client host's FQDN
2084 hostname = socket.gethostbyaddr(socket.gethostname())[0]
2087 ConfigurationDefaults = Configuration()
2088 ServerDefaults = Server()
2089 UserDefaults = User()
2091 # Read the existing configuration. We set the umask to 077 to make sure
2092 # that group & other read/write permissions are shut off -- we wouldn't
2093 # want crackers to snoop password information out of the tempfile.
2094 tmpfile = tempfile.mktemp()
2096 cmd = "umask 077 && fetchmail </dev/null -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2098 cmd = "umask 077 && fetchmail </dev/null --configdump --nosyslog >" + tmpfile
2103 print "`" + cmd + "' run failure, status " + `s`
2106 print "Unknown error while running fetchmail --configdump"
2113 print "Can't read configuration output of fetchmail --configdump."
2119 # The tricky part -- initializing objects from the configuration global
2120 # `Configuration' is the top level of the object tree we're going to mung.
2121 # The dictmembers list is used to track the set of fields the dictionary
2122 # contains; in particular, we can use it to tell whether things like the
2123 # monitor, interface, ssl, sslkey, or sslcert fields are present.
2125 Fetchmailrc = Configuration()
2126 copy_instance(Fetchmailrc, fetchmailrc)
2127 Fetchmailrc.servers = [];
2128 for server in fetchmailrc['servers']:
2130 copy_instance(Newsite, server)
2131 Fetchmailrc.servers.append(Newsite)
2133 for user in server['users']:
2135 copy_instance(Newuser, user)
2136 Newsite.users.append(Newuser)
2138 # We may want to display the configuration and quit
2140 print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2142 # The theory here is that -f alone sets the rcfile location,
2143 # but -d and -f together mean the new configuration should go to stdout.
2144 if not rcfile and not dump:
2145 rcfile = os.environ["HOME"] + "/.fetchmailrc"
2147 # OK, now run the configuration edit
2148 root = MainWindow(rcfile)
2151 # The following sets edit modes for GNU EMACS