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.properties = None # No exiguous properties
26 self.invisible = FALSE # Suppress Received line & spoof?
27 self.syslog = FALSE # Use syslogd for logging?
28 self.servers = [] # List of included sites
29 Configuration.typemap = (
30 ('poll_interval', 'Int'),
31 ('logfile', 'String'),
33 ('postmaster', 'String'),
34 ('bouncemail', 'Boolean'),
35 ('spambounce', 'Boolean'),
36 ('properties', 'String'),
37 ('syslog', 'Boolean'),
38 ('invisible', 'Boolean'))
42 if self.syslog != ConfigurationDefaults.syslog:
43 str = str + ("set syslog\n")
45 str = str + ("set logfile \"%s\"\n" % (self.logfile,));
46 if self.idfile != ConfigurationDefaults.idfile:
47 str = str + ("set idfile \"%s\"\n" % (self.idfile,));
48 if self.postmaster != ConfigurationDefaults.postmaster:
49 str = str + ("set postmaster \"%s\"\n" % (self.postmaster,));
51 str = str + ("set bouncemail\n")
53 str = str + ("set nobouncemail\n")
55 str = str + ("set spambounce\n")
57 str = str + ("set no spambounce\n")
58 if self.properties != ConfigurationDefaults.properties:
59 str = str + ("set properties \"%s\"\n" % (self.properties,));
60 if self.poll_interval > 0:
61 str = str + "set daemon " + `self.poll_interval` + "\n"
62 for site in self.servers:
63 str = str + repr(site)
66 def __delitem__(self, name):
67 for si in range(len(self.servers)):
68 if self.servers[si].pollname == name:
73 return "[Configuration: " + repr(self) + "]"
77 self.pollname = None # Poll label
78 self.via = None # True name of host
79 self.active = TRUE # Poll status
80 self.interval = 0 # Skip interval
81 self.protocol = 'auto' # Default to auto protocol
82 self.service = None # Service name to use
83 self.uidl = FALSE # Don't use RFC1725 UIDLs by default
84 self.auth = 'any' # Default to password authentication
85 self.timeout = 300 # 5-minute timeout
86 self.envelope = 'Received' # Envelope-address header
87 self.envskip = 0 # Number of envelope headers to skip
88 self.qvirtual = None # Name prefix to strip
89 self.aka = [] # List of DNS aka names
90 self.dns = TRUE # Enable DNS lookup on multidrop
91 self.localdomains = [] # Domains to be considered local
92 self.interface = None # IP address and range
93 self.monitor = None # IP address and range
94 self.plugin = None # Plugin command for going to server
95 self.plugout = None # Plugin command for going to listener
96 self.principal = None # Kerberos principal
97 self.esmtpname = None # ESMTP 2554 name
98 self.esmtppassword = None # ESMTP 2554 password
99 self.tracepolls = FALSE # Add trace-poll info to headers
100 self.users = [] # List of user entries for site
102 ('pollname', 'String'),
104 ('active', 'Boolean'),
106 ('protocol', 'String'),
107 ('service', 'String'),
111 ('envelope', 'String'),
113 ('qvirtual', 'String'),
116 # leave localdomains out
117 ('interface', 'String'),
118 ('monitor', 'String'),
119 ('plugin', 'String'),
120 ('plugout', 'String'),
121 ('esmtpname', 'String'),
122 ('esmtppassword', 'String'),
123 ('principal', 'String'),
124 ('tracepolls','Boolean'))
126 def dump(self, folded):
128 if self.active: res = res + "poll"
129 else: res = res + "skip"
130 res = res + (" " + self.pollname)
132 res = res + (" via " + str(self.via) + "\n");
133 if self.protocol != ServerDefaults.protocol:
134 res = res + " with proto " + self.protocol
135 if self.service and self.protocol and self.service != defaultports[self.protocol] and defaultports[self.protocol] and self.service != ianaservices[defaultports[self.protocol]]:
136 res = res + " service " + self.service
137 if self.timeout != ServerDefaults.timeout:
138 res = res + " timeout " + `self.timeout`
139 if self.interval != ServerDefaults.interval:
140 res = res + " interval " + `self.interval`
141 if self.envelope != ServerDefaults.envelope or self.envskip != ServerDefaults.envskip:
143 res = res + " envelope " + `self.envskip` + " " + self.envelope
145 res = res + " envelope " + self.envelope
147 res = res + (" qvirtual " + str(self.qvirtual) + "\n");
148 if self.auth != ServerDefaults.auth:
149 res = res + " auth " + self.auth
150 if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl:
151 res = res + " and options"
152 if self.dns != ServerDefaults.dns:
153 res = res + flag2str(self.dns, 'dns')
154 if self.uidl != ServerDefaults.uidl:
155 res = res + flag2str(self.uidl, 'uidl')
156 if folded: res = res + "\n "
157 else: res = res + " "
163 if self.aka and self.localdomains: res = res + " "
164 if self.localdomains:
165 res = res + ("localdomains")
166 for x in self.localdomains:
168 if (self.aka or self.localdomains):
175 res = res + "tracepolls\n"
178 res = res + " interface " + str(self.interface)
180 res = res + " monitor " + str(self.monitor)
182 res = res + " plugin " + `self.plugin`
184 res = res + " plugout " + `self.plugout`
186 res = res + " principal " + `self.principal`
188 res = res + " esmtpname " + `self.esmtpname`
189 if self.esmtppassword:
190 res = res + " esmtppassword " + `self.esmtppassword`
191 if self.interface or self.monitor or self.principal or self.plugin or self.plugout:
195 if res[-1] == " ": res = res[0:-1]
197 for user in self.users:
198 res = res + repr(user)
202 def __delitem__(self, name):
203 for ui in range(len(self.users)):
204 if self.users[ui].remote == name:
209 return self.dump(TRUE)
212 return "[Server: " + self.dump(FALSE) + "]"
216 if os.environ.has_key("USER"):
217 self.remote = os.environ["USER"] # Remote username
218 elif os.environ.has_key("LOGNAME"):
219 self.remote = os.environ["LOGNAME"]
221 print "Can't get your username!"
223 self.localnames = [self.remote,]# Local names
224 self.password = None # Password for mail account access
225 self.mailboxes = [] # Remote folders to retrieve from
226 self.smtphunt = [] # Hosts to forward to
227 self.fetchdomains = [] # Domains to fetch from
228 self.smtpaddress = None # Append this to MAIL FROM line
229 self.smtpname = None # Use this for RCPT TO
230 self.preconnect = None # Connection setup
231 self.postconnect = None # Connection wrapup
232 self.mda = None # Mail Delivery Agent
233 self.bsmtp = None # BSMTP output file
234 self.lmtp = FALSE # Use LMTP rather than SMTP?
235 self.antispam = "" # Listener's spam-block code
236 self.keep = FALSE # Keep messages
237 self.flush = FALSE # Flush messages
238 self.limitflush = FALSE # Flush oversized messages
239 self.fetchall = FALSE # Fetch old messages
240 self.rewrite = TRUE # Rewrite message headers
241 self.forcecr = FALSE # Force LF -> CR/LF
242 self.stripcr = FALSE # Strip CR
243 self.pass8bits = FALSE # Force BODY=7BIT
244 self.mimedecode = FALSE # Undo MIME armoring
245 self.dropstatus = FALSE # Drop incoming Status lines
246 self.dropdelivered = FALSE # Drop incoming Delivered-To lines
247 self.idle = FALSE # IDLE after poll
248 self.limit = 0 # Message size limit
249 self.warnings = 3600 # Size warning interval (see tunable.h)
250 self.fetchlimit = 0 # Max messages fetched per batch
251 self.fetchsizelimit = 100 # Max message sizes fetched per transaction
252 self.fastuidl = 10 # Do fast uidl 9 out of 10 times
253 self.batchlimit = 0 # Max message forwarded per batch
254 self.expunge = 0 # Interval between expunges (IMAP)
255 self.ssl = 0 # Enable Seccure Socket Layer
256 self.sslkey = None # SSL key filename
257 self.sslcert = None # SSL certificate filename
258 self.sslproto = None # Force SSL?
259 self.sslcertck = 0 # Enable strict SSL cert checking
260 self.sslcertpath = None # Path to trusted certificates
261 self.sslfingerprint = None # SSL key fingerprint to check
262 self.properties = None # Extension properties
264 ('remote', 'String'),
265 # leave out mailboxes and localnames
266 ('password', 'String'),
267 # Leave out smtphunt, fetchdomains
268 ('smtpaddress', 'String'),
269 ('smtpname', 'String'),
270 ('preconnect', 'String'),
271 ('postconnect', 'String'),
275 ('antispam', 'String'),
277 ('flush', 'Boolean'),
278 ('limitflush', 'Boolean'),
279 ('fetchall', 'Boolean'),
280 ('rewrite', 'Boolean'),
281 ('forcecr', 'Boolean'),
282 ('stripcr', 'Boolean'),
283 ('pass8bits', 'Boolean'),
284 ('mimedecode', 'Boolean'),
285 ('dropstatus', 'Boolean'),
286 ('dropdelivered', 'Boolean'),
290 ('fetchlimit', 'Int'),
291 ('fetchsizelimit', 'Int'),
293 ('batchlimit', 'Int'),
296 ('sslkey', 'String'),
297 ('sslcert', 'String'),
298 ('sslcertck', 'Boolean'),
299 ('sslcertpath', 'String'),
300 ('sslfingerprint', 'String'),
301 ('properties', 'String'))
305 res = res + "user " + `self.remote` + " there ";
307 res = res + "with password " + `self.password` + " "
310 for x in self.localnames:
311 res = res + " " + `x`
313 if (self.keep != UserDefaults.keep
314 or self.flush != UserDefaults.flush
315 or self.limitflush != UserDefaults.limitflush
316 or self.fetchall != UserDefaults.fetchall
317 or self.rewrite != UserDefaults.rewrite
318 or self.forcecr != UserDefaults.forcecr
319 or self.stripcr != UserDefaults.stripcr
320 or self.pass8bits != UserDefaults.pass8bits
321 or self.mimedecode != UserDefaults.mimedecode
322 or self.dropstatus != UserDefaults.dropstatus
323 or self.dropdelivered != UserDefaults.dropdelivered
324 or self.idle != UserDefaults.idle):
325 res = res + " options"
326 if self.keep != UserDefaults.keep:
327 res = res + flag2str(self.keep, 'keep')
328 if self.flush != UserDefaults.flush:
329 res = res + flag2str(self.flush, 'flush')
330 if self.limitflush != UserDefaults.limitflush:
331 res = res + flag2str(self.limitflush, 'limitflush')
332 if self.fetchall != UserDefaults.fetchall:
333 res = res + flag2str(self.fetchall, 'fetchall')
334 if self.rewrite != UserDefaults.rewrite:
335 res = res + flag2str(self.rewrite, 'rewrite')
336 if self.forcecr != UserDefaults.forcecr:
337 res = res + flag2str(self.forcecr, 'forcecr')
338 if self.stripcr != UserDefaults.stripcr:
339 res = res + flag2str(self.stripcr, 'stripcr')
340 if self.pass8bits != UserDefaults.pass8bits:
341 res = res + flag2str(self.pass8bits, 'pass8bits')
342 if self.mimedecode != UserDefaults.mimedecode:
343 res = res + flag2str(self.mimedecode, 'mimedecode')
344 if self.dropstatus != UserDefaults.dropstatus:
345 res = res + flag2str(self.dropstatus, 'dropstatus')
346 if self.dropdelivered != UserDefaults.dropdelivered:
347 res = res + flag2str(self.dropdelivered, 'dropdelivered')
348 if self.idle != UserDefaults.idle:
349 res = res + flag2str(self.idle, 'idle')
350 if self.limit != UserDefaults.limit:
351 res = res + " limit " + `self.limit`
352 if self.warnings != UserDefaults.warnings:
353 res = res + " warnings " + `self.warnings`
354 if self.fetchlimit != UserDefaults.fetchlimit:
355 res = res + " fetchlimit " + `self.fetchlimit`
356 if self.fetchsizelimit != UserDefaults.fetchsizelimit:
357 res = res + " fetchsizelimit " + `self.fetchsizelimit`
358 if self.fastuidl != UserDefaults.fastuidl:
359 res = res + " fastuidl " + `self.fastuidl`
360 if self.batchlimit != UserDefaults.batchlimit:
361 res = res + " batchlimit " + `self.batchlimit`
362 if self.ssl and self.ssl != UserDefaults.ssl:
363 res = res + flag2str(self.ssl, 'ssl')
364 if self.sslkey and self.sslkey != UserDefaults.sslkey:
365 res = res + " sslkey " + `self.sslkey`
366 if self.sslcert and self.sslcert != UserDefaults.sslcert:
367 res = res + " sslcert " + `self.sslcert`
368 if self.sslproto and self.sslproto != UserDefaults.sslproto:
369 res = res + " sslproto " + `self.sslproto`
370 if self.sslcertck and self.sslcertck != UserDefaults.sslcertck:
371 res = res + flag2str(self.sslcertck, 'sslcertck')
372 if self.sslcertpath and self.sslcertpath != UserDefaults.sslcertpath:
373 res = res + " sslcertpath " + `self.sslcertpath`
374 if self.sslfingerprint and self.sslfingerprint != UserDefaults.sslfingerprint:
375 res = res + " sslfingerprint " + `self.sslfingerprint`
376 if self.expunge != UserDefaults.expunge:
377 res = res + " expunge " + `self.expunge`
379 trimmed = self.smtphunt;
380 if trimmed != [] and trimmed[len(trimmed) - 1] == "localhost":
381 trimmed = trimmed[0:len(trimmed) - 1]
382 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
383 trimmed = trimmed[0:len(trimmed) - 1]
385 res = res + " smtphost "
389 trimmed = self.fetchdomains
390 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
391 trimmed = trimmed[0:len(trimmed) - 1]
393 res = res + " fetchdomains "
398 res = res + " folder"
399 for x in self.mailboxes:
402 for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'):
403 if getattr(self, fld):
404 res = res + " %s %s\n" % (fld, `getattr(self, fld)`)
405 if self.lmtp != UserDefaults.lmtp:
406 res = res + flag2str(self.lmtp, 'lmtp')
407 if self.antispam != UserDefaults.antispam:
408 res = res + " antispam " + self.antispam + "\n"
412 return "[User: " + repr(self) + "]"
418 # IANA port assignments and bogus 1109 entry
419 ianaservices = {"pop2":109,
426 # fetchmail protocol to IANA service name
427 defaultports = {"auto":None,
436 authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp",
440 'title' : 'List Selection Help',
441 'banner': 'List Selection',
443 You must select an item in the list box (by clicking on it).
446 def flag2str(value, string):
447 # make a string representation of a .fetchmailrc flag or negated flag
451 if value == FALSE: str = str + ("no ")
455 class LabeledEntry(Frame):
456 # widget consisting of entry field with caption to left
457 def bind(self, key, action):
458 self.E.bind(key, action)
461 def __init__(self, Master, text, textvar, lwidth, ewidth=12):
462 Frame.__init__(self, Master)
463 self.L = Label(self, {'text':text, 'width':lwidth, 'anchor':'w'})
464 self.E = Entry(self, {'textvar':textvar, 'width':ewidth})
465 self.L.pack({'side':'left'})
466 self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
468 def ButtonBar(frame, legend, ref, alternatives, depth, command):
469 # array of radio buttons, caption to left, picking from a string list
471 width = (len(alternatives)+1) / depth;
472 Label(bar, text=legend).pack(side=LEFT)
473 for column in range(width):
474 subframe = Frame(bar)
475 for row in range(depth):
476 ind = width * row + column
477 if ind < len(alternatives):
478 Radiobutton(subframe,
479 {'text':alternatives[ind],
481 'value':alternatives[ind],
482 'command':command}).pack(side=TOP, anchor=W)
484 # This is just a spacer
485 Radiobutton(subframe,
486 {'text':" ",'state':DISABLED}).pack(side=TOP, anchor=W)
487 subframe.pack(side=LEFT)
491 def helpwin(helpdict):
492 # help message window with a self-destruct button
494 helpwin.title(helpdict['title'])
495 helpwin.iconname(helpdict['title'])
496 Label(helpwin, text=helpdict['banner']).pack()
497 textframe = Frame(helpwin)
498 scroll = Scrollbar(textframe)
499 helpwin.textwidget = Text(textframe, setgrid=TRUE)
500 textframe.pack(side=TOP, expand=YES, fill=BOTH)
501 helpwin.textwidget.config(yscrollcommand=scroll.set)
502 helpwin.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
503 scroll.config(command=helpwin.textwidget.yview)
504 scroll.pack(side=RIGHT, fill=BOTH)
505 helpwin.textwidget.insert(END, helpdict['text']);
506 Button(helpwin, text='Done',
507 command=lambda x=helpwin: x.destroy(), bd=2).pack()
508 textframe.pack(side=TOP)
510 def make_icon_window(base, image):
512 # Some older pythons will error out on this
513 icon_image = PhotoImage(data=image)
514 icon_window = Toplevel()
515 Label(icon_window, image=icon_image, bg='black').pack()
516 base.master.iconwindow(icon_window)
517 # Avoid TkInter brain death. PhotoImage objects go out of
518 # scope when the enclosing function returns. Therefore
519 # we have to explicitly link them to something.
520 base.keepalive.append(icon_image)
524 class ListEdit(Frame):
525 # edit a list of values (duplicates not allowed) with a supplied editor hook
526 def __init__(self, newlegend, list, editor, deletor, master, helptxt):
528 self.deletor = deletor
531 # Set up a widget to accept new elements
532 self.newval = StringVar(master)
533 newwin = LabeledEntry(master, newlegend, self.newval, '12')
534 newwin.bind('<Double-1>', self.handleNew)
535 newwin.bind('<Return>', self.handleNew)
536 newwin.pack(side=TOP, fill=X, anchor=E)
538 # Edit the existing list
539 listframe = Frame(master)
540 scroll = Scrollbar(listframe)
541 self.listwidget = Listbox(listframe, height=0, selectmode='browse')
544 self.listwidget.insert(END, x)
545 listframe.pack(side=TOP, expand=YES, fill=BOTH)
546 self.listwidget.config(yscrollcommand=scroll.set)
547 self.listwidget.pack(side=LEFT, expand=YES, fill=BOTH)
548 scroll.config(command=self.listwidget.yview)
549 scroll.pack(side=RIGHT, fill=BOTH)
550 self.listwidget.config(selectmode=SINGLE, setgrid=TRUE)
551 self.listwidget.bind('<Double-1>', self.handleList);
552 self.listwidget.bind('<Return>', self.handleList);
556 Button(bf, text='Edit', command=self.editItem).pack(side=LEFT)
557 Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT)
559 self.helptxt = helptxt
560 Button(bf, text='Help', fg='blue',
561 command=self.help).pack(side=RIGHT)
565 helpwin(self.helptxt)
567 def handleList(self, event):
570 def handleNew(self, event):
571 item = self.newval.get()
573 entire = self.listwidget.get(0, self.listwidget.index('end'));
574 if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
575 self.listwidget.insert('end', item)
576 if self.list != None: self.list.append(item)
578 apply(self.editor, (item,))
582 select = self.listwidget.curselection()
587 if index and self.editor:
588 label = self.listwidget.get(index);
590 apply(self.editor, (label,))
592 def deleteItem(self):
593 select = self.listwidget.curselection()
597 index = string.atoi(select[0])
598 label = self.listwidget.get(index);
599 self.listwidget.delete(index)
600 if self.list != None:
602 if self.deletor != None:
603 apply(self.deletor, (label,))
605 def ConfirmQuit(frame, context):
608 text = 'Really quit ' + context + ' without saving?',
610 strings = ('Yes', 'No'),
614 def dispose_window(master, legend, help, savelegend='OK'):
615 dispose = Frame(master, relief=RAISED, bd=5)
616 Label(dispose, text=legend).pack(side=TOP,pady=10)
617 Button(dispose, text=savelegend, fg='blue',
618 command=master.save).pack(side=LEFT)
619 Button(dispose, text='Quit', fg='blue',
620 command=master.nosave).pack(side=LEFT)
621 Button(dispose, text='Help', fg='blue',
622 command=lambda x=help: helpwin(x)).pack(side=RIGHT)
627 # Common methods for Tkinter widgets -- deals with Tkinter declaration
628 def post(self, widgetclass, field):
629 for x in widgetclass.typemap:
630 if x[1] == 'Boolean':
631 setattr(self, x[0], BooleanVar(self))
632 elif x[1] == 'String':
633 setattr(self, x[0], StringVar(self))
635 setattr(self, x[0], IntVar(self))
636 source = getattr(getattr(self, field), x[0])
638 getattr(self, x[0]).set(source)
640 def fetch(self, widgetclass, field):
641 for x in widgetclass.typemap:
642 setattr(getattr(self, field), x[0], getattr(self, x[0]).get())
645 # First, code to set the global fetchmail run controls.
648 configure_novice_help = {
649 'title' : 'Fetchmail novice configurator help',
650 'banner': 'Novice configurator help',
652 In the `Novice Configurator Controls' panel, you can:
654 Press `Save' to save the new fetchmail configuration you have created.
655 Press `Quit' to exit without saving.
656 Press `Help' to bring up this help message.
658 In the `Novice Configuration' panels, you will set up the basic data
659 needed to create a simple fetchmail setup. These include:
661 1. The name of the remote site you want to query.
663 2. Your login name on that site.
665 3. Your password on that site.
667 4. A protocol to use (POP, IMAP, ETRN, etc.)
669 5. A polling interval.
671 6. Options to fetch old messages as well as new, uor to suppress
672 deletion of fetched message.
674 The novice-configuration code will assume that you want to forward mail
675 to a local sendmail listener with no special options.
678 configure_expert_help = {
679 'title' : 'Fetchmail expert configurator help',
680 'banner': 'Expert configurator help',
682 In the `Expert Configurator Controls' panel, you can:
684 Press `Save' to save the new fetchmail configuration you have edited.
685 Press `Quit' to exit without saving.
686 Press `Help' to bring up this help message.
688 In the `Run Controls' panel, you can set the following options that
689 control how fetchmail runs:
692 Number of seconds to wait between polls in the background.
693 If zero, fetchmail will run in foreground.
696 If empty, emit progress and error messages to stderr.
697 Otherwise this gives the name of the files to write to.
698 This field is ignored if the "Log to syslog?" option is on.
701 If empty, store seen-message IDs in .fetchids under user's home
702 directory. If nonempty, use given file name.
705 Who to send multidrop mail to as a last resort if no address can
706 be matched. Normally empty; in this case, fetchmail treats the
707 invoking user as the address of last resort unless that user is
708 root. If that user is root, fetchmail sends to `postmaster'.
711 If this option is on (the default) error mail goes to the sender.
712 Otherwise it goes to the postmaster.
715 If this option is on, spam bounces are sent to the sender or
716 postmaster (depending on the "Bounces to sender?" option. Otherwise,
717 spam bounces are not sent (the default).
720 If false (the default) fetchmail generates a Received line into
721 each message and generates a HELO from the machine it is running on.
722 If true, fetchmail generates no Received line and HELOs as if it were
725 In the `Remote Mail Configurations' panel, you can:
727 1. Enter the name of a new remote mail server you want fetchmail to query.
729 To do this, simply enter a label for the poll configuration in the
730 `New Server:' box. The label should be a DNS name of the server (unless
731 you are using ssh or some other tunneling method and will fill in the `via'
732 option on the site configuration screen).
734 2. Change the configuration of an existing site.
736 To do this, find the site's label in the listbox and double-click it.
737 This will take you to a site configuration dialogue.
741 class ConfigurationEdit(Frame, MyWidget):
742 def __init__(self, configuration, outfile, master, onexit):
744 self.configuration = configuration
745 self.outfile = outfile
746 self.container = master
748 ConfigurationEdit.mode_to_help = {
749 'novice':configure_novice_help, 'expert':configure_expert_help
752 def server_edit(self, sitename):
753 self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
755 def server_delete(self, sitename):
757 for user in self.subwidgets.keys():
759 del self.configuration[sitename]
763 def edit(self, mode):
765 Frame.__init__(self, self.container)
766 self.master.title('fetchmail ' + self.mode + ' configurator');
767 self.master.iconname('fetchmail ' + self.mode + ' configurator');
768 self.master.protocol('WM_DELETE_WINDOW', self.nosave)
769 self.keepalive = [] # Use this to anchor the PhotoImage object
770 make_icon_window(self, fetchmail_icon)
772 self.post(Configuration, 'configuration')
775 'Configurator ' + self.mode + ' Controls',
776 ConfigurationEdit.mode_to_help[self.mode],
779 gf = Frame(self, relief=RAISED, bd = 5)
781 text='Fetchmail Run Controls',
782 bd=2).pack(side=TOP, pady=10)
787 if self.mode != 'novice':
789 log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
790 log.pack(side=RIGHT, anchor=E)
792 # Set the poll interval
793 de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
794 de.pack(side=RIGHT, anchor=E)
799 if self.mode != 'novice':
802 {'text':'Bounces to sender?',
803 'variable':self.bouncemail,
804 'relief':GROOVE}).pack(side=LEFT, anchor=W)
809 {'text':'send spam bounces?',
810 'variable':self.spambounce,
811 'relief':GROOVE}).pack(side=LEFT, anchor=W)
816 {'text':'Log to syslog?',
817 'variable':self.syslog,
818 'relief':GROOVE}).pack(side=LEFT, anchor=W)
819 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
820 log.pack(side=RIGHT, anchor=E)
824 {'text':'Invisible mode?',
825 'variable':self.invisible,
826 'relief':GROOVE}).pack(side=LEFT, anchor=W)
828 log = LabeledEntry(gf, ' Idfile:', self.idfile, '14')
829 log.pack(side=RIGHT, anchor=E)
833 # Expert mode allows us to edit multiple sites
834 lf = Frame(self, relief=RAISED, bd=5)
836 text='Remote Mail Server Configurations',
837 bd=2).pack(side=TOP, pady=10)
838 ListEdit('New Server:',
839 map(lambda x: x.pollname, self.configuration.servers),
840 lambda site, self=self: self.server_edit(site),
841 lambda site, self=self: self.server_delete(site),
846 for sitename in self.subwidgets.keys():
847 self.subwidgets[sitename].destruct()
848 self.master.destroy()
852 if ConfirmQuit(self, self.mode + " configuration editor"):
856 for sitename in self.subwidgets.keys():
857 self.subwidgets[sitename].save()
858 self.fetch(Configuration, 'configuration')
862 elif not os.path.isfile(self.outfile) or Dialog(self,
863 title = 'Overwrite existing run control file?',
864 text = 'Really overwrite existing run control file?',
866 strings = ('Yes', 'No'),
867 default = 1).num == 0:
869 os.rename(self.outfile, self.outfile + "~")
870 # Pre-1.5.2 compatibility...
873 oldumask = os.umask(077)
874 fm = open(self.outfile, 'w')
879 os.chmod(self.outfile, 0600)
880 fm.write("# Configuration created %s by fetchmailconf %s\n" % (time.ctime(time.time()), version))
881 fm.write(`self.configuration`)
887 # Server editing stuff.
890 'title' : 'Remote site help',
891 'banner': 'Remote sites',
893 When you add a site name to the list here,
894 you initialize an entry telling fetchmail
895 how to poll a new site.
897 When you select a sitename (by double-
898 clicking it, or by single-clicking to
899 select and then clicking the Edit button),
900 you will open a window to configure that
905 'title' : 'Server options help',
906 'banner': 'Server Options',
908 The server options screen controls fetchmail
909 options that apply to one of your mailservers.
911 Once you have a mailserver configuration set
912 up as you like it, you can select `OK' to
913 store it in the server list maintained in
914 the main configuration window.
916 If you wish to discard changes to a server
917 configuration, select `Quit'.
921 'title' : 'Run Control help',
922 'banner': 'Run Controls',
924 If the `Poll normally' checkbox is on, the host is polled as part of
925 the normal operation of fetchmail when it is run with no arguments.
926 If it is off, fetchmail will only query this host when it is given as
927 a command-line argument.
929 The `True name of server' box should specify the actual DNS name
930 to query. By default this is the same as the poll name.
932 Normally each host described in the file is queried once each
933 poll cycle. If `Cycles to skip between polls' is greater than 0,
934 that's the number of poll cycles that are skipped between the
935 times this post is actually polled.
937 The `Server timeout' is the number of seconds fetchmail will wait
938 for a reply from the mailserver before concluding it is hung and
943 'title' : 'Protocol and Port help',
944 'banner': 'Protocol and Port',
946 These options control the remote-mail protocol
947 and TCP/IP service port used to query this
950 If you click the `Probe for supported protocols'
951 button, fetchmail will try to find you the most
952 capable server on the selected host (this will
953 only work if you're conncted to the Internet).
954 The probe only checks for ordinary IMAP and POP
955 protocols; fortunately these are the most
956 frequently supported.
958 The `Protocol' button bar offers you a choice of
959 all the different protocols available. The `auto'
960 protocol is the default mode; it probes the host
961 ports for POP3 and IMAP to see if either is
964 Normally the TCP/IP service port to use is
965 dictated by the protocol choice. The `Service'
966 field (only present in expert mode) lets you
967 set a non-standard service (port).
971 'title' : 'Security option help',
972 'banner': 'Security',
974 The 'authorization mode' allows you to choose the
975 mode that fetchmail uses to log in to your server. You
976 can usually leave this at 'any', but you will have to pick
977 'NTLM' and 'MSN' manually for the nonce.
979 The 'interface' option allows you to specify a range
980 of IP addresses to monitor for activity. If these
981 addresses are not active, fetchmail will not poll.
982 Specifying this may protect you from a spoofing attack
983 if your client machine has more than one IP gateway
984 address and some of the gateways are to insecure nets.
986 The `monitor' option, if given, specifies the only
987 device through which fetchmail is permitted to connect
988 to servers. This option may be used to prevent
989 fetchmail from triggering an expensive dial-out if the
990 interface is not already active.
992 The `interface' and `monitor' options are available
993 only for Linux and freeBSD systems. See the fetchmail
994 manual page for details on these.
996 The ssl option enables SSL communication with a mailserver
997 supporting Secure Sockets Layer. The sslkey and sslcert options
998 declare key and certificate files for use with SSL.
999 The sslcertck option enables strict checking of SSL server
1000 certificates (and sslcertpath gives trusted certificate
1001 directory). With sslfingerprint, you can specify a finger-
1002 print the server's key is checked against.
1006 'title' : 'Multidrop option help',
1007 'banner': 'Multidrop',
1009 These options are only useful with multidrop mode.
1010 See the manual page for extended discussion.
1014 'title' : 'User list help',
1015 'banner': 'User list',
1017 When you add a user name to the list here,
1018 you initialize an entry telling fetchmail
1019 to poll the site on behalf of the new user.
1021 When you select a username (by double-
1022 clicking it, or by single-clicking to
1023 select and then clicking the Edit button),
1024 you will open a window to configure the
1025 user's options on that site.
1028 class ServerEdit(Frame, MyWidget):
1029 def __init__(self, host, parent):
1030 self.parent = parent
1032 self.subwidgets = {}
1033 for site in parent.configuration.servers:
1034 if site.pollname == host:
1036 if (self.server == None):
1037 self.server = Server()
1038 self.server.pollname = host
1039 self.server.via = None
1040 parent.configuration.servers.append(self.server)
1042 def edit(self, mode, master=None):
1043 Frame.__init__(self, master)
1045 self.master.title('Fetchmail host ' + self.server.pollname);
1046 self.master.iconname('Fetchmail host ' + self.server.pollname);
1047 self.post(Server, 'server')
1048 self.makeWidgets(self.server.pollname, mode)
1049 self.keepalive = [] # Use this to anchor the PhotoImage object
1050 make_icon_window(self, fetchmail_icon)
1053 # self.wait_window()
1057 for username in self.subwidgets.keys():
1058 self.subwidgets[username].destruct()
1059 del self.parent.subwidgets[self.server.pollname]
1060 self.master.destroy()
1063 if ConfirmQuit(self, 'server option editing'):
1067 self.fetch(Server, 'server')
1068 for username in self.subwidgets.keys():
1069 self.subwidgets[username].save()
1072 def defaultPort(self):
1073 proto = self.protocol.get()
1074 # Callback to reset the port number whenever the protocol type changes.
1075 # We used to only reset the port if it had a default (zero) value.
1076 # This turns out to be a bad idea especially in Novice mode -- if
1077 # you set POP3 and then set IMAP, the port invisibly remained 110.
1078 # Now we reset unconditionally on the theory that if you're setting
1079 # a custom port number you should be in expert mode and playing
1080 # close enough attention to notice this...
1081 self.service.set(defaultports[proto])
1082 if not proto in ("POP3", "APOP", "KPOP"): self.uidl.state = DISABLED
1084 def user_edit(self, username, mode):
1085 self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1087 def user_delete(self, username):
1088 if self.subwidgets.has_key(username):
1089 self.subwidgets[username].destruct()
1090 del self.server[username]
1092 def makeWidgets(self, host, mode):
1093 topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1095 leftwin = Frame(self);
1098 if mode != 'novice':
1099 ctlwin = Frame(leftwin, relief=RAISED, bd=5)
1100 Label(ctlwin, text="Run Controls").pack(side=TOP)
1101 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
1102 LabeledEntry(ctlwin, 'True name of ' + host + ':',
1103 self.via, leftwidth).pack(side=TOP, fill=X)
1104 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
1105 self.interval, leftwidth).pack(side=TOP, fill=X)
1106 LabeledEntry(ctlwin, 'Server timeout (seconds):',
1107 self.timeout, leftwidth).pack(side=TOP, fill=X)
1108 Button(ctlwin, text='Help', fg='blue',
1109 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
1112 # Compute the available protocols from the compile-time options
1113 protolist = ['auto']
1114 if 'pop2' in feature_options:
1115 protolist.append("POP2")
1116 if 'pop3' in feature_options:
1117 protolist = protolist + ["POP3", "APOP", "KPOP"]
1118 if 'sdps' in feature_options:
1119 protolist.append("SDPS")
1120 if 'imap' in feature_options:
1121 protolist.append("IMAP")
1122 if 'etrn' in feature_options:
1123 protolist.append("ETRN")
1124 if 'odmr' in feature_options:
1125 protolist.append("ODMR")
1127 protwin = Frame(leftwin, relief=RAISED, bd=5)
1128 Label(protwin, text="Protocol").pack(side=TOP)
1129 ButtonBar(protwin, '',
1130 self.protocol, protolist, 2,
1132 if mode != 'novice':
1133 LabeledEntry(protwin, 'On server TCP/IP service:',
1134 self.service, leftwidth).pack(side=TOP, fill=X)
1136 Checkbutton(protwin,
1137 text="POP3: track `seen' with client-side UIDLs?",
1138 variable=self.uidl).pack(side=TOP)
1139 Button(protwin, text='Probe for supported protocols', fg='blue',
1140 command=self.autoprobe).pack(side=LEFT)
1141 Button(protwin, text='Help', fg='blue',
1142 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
1143 protwin.pack(fill=X)
1145 userwin = Frame(leftwin, relief=RAISED, bd=5)
1146 Label(userwin, text="User entries for " + host).pack(side=TOP)
1147 ListEdit("New user: ",
1148 map(lambda x: x.remote, self.server.users),
1149 lambda u, m=mode, s=self: s.user_edit(u, m),
1150 lambda u, s=self: s.user_delete(u),
1152 userwin.pack(fill=X)
1154 leftwin.pack(side=LEFT, anchor=N, fill=X);
1156 if mode != 'novice':
1157 rightwin = Frame(self);
1159 mdropwin = Frame(rightwin, relief=RAISED, bd=5)
1160 Label(mdropwin, text="Multidrop options").pack(side=TOP)
1161 LabeledEntry(mdropwin, 'Envelope address header:',
1162 self.envelope, '22').pack(side=TOP, fill=X)
1163 LabeledEntry(mdropwin, 'Envelope headers to skip:',
1164 self.envskip, '22').pack(side=TOP, fill=X)
1165 LabeledEntry(mdropwin, 'Name prefix to strip:',
1166 self.qvirtual, '22').pack(side=TOP, fill=X)
1167 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
1168 variable=self.dns).pack(side=TOP)
1169 Label(mdropwin, text="DNS aliases").pack(side=TOP)
1170 ListEdit("New alias: ", self.server.aka, None, None, mdropwin, None)
1171 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
1172 ListEdit("New domain: ",
1173 self.server.localdomains, None, None, mdropwin, multihelp)
1174 mdropwin.pack(fill=X)
1176 if os_type in ('linux', 'freebsd'):
1177 secwin = Frame(rightwin, relief=RAISED, bd=5)
1178 Label(secwin, text="Security").pack(side=TOP)
1179 # Don't actually let users set this. KPOP sets it implicitly
1180 ButtonBar(secwin, 'Authorization mode:',
1181 self.auth, authlist, 2, None).pack(side=TOP)
1182 if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
1183 LabeledEntry(secwin, 'IP range to check before poll:',
1184 self.interface, leftwidth).pack(side=TOP, fill=X)
1185 if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
1186 LabeledEntry(secwin, 'Interface to monitor:',
1187 self.monitor, leftwidth).pack(side=TOP, fill=X)
1188 # Someday this should handle Kerberos 5 too
1189 if 'kerberos' in feature_options:
1190 LabeledEntry(secwin, 'Principal:',
1191 self.principal, '12').pack(side=TOP, fill=X)
1192 # ESMTP authentication
1193 LabeledEntry(secwin, 'ESMTP name:',
1194 self.esmtpname, '12').pack(side=TOP, fill=X)
1195 LabeledEntry(secwin, 'ESMTP password:',
1196 self.esmtppassword, '12').pack(side=TOP, fill=X)
1197 Button(secwin, text='Help', fg='blue',
1198 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
1201 rightwin.pack(side=LEFT, anchor=N);
1203 def autoprobe(self):
1204 # Note: this only handles case (1) near fetchmail.c:1032
1205 # We're assuming people smart enough to set up ssh tunneling
1206 # won't need autoprobing.
1208 realhost = self.server.via
1210 realhost = self.server.pollname
1212 for protocol in ("IMAP","POP3","POP2"):
1213 service = defaultports[protocol]
1214 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1216 sock.connect((realhost, ianaservices[service]))
1217 greetline = sock.recv(1024)
1223 confwin = Toplevel()
1224 if greetline == None:
1225 title = "Autoprobe of " + realhost + " failed"
1227 Fetchmailconf didn't find any mailservers active.
1228 This could mean the host doesn't support any,
1229 or that your Internet connection is down, or
1230 that the host is so slow that the probe timed
1231 out before getting a response.
1235 # OK, now try to recognize potential problems
1237 if protocol == "POP2":
1238 warnings = warnings + """
1239 It appears you have somehow found a mailserver running only POP2.
1240 Congratulations. Have you considered a career in archaeology?
1242 Unfortunately, stock fetchmail binaries don't include POP2 support anymore.
1243 Unless the first line of your fetchmail -V output includes the string "POP2",
1244 you'll have to build it from sources yourself with the configure
1245 switch --enable-POP2.
1249 ### POP3 servers start here
1251 if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
1252 warnings = warnings + """
1253 This appears to be an old version of the UC Davis POP server. These are
1254 dangerously unreliable (among other problems, they may drop your mailbox
1255 on the floor if your connection is interrupted during the session).
1257 It is strongly recommended that you find a better POP3 server. The fetchmail
1258 FAQ includes pointers to good ones.
1261 if string.find(greetline, "comcast.net") > 0:
1262 warnings = warnings + """
1263 The Comcast Maillennium POP3 server only returns the first 80K of a long
1264 message retrieved with TOP. Its response to RETR is normal, so use the
1268 # Steve VanDevender <stevev@efn.org> writes:
1269 # The only system I have seen this happen with is cucipop-1.31
1270 # under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
1271 # 2.x and probably quite a few other systems. It appears to be a
1272 # bug or bad interaction with the SunOS realloc() -- it turns out
1273 # that internally cucipop does allocate a certain data structure in
1274 # multiples of 16, using realloc() to bump it up to the next
1275 # multiple if it needs more.
1277 # The distinctive symptom is that when there are 16 messages in the
1278 # inbox, you can RETR and DELE all 16 messages successfully, but on
1279 # QUIT cucipop returns something like "-ERR Error locking your
1280 # mailbox" and aborts without updating it.
1282 # The cucipop banner looks like:
1284 # +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1286 if string.find(greetline, "Cubic Circle") > 0:
1287 warnings = warnings + """
1288 I see your server is running cucipop. Better make sure the server box
1289 isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
1290 under that version, and doesn't cope with the result gracefully. Newer
1291 SunOS and Solaris machines run cucipop OK.
1293 Also, some versions of cucipop don't assert an exclusive lock on your
1294 mailbox when it's being queried. This means that if you have more than
1295 one fetchmail query running against the same mailbox, bad things can happen.
1297 if string.find(greetline, "David POP3 Server") > 0:
1298 warnings = warnings + """
1299 This POP3 server is badly broken. You should get rid of it -- and the
1300 brain-dead Microsoft operating system it rode in on.
1303 # The greeting line on the server known to be buggy is:
1304 # +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
1306 if string.find(greetline, "FTGate") > 0:
1307 warnings = warnings + """
1308 This POP server has a weird bug; it says OK twice in response to TOP.
1309 Its response to RETR is normal, so use the `fetchall' option.
1312 if string.find(greetline, " geonet.de") > 0:
1313 warnings = warnings + """
1314 You appear to be using geonet. As of late 2002, the TOP command on
1315 geonet's POP3 is broken. Use the fetchall option.
1318 if string.find(greetline, "OpenMail") > 0:
1319 warnings = warnings + """
1320 You appear to be using some version of HP OpenMail. Many versions of
1321 OpenMail do not process the "TOP" command correctly; the symptom is that
1322 only the header and first line of each message is retrieved. To work
1323 around this bug, turn on `fetchall' on all user entries associated with
1327 if string.find(greetline, "Escape character is") > 0:
1328 warnings = warnings + """
1329 Your greeting line looks like it was written by a fetid pile of
1330 camel dung identified to me as `popa3d written by Solar Designer'.
1331 Beware! The UIDL support in this thing is known to be completely broken,
1332 and other things probably are too.
1335 if string.find(greetline, "MercuryP/NLM v1.48") > 0:
1336 warnings = warnings + """
1337 This is not a POP3 server. It has delusions of being one, but after
1338 RETR all messages are automatically marked to be deleted. The only
1339 way to prevent this is to issue an RSET before leaving the server.
1340 Fetchmail does this, but we suspect this is probably broken in lots
1344 if string.find(greetline, "POP-Max") > 0:
1345 warnings = warnings + """
1346 The Mail Max POP3 server screws up on mail with attachments. It
1347 reports the message size with attachments included, but doesn't
1348 download them on a RETR or TOP (this violates the IMAP RFCs). It also
1349 doesn't implement TOP correctly. You should get rid of it -- and the
1350 brain-dead NT server it rode in on.
1353 if string.find(greetline, "POP3 Server Ready") > 0:
1354 warnings = warnings + """
1355 Some server that uses this greeting line has been observed to choke on
1356 TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
1359 if string.find(greetline, "QPOP") > 0:
1360 warnings = warnings + """
1361 This appears to be a version of Eudora qpopper. That's good. Fetchmail
1362 knows all about qpopper. However, be aware that the 2.53 version of
1363 qpopper does something odd that causes fetchmail to hang with a socket
1364 error on very large messages. This is probably not a fetchmail bug, as
1365 it has been observed with fetchpop. The fix is to upgrade to qpopper
1366 3.0beta or a more recent version. Better yet, switch to IMAP.
1369 if string.find(greetline, " sprynet.com") > 0:
1370 warnings = warnings + """
1371 You appear to be using a SpryNet server. In mid-1999 it was reported that
1372 the SpryNet TOP command marks messages seen. Therefore, for proper error
1373 recovery in the event of a line drop, it is strongly recommended that you
1374 turn on `fetchall' on all user entries associated with this server.
1377 if string.find(greetline, "TEMS POP3") > 0:
1378 warnings = warnings + """
1379 Your POP3 server has "TEMS" in its header line. At least one such
1380 server does not process the "TOP" command correctly; the symptom is
1381 that fetchmail hangs when trying to retrieve mail. To work around
1382 this bug, turn on `fetchall' on all user entries associated with this
1386 if string.find(greetline, " spray.se") > 0:
1387 warnings = warnings + """
1388 Your POP3 server has "spray.se" in its header line. In May 2000 at
1389 least one such server did not process the "TOP" command correctly; the
1390 symptom is that messages are treated as headerless. To work around
1391 this bug, turn on `fetchall' on all user entries associated with this
1395 if string.find(greetline, " usa.net") > 0:
1396 warnings = warnings + """
1397 You appear to be using USA.NET's free mail service. Their POP3 servers
1398 (at least as of the 2.2 version in use mid-1998) are quite flaky, but
1399 fetchmail can compensate. They seem to require that fetchall be switched on
1400 (otherwise you won't necessarily see all your mail, not even new mail).
1401 They also botch the TOP command the fetchmail normally uses for retrieval
1402 (it only retrieves about 10 lines rather than the number specified).
1403 Turning on fetchall will disable the use of TOP.
1405 Therefore, it is strongly recommended that you turn on `fetchall' on all
1406 user entries associated with this server.
1409 if string.find(greetline, " Novonyx POP3") > 0:
1410 warnings = warnings + """
1411 Your mailserver is running Novonyx POP3. This server, at least as of
1412 version 2.17, seems to have problems handling and reporting seen bits.
1413 You may have to use the fetchall option.
1416 if string.find(greetline, " IMS POP3") > 0:
1417 warnings = warnings + """
1418 Some servers issuing the greeting line 'IMS POP3' have been known to
1419 do byte-stuffing incorrectly. This means that if a message you receive
1420 has a . (period) at start of line, fetchmail will become confused and
1421 probably wedge itself. (This bug was recorded on IMS POP3 0.86.)
1425 ### IMAP servers start here
1427 if string.find(greetline, "GroupWise") > 0:
1428 warnings = warnings + """
1429 The Novell GroupWise IMAP server would be better named GroupFoolish;
1430 it is (according to the designer of IMAP) unusably broken. Among
1431 other things, it doesn't include a required content length in its
1432 BODY[TEXT] response.<p>
1434 Fetchmail works around this problem, but we strongly recommend voting
1435 with your dollars for a server that isn't brain-dead. If you stick
1436 with code as shoddy as GroupWise seems to be, you will probably pay
1437 for it with other problems.<p>
1440 if string.find(greetline, "Netscape IMAP4rev1 Service 3.6") > 0:
1441 warnings = warnings + """
1442 This server violates the RFC2060 requirement that a BODY[TEXT] fetch should
1443 set the messages's Seen flag. As a result, if you use the keep option the
1444 same messages will be downloaded over and over.
1447 if string.find(greetline, "InterChange") > 0:
1448 warnings = warnings + """
1450 The InterChange IMAP server at release levels below 3.61.08 screws up
1451 on mail with attachments. It doesn't fetch them if you give it a
1452 BODY[TEXT] request, though it does if you request RFC822.TEXT.
1453 According to the IMAP RFCs and their maintainer these should be
1454 equivalent -- and we can't drop the BODY[TEXT] form because M$
1455 Exchange (quite legally under RFC2062) rejectsit. The InterChange
1456 folks claim to have fixed this bug in 3.61.08.
1459 if string.find(greetline, "Imail") > 0:
1460 warnings = warnings + """
1461 We've seen a bug report indicating that this IMAP server (at least as of
1462 version 5.0.7) returns an invalid body size for messages with MIME
1463 attachments; the effect is to drop the attachments on the floor. We
1464 recommend you upgrade to a non-broken IMAP server.
1467 if string.find(greetline, "Domino IMAP4") > 0:
1468 warnings = warnings + """
1469 Your IMAP server appears to be Lotus Domino. This server, at least up
1470 to version 4.6.2a, has a bug in its generation of MIME boundaries (see
1471 the details in the fetchmail FAQ). As a result, even MIME aware MUAs
1472 will see attachments as part of the message text. If your Domino server's
1473 POP3 facility is enabled, we recommend you fall back on it.
1477 ### Checks for protocol variants start here
1479 closebrak = string.find(greetline, ">")
1480 if closebrak > 0 and greetline[closebrak+1] == "\r":
1481 warnings = warnings + """
1482 It looks like you could use APOP on this server and avoid sending it your
1483 password in clear. You should talk to the mailserver administrator about
1487 if string.find(greetline, "IMAP2bis") > 0:
1488 warnings = warnings + """
1489 IMAP2bis servers have a minor problem; they can't peek at messages without
1490 marking them seen. If you take a line hit during the retrieval, the
1491 interrupted message may get left on the server, marked seen.
1493 To work around this, it is recommended that you set the `fetchall'
1494 option on all user entries associated with this server, so any stuck
1495 mail will be retrieved next time around.
1497 To fix this bug, upgrade to an IMAP4 server. The fetchmail FAQ includes
1498 a pointer to an open-source implementation.
1501 if string.find(greetline, "IMAP4rev1") > 0:
1502 warnings = warnings + """
1503 I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
1504 remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
1505 has therefore been extremely well tested with this class of server.
1509 warnings = warnings + """
1510 Fetchmail doesn't know anything special about this server type.
1514 # Display success window with warnings
1515 title = "Autoprobe of " + realhost + " succeeded"
1516 confirm = "The " + protocol + " server said:\n\n" + greetline + warnings
1517 self.protocol.set(protocol)
1518 confwin.title(title)
1519 confwin.iconname(title)
1520 Label(confwin, text=title).pack()
1521 Message(confwin, text=confirm, width=600).pack()
1522 Button(confwin, text='Done',
1523 command=lambda x=confwin: x.destroy(), bd=2).pack()
1526 # User editing stuff
1530 'title' : 'User option help',
1531 'banner': 'User options',
1533 You may use this panel to set options
1534 that may differ between individual
1537 Once you have a user configuration set
1538 up as you like it, you can select `OK' to
1539 store it in the user list maintained in
1540 the site configuration window.
1542 If you wish to discard the changes you have
1543 made to user options, select `Quit'.
1547 'title' : 'Local name help',
1548 'banner': 'Local names',
1550 The local name(s) in a user entry are the
1551 people on the client machine who should
1552 receive mail from the poll described.
1554 Note: if a user entry has more than one
1555 local name, messages will be retrieved
1556 in multidrop mode. This complicates
1557 the configuration issues; see the manual
1558 page section on multidrop mode.
1560 Warning: Be careful with local names
1561 such as foo@bar.com, as that can cause
1562 the mail to be sent to foo@bar.com instead
1563 of sending it to your local system.
1566 class UserEdit(Frame, MyWidget):
1567 def __init__(self, username, parent):
1568 self.parent = parent
1570 for user in parent.server.users:
1571 if user.remote == username:
1573 if self.user == None:
1575 self.user.remote = username
1576 self.user.localnames = [username]
1577 parent.server.users.append(self.user)
1579 def edit(self, mode, master=None):
1580 Frame.__init__(self, master)
1582 self.master.title('Fetchmail user ' + self.user.remote
1583 + ' querying ' + self.parent.server.pollname);
1584 self.master.iconname('Fetchmail user ' + self.user.remote);
1585 self.post(User, 'user')
1586 self.makeWidgets(mode, self.parent.server.pollname)
1587 self.keepalive = [] # Use this to anchor the PhotoImage object
1588 make_icon_window(self, fetchmail_icon)
1591 # self.wait_window()
1595 # Yes, this test can fail -- if you delete the parent window.
1596 if self.parent.subwidgets.has_key(self.user.remote):
1597 del self.parent.subwidgets[self.user.remote]
1598 self.master.destroy()
1601 if ConfirmQuit(self, 'user option editing'):
1606 for x in self.user.localnames: ok = ok + (string.find(x, '@') != -1)
1607 if ok == 0 or Dialog(self,
1608 title = "Really accept an embedded '@' ?",
1609 text = "Local names with an embedded '@', such as in foo@bar "
1610 "might result in your mail being sent to foo@bar.com "
1611 "instead of your local system.\n Are you sure you want "
1612 "a local user name with an '@' in it?",
1613 bitmap = 'question',
1614 strings = ('Yes', 'No'),
1615 default = 1).num == 0:
1616 self.fetch(User, 'user')
1619 def makeWidgets(self, mode, servername):
1620 dispose_window(self,
1621 "User options for " + self.user.remote + " querying " + servername,
1624 if mode != 'novice':
1625 leftwin = Frame(self);
1629 secwin = Frame(leftwin, relief=RAISED, bd=5)
1630 Label(secwin, text="Authentication").pack(side=TOP)
1631 LabeledEntry(secwin, 'Password:',
1632 self.password, '12').pack(side=TOP, fill=X)
1633 secwin.pack(fill=X, anchor=N)
1635 if 'ssl' in feature_options or 'ssl' in dictmembers:
1636 sslwin = Frame(leftwin, relief=RAISED, bd=5)
1637 Checkbutton(sslwin, text="Use SSL?",
1638 variable=self.ssl).pack(side=TOP, fill=X)
1639 LabeledEntry(sslwin, 'SSL key:',
1640 self.sslkey, '14').pack(side=TOP, fill=X)
1641 LabeledEntry(sslwin, 'SSL certificate:',
1642 self.sslcert, '14').pack(side=TOP, fill=X)
1643 Checkbutton(sslwin, text="Check server SSL certificate?",
1644 variable=self.sslcertck).pack(side=TOP, fill=X)
1645 LabeledEntry(sslwin, 'SSL trusted certificate directory:',
1646 self.sslcertpath, '14').pack(side=TOP, fill=X)
1647 LabeledEntry(sslwin, 'SSL key fingerprint:',
1648 self.sslfingerprint, '14').pack(side=TOP, fill=X)
1649 sslwin.pack(fill=X, anchor=N)
1651 names = Frame(leftwin, relief=RAISED, bd=5)
1652 Label(names, text="Local names").pack(side=TOP)
1653 ListEdit("New name: ",
1654 self.user.localnames, None, None, names, localhelp)
1655 names.pack(fill=X, anchor=N)
1657 if mode != 'novice':
1658 targwin = Frame(leftwin, relief=RAISED, bd=5)
1659 Label(targwin, text="Forwarding Options").pack(side=TOP)
1660 Label(targwin, text="Listeners to forward to").pack(side=TOP)
1661 ListEdit("New listener:",
1662 self.user.smtphunt, None, None, targwin, None)
1663 Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
1664 ListEdit("Domains:",
1665 self.user.fetchdomains, None, None, targwin, None)
1666 LabeledEntry(targwin, 'Append to MAIL FROM line:',
1667 self.smtpaddress, '26').pack(side=TOP, fill=X)
1668 LabeledEntry(targwin, 'Set RCPT To address:',
1669 self.smtpname, '26').pack(side=TOP, fill=X)
1670 LabeledEntry(targwin, 'Connection setup command:',
1671 self.preconnect, '26').pack(side=TOP, fill=X)
1672 LabeledEntry(targwin, 'Connection wrapup command:',
1673 self.postconnect, '26').pack(side=TOP, fill=X)
1674 LabeledEntry(targwin, 'Local delivery agent:',
1675 self.mda, '26').pack(side=TOP, fill=X)
1676 LabeledEntry(targwin, 'BSMTP output file:',
1677 self.bsmtp, '26').pack(side=TOP, fill=X)
1678 LabeledEntry(targwin, 'Listener spam-block codes:',
1679 self.antispam, '26').pack(side=TOP, fill=X)
1680 LabeledEntry(targwin, 'Pass-through properties:',
1681 self.properties, '26').pack(side=TOP, fill=X)
1682 Checkbutton(targwin, text="Use LMTP?",
1683 variable=self.lmtp).pack(side=TOP, fill=X)
1684 targwin.pack(fill=X, anchor=N)
1686 if mode != 'novice':
1687 leftwin.pack(side=LEFT, fill=X, anchor=N)
1688 rightwin = Frame(self)
1692 optwin = Frame(rightwin, relief=RAISED, bd=5)
1693 Label(optwin, text="Processing Options").pack(side=TOP)
1694 Checkbutton(optwin, text="Suppress deletion of messages after reading",
1695 variable=self.keep).pack(side=TOP, anchor=W)
1696 Checkbutton(optwin, text="Fetch old messages as well as new",
1697 variable=self.fetchall).pack(side=TOP, anchor=W)
1698 if mode != 'novice':
1699 Checkbutton(optwin, text="Flush seen messages before retrieval",
1700 variable=self.flush).pack(side=TOP, anchor=W)
1701 Checkbutton(optwin, text="Flush oversized messages before retrieval",
1702 variable=self.limitflush).pack(side=TOP, anchor=W)
1703 Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply",
1704 variable=self.rewrite).pack(side=TOP, anchor=W)
1705 Checkbutton(optwin, text="Force CR/LF at end of each line",
1706 variable=self.forcecr).pack(side=TOP, anchor=W)
1707 Checkbutton(optwin, text="Strip CR from end of each line",
1708 variable=self.stripcr).pack(side=TOP, anchor=W)
1709 Checkbutton(optwin, text="Pass 8 bits even though SMTP says 7BIT",
1710 variable=self.pass8bits).pack(side=TOP, anchor=W)
1711 Checkbutton(optwin, text="Undo MIME armoring on header and body",
1712 variable=self.mimedecode).pack(side=TOP, anchor=W)
1713 Checkbutton(optwin, text="Drop Status lines from forwarded messages",
1714 variable=self.dropstatus).pack(side=TOP, anchor=W)
1715 Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
1716 variable=self.dropdelivered).pack(side=TOP, anchor=W)
1719 if mode != 'novice':
1720 limwin = Frame(rightwin, relief=RAISED, bd=5)
1721 Label(limwin, text="Resource Limits").pack(side=TOP)
1722 LabeledEntry(limwin, 'Message size limit:',
1723 self.limit, '30').pack(side=TOP, fill=X)
1724 LabeledEntry(limwin, 'Size warning interval:',
1725 self.warnings, '30').pack(side=TOP, fill=X)
1726 LabeledEntry(limwin, 'Max messages to fetch per poll:',
1727 self.fetchlimit, '30').pack(side=TOP, fill=X)
1728 LabeledEntry(limwin, 'Max message sizes to fetch per transaction:',
1729 self.fetchsizelimit, '30').pack(side=TOP, fill=X)
1730 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1731 LabeledEntry(limwin, 'Use fast UIDL:',
1732 self.fastuidl, '30').pack(side=TOP, fill=X)
1733 LabeledEntry(limwin, 'Max messages to forward per poll:',
1734 self.batchlimit, '30').pack(side=TOP, fill=X)
1735 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1736 LabeledEntry(limwin, 'Interval between expunges:',
1737 self.expunge, '30').pack(side=TOP, fill=X)
1738 Checkbutton(limwin, text="Idle after each poll (IMAP only)",
1739 variable=self.idle).pack(side=TOP, anchor=W)
1742 if self.parent.server.protocol == 'IMAP':
1743 foldwin = Frame(rightwin, relief=RAISED, bd=5)
1744 Label(foldwin, text="Remote folders (IMAP only)").pack(side=TOP)
1745 ListEdit("New folder:", self.user.mailboxes,
1746 None, None, foldwin, None)
1747 foldwin.pack(fill=X, anchor=N)
1749 if mode != 'novice':
1750 rightwin.pack(side=LEFT)
1756 # Top-level window that offers either novice or expert mode
1757 # (but not both at once; it disappears when one is selected).
1760 class Configurator(Frame):
1761 def __init__(self, outfile, master, onexit, parent):
1762 Frame.__init__(self, master)
1763 self.outfile = outfile
1764 self.onexit = onexit
1765 self.parent = parent
1766 self.master.title('fetchmail configurator');
1767 self.master.iconname('fetchmail configurator');
1769 self.keepalive = [] # Use this to anchor the PhotoImage object
1770 make_icon_window(self, fetchmail_icon)
1772 Message(self, text="""
1773 Use `Novice Configuration' for basic fetchmail setup;
1774 with this, you can easily set up a single-drop connection
1775 to one remote mail server.
1776 """, width=600).pack(side=TOP)
1777 Button(self, text='Novice Configuration',
1778 fg='blue', command=self.novice).pack()
1780 Message(self, text="""
1781 Use `Expert Configuration' for advanced fetchmail setup,
1782 including multiple-site or multidrop connections.
1783 """, width=600).pack(side=TOP)
1784 Button(self, text='Expert Configuration',
1785 fg='blue', command=self.expert).pack()
1787 Message(self, text="""
1788 Or you can just select `Quit' to leave the configurator now and
1789 return to the main panel.
1790 """, width=600).pack(side=TOP)
1791 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1792 master.protocol("WM_DELETE_WINDOW", self.leave)
1795 self.master.destroy()
1796 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1799 self.master.destroy()
1800 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1803 self.master.destroy()
1806 # Run a command in a scrolling text widget, displaying its output
1808 class RunWindow(Frame):
1809 def __init__(self, command, master, parent):
1810 Frame.__init__(self, master)
1811 self.master = master
1812 self.master.title('fetchmail run window');
1813 self.master.iconname('fetchmail run window');
1816 text="Running "+command,
1817 bd=2).pack(side=TOP, pady=10)
1818 self.keepalive = [] # Use this to anchor the PhotoImage object
1819 make_icon_window(self, fetchmail_icon)
1821 # This is a scrolling text window
1822 textframe = Frame(self)
1823 scroll = Scrollbar(textframe)
1824 self.textwidget = Text(textframe, setgrid=TRUE)
1825 textframe.pack(side=TOP, expand=YES, fill=BOTH)
1826 self.textwidget.config(yscrollcommand=scroll.set)
1827 self.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
1828 scroll.config(command=self.textwidget.yview)
1829 scroll.pack(side=RIGHT, fill=BOTH)
1830 textframe.pack(side=TOP)
1832 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1834 self.update() # Draw widget before executing fetchmail
1836 # Always look for a runnable command in the directory we're running in
1837 # first. This avoids some obscure version-skew errors that can occur
1838 # if you pick up an old fetchmail from the standard system locations.
1839 os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"]
1840 child_stdout = os.popen(command + " 2>&1 </dev/null", "r")
1842 ch = child_stdout.read(1)
1845 self.textwidget.insert(END, ch)
1846 self.textwidget.insert(END, "Done.")
1847 self.textwidget.see(END);
1850 self.master.destroy()
1852 # Here's where we choose either configuration or launching
1854 class MainWindow(Frame):
1855 def __init__(self, outfile, master=None):
1856 Frame.__init__(self, master)
1857 self.outfile = outfile
1858 self.master.title('fetchmail launcher');
1859 self.master.iconname('fetchmail launcher');
1862 text='Fetchmailconf ' + version,
1863 bd=2).pack(side=TOP, pady=10)
1864 self.keepalive = [] # Use this to anchor the PhotoImage object
1865 make_icon_window(self, fetchmail_icon)
1868 ## Test icon display with the following:
1869 # icon_image = PhotoImage(data=fetchmail_icon)
1870 # Label(self, image=icon_image).pack(side=TOP, pady=10)
1871 # self.keepalive.append(icon_image)
1873 Message(self, text="""
1874 Use `Configure fetchmail' to tell fetchmail about the remote
1875 servers it should poll (the host name, your username there,
1876 whether to use POP or IMAP, and so forth).
1877 """, width=600).pack(side=TOP)
1878 self.configbutton = Button(self, text='Configure fetchmail',
1879 fg='blue', command=self.configure)
1880 self.configbutton.pack()
1882 Message(self, text="""
1883 Use `Run fetchmail' to run fetchmail with debugging enabled.
1884 This is a good way to test out a new configuration.
1885 """, width=600).pack(side=TOP)
1886 Button(self, text='Run fetchmail',fg='blue', command=self.test).pack()
1888 Message(self, text="""
1889 Use `Run fetchmail' to run fetchmail in foreground.
1890 Progress messages will be shown, but not debug messages.
1891 """, width=600).pack(side=TOP)
1892 Button(self, text='Run fetchmail', fg='blue', command=self.run).pack()
1894 Message(self, text="""
1895 Or you can just select `Quit' to exit the launcher now.
1896 """, width=600).pack(side=TOP)
1897 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1899 def configure(self):
1900 self.configbutton.configure(state=DISABLED)
1901 Configurator(self.outfile, Toplevel(),
1902 lambda self=self: self.configbutton.configure(state=NORMAL),
1905 cmd = "fetchmail -N -d0 --nosyslog -v"
1907 cmd = cmd + " -f " + rcfile
1908 RunWindow(cmd, Toplevel(), self)
1911 cmd = "fetchmail -N -d0"
1913 cmd = cmd + " -f " + rcfile
1914 RunWindow(cmd, Toplevel(), self)
1919 # Functions for turning a dictionary into an instantiated object tree.
1921 def intersect(list1, list2):
1922 # Compute set intersection of lists
1929 def setdiff(list1, list2):
1930 # Compute set difference of lists
1937 def copy_instance(toclass, fromdict):
1938 # Initialize a class object of given type from a conformant dictionary.
1939 for fld in fromdict.keys():
1940 if not fld in dictmembers:
1941 dictmembers.append(fld)
1942 # The `optional' fields are the ones we can ignore for purposes of
1943 # conformability checking; they'll still get copied if they are
1944 # present in the dictionary.
1945 optional = ('interface', 'monitor',
1946 'esmtpname', 'esmtppassword',
1947 'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
1948 'sslcertpath', 'sslfingerprint', 'showdots')
1949 class_sig = setdiff(toclass.__dict__.keys(), optional)
1951 dict_keys = setdiff(fromdict.keys(), optional)
1953 common = intersect(class_sig, dict_keys)
1954 if 'typemap' in class_sig:
1955 class_sig.remove('typemap')
1956 if tuple(class_sig) != tuple(dict_keys):
1957 print "Fields don't match what fetchmailconf expected:"
1958 # print "Class signature: " + `class_sig`
1959 # print "Dictionary keys: " + `dict_keys`
1960 diff = setdiff(class_sig, common)
1962 print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
1963 diff = setdiff(dict_keys, common)
1965 print "Not matched in dictionary keys: " + `diff`
1968 for x in fromdict.keys():
1969 setattr(toclass, x, fromdict[x])
1972 # And this is the main sequence. How it works:
1974 # First, call `fetchmail --configdump' and trap the output in a tempfile.
1975 # This should fill it with a Python initializer for a variable `fetchmailrc'.
1976 # Run execfile on the file to pull fetchmailrc into Python global space.
1977 # You don't want static data, though; you want, instead, a tree of objects
1978 # with the same data members and added appropriate methods.
1980 # This is what the copy_instance function() is for. It tries to copy a
1981 # dictionary field by field into a class, aborting if the class and dictionary
1982 # have different data members (except for any typemap member in the class;
1983 # that one is strictly for use by the MyWidget supperclass).
1985 # Once the object tree is set up, require user to choose novice or expert
1986 # mode and instantiate an edit object for the configuration. Class methods
1987 # will take it all from there.
1989 # Options (not documented because they're for fetchmailconf debuggers only):
1990 # -d: Read the configuration and dump it to stdout before editing. Dump
1991 # the edited result to stdout as well.
1992 # -f: specify the run control file to read.
1994 if __name__ == '__main__':
1996 if not os.environ.has_key("DISPLAY"):
1997 print "fetchmailconf must be run under X"
2000 fetchmail_icon = """
2001 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
2002 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
2003 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
2004 CJR7Y/+UMdbOxnNrY97Ove/Wvd7GrZyEa961jL2Ua9alc86ca7WEUntSKcaMSqVjGNZ7GGM5CNa1
2005 jPfOnN6tc3taMffeve/WtWtaQv/OjGtSMYRzWv/erda1hM6te7WUY62MWs61jP/vzv/ntda9jL2l
2006 czEhAO/n1oyEc//elDEpGEo5EOfexpyUe+/epefevffvxnNrQpyUStbWzsbGvZyclN7ezmNjWv//
2007 5/f33qWllNbWve/vzv//1ufnve/vvf//xvf3vefnrf//taWlc0pKMf//pbW1Y///jKWlWq2tWsbG
2008 Y///c97eUvf3Ut7nc+/3a87We8bOjOfv1u/37/f//621tb3Gxtbn52Nra87n53uUlJTv/6W9xuf3
2009 /8bW3iExOXu11tbv/5TW/4TO/63e/zmt/1KUxlK1/2u9/wCM/73GzrXG1gBKjACE/87e72NzhCkx
2010 OaXO92OMtUql/xCE/wApUtbe57W9xnN7hHut52Ot/xBSnABKnABavQB7/2ul7zF71gBr77XO73Oc
2011 1lqc9yFSlBApSimE/wAYOQApY0J7zlKM5wAxhABS1gBj/6W95wAhWgA5nAAYSgBS7wBS/wBK9wAp
2012 jABC5wBK/wApnABC/wApxgAhtYSMtQAQYwAp/3OE74SMxgAYxlpjvWNr70pS/wgQ3sbGzs7O1qWl
2013 3qWl70pKe0JC/yEhlCkp/wgI/wAAEAAAIQAAKQAAOQAASgAAUgAAYwAAawAAlAAAnAAApQAArQAA
2014 zgAA1gAA5wAA9wAA/0pC/xgQ52Na9ykhe4R7zikhYxgQSjEpQgAAACwAAAAAPAAoAAAI/wABCBxI
2015 sKDBgwgTKiRIYKHDhxARIvgXsaLFhGgEUBSYoKPHjyBDihxJkuS/kwNLqlzJcuTJjQBaypxpEiVH
2016 mjhxvkyZs2fLnTd9ehxAtKjRo0ZrwhTasUsENhYHKOUpk1E3j11mxCBiQVLEBlJd2owp9iVRjwUs
2017 zMCQ5IcLD4saPVxjIKxIoGTvvqSoyFEFGTBeqEhyxAoSFR/USGKVcEGBAwDshsSr1OYTEyhQpJiS
2018 ZcoUKWOQtJDRJFSaggzUGBgoGSTlsjahlPCRIkWVKT16THHRIoqIISBIEUgAYIGBhgRbf3ytFygU
2019 FZp9UDmxQkkMCRwyZKDBQy4aApABhP8XqNwj88l7BVpQYZtF5iArWgwAgGZBq24HU7OeGhQ90PVA
2020 aKZZCiiUMJ9ArSTEwGqR8ZeXfzbV0MIIMQTBwoUdxDDfAm8sZFyDZVEF4UYSKBEBD0+k6IEFPMxH
2021 3FzldXSea+kBgANJSOWIlIMhXZXAXv+c1WM3PuJEpH8iuhbAkv+MdENPRHaTRkdF/jiWSKCAwlKW
2022 VbbkY5Q0LgUSKExgoYBKCjCxARpdltQNKHaUoYAddnR53lVRnJLKBWh4RIEGCZx5FSOv1OLNDUVe
2023 deZHaWiZAB35fIOGNtbEUeV5oGAByzPOrBPFGt3kwEgxITACSg5oLGGLMg60oQAjaNz/oAAcN4Ai
2024 a0c3kHFDK3jYsw4g9sRzBgPLXdkRrBrQ8gsWQUxCCRZX9IJNBQ1s8IgCdeBCzBYN6IBIN2TUsQYd
2025 dXhDBxdzlAHOHHKEcocZdWwDjx8MTCmjsR2FMAstw1RyiSzHqPLALaOwk8QmzCzDCSi0xJKMMk4E
2026 Yw8389iTDT32GAKOPf7YY0Aa9tATyD3w/EGsefgmgEYUtPiChLKWQDMBJtEUgYkzH2RiTgGfTMCI
2027 Mlu0Yc85hNiDziH2tMqOGL72QY47gshLb7Fi4roELcjoQIsxWpDwQyfS2OCJMkLI4YUmyhgxSTVg
2028 CP2FHPZ80UDcieBjStNPD5LPOyZT/y0iHGiMwswexDSzRiRq6KIMJBc4M8skwKAyChia2KPH3P24
2029 YU8/lFhOTj152OPOHuXMU4g48vCRiN/9rZGLMdS4csUu1JzDgxuipOMDHMKsAwEnq/ByzTrrZMNO
2030 OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
2031 xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
2032 gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
2034 # The base64 data in the string above was generated by the following procedure:
2037 # print base64.encodestring(open("fetchmail.gif", "rb").read())
2041 (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV")
2042 dump = rcfile = None;
2043 for (switch, val) in options:
2044 if (switch == '-d'):
2046 elif (switch == '-f'):
2048 elif (switch == '-h'):
2050 Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|-V}
2051 -d - dump configuration (for debugging)
2052 -f fmrc - read alternate fetchmailrc file
2053 -h - print this help text and quit
2054 -V - print fetchmailconf version and quit
2057 elif (switch == '-V'):
2058 print "fetchmailconf %s" % version
2061 # Get client host's FQDN
2062 hostname = socket.gethostbyaddr(socket.gethostname())[0]
2065 ConfigurationDefaults = Configuration()
2066 ServerDefaults = Server()
2067 UserDefaults = User()
2069 # Read the existing configuration. We set the umask to 077 to make sure
2070 # that group & other read/write permissions are shut off -- we wouldn't
2071 # want crackers to snoop password information out of the tempfile.
2072 tmpfile = tempfile.mktemp()
2074 cmd = "umask 077 && fetchmail </dev/null -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2076 cmd = "umask 077 && fetchmail </dev/null --configdump --nosyslog >" + tmpfile
2081 print "`" + cmd + "' run failure, status " + `s`
2084 print "Unknown error while running fetchmail --configdump"
2091 print "Can't read configuration output of fetchmail --configdump."
2097 # The tricky part -- initializing objects from the configuration global
2098 # `Configuration' is the top level of the object tree we're going to mung.
2099 # The dictmembers list is used to track the set of fields the dictionary
2100 # contains; in particular, we can use it to tell whether things like the
2101 # monitor, interface, ssl, sslkey, or sslcert fields are present.
2103 Fetchmailrc = Configuration()
2104 copy_instance(Fetchmailrc, fetchmailrc)
2105 Fetchmailrc.servers = [];
2106 for server in fetchmailrc['servers']:
2108 copy_instance(Newsite, server)
2109 Fetchmailrc.servers.append(Newsite)
2111 for user in server['users']:
2113 copy_instance(Newuser, user)
2114 Newsite.users.append(Newuser)
2116 # We may want to display the configuration and quit
2118 print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2120 # The theory here is that -f alone sets the rcfile location,
2121 # but -d and -f together mean the new configuration should go to stdout.
2122 if not rcfile and not dump:
2123 rcfile = os.environ["HOME"] + "/.fetchmailrc"
2125 # OK, now run the configuration edit
2126 root = MainWindow(rcfile)
2129 # The following sets edit modes for GNU EMACS