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
8 version = "1.52 $Revision$"
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 = 4 # Do fast uidl 3 out of 4 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 poll interval in seconds.
670 If 0, fetchmail will run in the foreground once when started.
671 If > 0, fetchmail will run in the background and start a new poll
672 cycle after the interval has elapsed.
674 6. Options to fetch old messages as well as new, or to suppress
675 deletion of fetched message.
677 The novice-configuration code will assume that you want to forward mail
678 to a local sendmail listener with no special options.
681 configure_expert_help = {
682 'title' : 'Fetchmail expert configurator help',
683 'banner': 'Expert configurator help',
685 In the `Expert Configurator Controls' panel, you can:
687 Press `Save' to save the new fetchmail configuration you have edited.
688 Press `Quit' to exit without saving.
689 Press `Help' to bring up this help message.
691 In the `Run Controls' panel, you can set the following options that
692 control how fetchmail runs:
695 Number of seconds to wait between polls in the background.
696 If zero, fetchmail will run in foreground.
699 If empty, emit progress and error messages to stderr.
700 Otherwise this gives the name of the files to write to.
701 This field is ignored if the "Log to syslog?" option is on.
704 If empty, store seen-message IDs in .fetchids under user's home
705 directory. If nonempty, use given file name.
708 Who to send multidrop mail to as a last resort if no address can
709 be matched. Normally empty; in this case, fetchmail treats the
710 invoking user as the address of last resort unless that user is
711 root. If that user is root, fetchmail sends to `postmaster'.
714 If this option is on (the default) error mail goes to the sender.
715 Otherwise it goes to the postmaster.
718 If this option is on, spam bounces are sent to the sender or
719 postmaster (depending on the "Bounces to sender?" option. Otherwise,
720 spam bounces are not sent (the default).
723 If false (the default) fetchmail generates a Received line into
724 each message and generates a HELO from the machine it is running on.
725 If true, fetchmail generates no Received line and HELOs as if it were
728 In the `Remote Mail Configurations' panel, you can:
730 1. Enter the name of a new remote mail server you want fetchmail to query.
732 To do this, simply enter a label for the poll configuration in the
733 `New Server:' box. The label should be a DNS name of the server (unless
734 you are using ssh or some other tunneling method and will fill in the `via'
735 option on the site configuration screen).
737 2. Change the configuration of an existing site.
739 To do this, find the site's label in the listbox and double-click it.
740 This will take you to a site configuration dialogue.
744 class ConfigurationEdit(Frame, MyWidget):
745 def __init__(self, configuration, outfile, master, onexit):
747 self.configuration = configuration
748 self.outfile = outfile
749 self.container = master
751 ConfigurationEdit.mode_to_help = {
752 'novice':configure_novice_help, 'expert':configure_expert_help
755 def server_edit(self, sitename):
756 self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
758 def server_delete(self, sitename):
760 for user in self.subwidgets.keys():
762 del self.configuration[sitename]
766 def edit(self, mode):
768 Frame.__init__(self, self.container)
769 self.master.title('fetchmail ' + self.mode + ' configurator');
770 self.master.iconname('fetchmail ' + self.mode + ' configurator');
771 self.master.protocol('WM_DELETE_WINDOW', self.nosave)
772 self.keepalive = [] # Use this to anchor the PhotoImage object
773 make_icon_window(self, fetchmail_icon)
775 self.post(Configuration, 'configuration')
778 'Configurator ' + self.mode + ' Controls',
779 ConfigurationEdit.mode_to_help[self.mode],
782 gf = Frame(self, relief=RAISED, bd = 5)
784 text='Fetchmail Run Controls',
785 bd=2).pack(side=TOP, pady=10)
790 if self.mode != 'novice':
792 log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
793 log.pack(side=RIGHT, anchor=E)
795 # Set the poll interval
796 de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
797 de.pack(side=RIGHT, anchor=E)
802 if self.mode != 'novice':
805 {'text':'Bounces to sender?',
806 'variable':self.bouncemail,
807 'relief':GROOVE}).pack(side=LEFT, anchor=W)
812 {'text':'send spam bounces?',
813 'variable':self.spambounce,
814 'relief':GROOVE}).pack(side=LEFT, anchor=W)
819 {'text':'Log to syslog?',
820 'variable':self.syslog,
821 'relief':GROOVE}).pack(side=LEFT, anchor=W)
822 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
823 log.pack(side=RIGHT, anchor=E)
827 {'text':'Invisible mode?',
828 'variable':self.invisible,
829 'relief':GROOVE}).pack(side=LEFT, anchor=W)
831 log = LabeledEntry(gf, ' Idfile:', self.idfile, '14')
832 log.pack(side=RIGHT, anchor=E)
836 # Expert mode allows us to edit multiple sites
837 lf = Frame(self, relief=RAISED, bd=5)
839 text='Remote Mail Server Configurations',
840 bd=2).pack(side=TOP, pady=10)
841 ListEdit('New Server:',
842 map(lambda x: x.pollname, self.configuration.servers),
843 lambda site, self=self: self.server_edit(site),
844 lambda site, self=self: self.server_delete(site),
849 for sitename in self.subwidgets.keys():
850 self.subwidgets[sitename].destruct()
851 self.master.destroy()
855 if ConfirmQuit(self, self.mode + " configuration editor"):
859 for sitename in self.subwidgets.keys():
860 self.subwidgets[sitename].save()
861 self.fetch(Configuration, 'configuration')
865 elif not os.path.isfile(self.outfile) or Dialog(self,
866 title = 'Overwrite existing run control file?',
867 text = 'Really overwrite existing run control file?',
869 strings = ('Yes', 'No'),
870 default = 1).num == 0:
872 os.rename(self.outfile, self.outfile + "~")
873 # Pre-1.5.2 compatibility...
876 oldumask = os.umask(077)
877 fm = open(self.outfile, 'w')
882 os.chmod(self.outfile, 0600)
883 fm.write("# Configuration created %s by fetchmailconf %s\n" % (time.ctime(time.time()), version))
884 fm.write(`self.configuration`)
890 # Server editing stuff.
893 'title' : 'Remote site help',
894 'banner': 'Remote sites',
896 When you add a site name to the list here,
897 you initialize an entry telling fetchmail
898 how to poll a new site.
900 When you select a sitename (by double-
901 clicking it, or by single-clicking to
902 select and then clicking the Edit button),
903 you will open a window to configure that
908 'title' : 'Server options help',
909 'banner': 'Server Options',
911 The server options screen controls fetchmail
912 options that apply to one of your mailservers.
914 Once you have a mailserver configuration set
915 up as you like it, you can select `OK' to
916 store it in the server list maintained in
917 the main configuration window.
919 If you wish to discard changes to a server
920 configuration, select `Quit'.
924 'title' : 'Run Control help',
925 'banner': 'Run Controls',
927 If the `Poll normally' checkbox is on, the host is polled as part of
928 the normal operation of fetchmail when it is run with no arguments.
929 If it is off, fetchmail will only query this host when it is given as
930 a command-line argument.
932 The `True name of server' box should specify the actual DNS name
933 to query. By default this is the same as the poll name.
935 Normally each host described in the file is queried once each
936 poll cycle. If `Cycles to skip between polls' is greater than 0,
937 that's the number of poll cycles that are skipped between the
938 times this post is actually polled.
940 The `Server timeout' is the number of seconds fetchmail will wait
941 for a reply from the mailserver before concluding it is hung and
946 'title' : 'Protocol and Port help',
947 'banner': 'Protocol and Port',
949 These options control the remote-mail protocol
950 and TCP/IP service port used to query this
953 If you click the `Probe for supported protocols'
954 button, fetchmail will try to find you the most
955 capable server on the selected host (this will
956 only work if you're conncted to the Internet).
957 The probe only checks for ordinary IMAP and POP
958 protocols; fortunately these are the most
959 frequently supported.
961 The `Protocol' button bar offers you a choice of
962 all the different protocols available. The `auto'
963 protocol is the default mode; it probes the host
964 ports for POP3 and IMAP to see if either is
967 Normally the TCP/IP service port to use is
968 dictated by the protocol choice. The `Service'
969 field (only present in expert mode) lets you
970 set a non-standard service (port).
974 'title' : 'Security option help',
975 'banner': 'Security',
977 The 'authorization mode' allows you to choose the
978 mode that fetchmail uses to log in to your server. You
979 can usually leave this at 'any', but you will have to pick
980 'NTLM' and 'MSN' manually for the nonce.
982 The 'interface' option allows you to specify a range
983 of IP addresses to monitor for activity. If these
984 addresses are not active, fetchmail will not poll.
985 Specifying this may protect you from a spoofing attack
986 if your client machine has more than one IP gateway
987 address and some of the gateways are to insecure nets.
989 The `monitor' option, if given, specifies the only
990 device through which fetchmail is permitted to connect
991 to servers. This option may be used to prevent
992 fetchmail from triggering an expensive dial-out if the
993 interface is not already active.
995 The `interface' and `monitor' options are available
996 only for Linux and freeBSD systems. See the fetchmail
997 manual page for details on these.
999 The ssl option enables SSL communication with a mailserver
1000 supporting Secure Sockets Layer. The sslkey and sslcert options
1001 declare key and certificate files for use with SSL.
1002 The sslcertck option enables strict checking of SSL server
1003 certificates (and sslcertpath gives trusted certificate
1004 directory). With sslfingerprint, you can specify a finger-
1005 print the server's key is checked against.
1009 'title' : 'Multidrop option help',
1010 'banner': 'Multidrop',
1012 These options are only useful with multidrop mode.
1013 See the manual page for extended discussion.
1017 'title' : 'User list help',
1018 'banner': 'User list',
1020 When you add a user name to the list here,
1021 you initialize an entry telling fetchmail
1022 to poll the site on behalf of the new user.
1024 When you select a username (by double-
1025 clicking it, or by single-clicking to
1026 select and then clicking the Edit button),
1027 you will open a window to configure the
1028 user's options on that site.
1031 class ServerEdit(Frame, MyWidget):
1032 def __init__(self, host, parent):
1033 self.parent = parent
1035 self.subwidgets = {}
1036 for site in parent.configuration.servers:
1037 if site.pollname == host:
1039 if (self.server == None):
1040 self.server = Server()
1041 self.server.pollname = host
1042 self.server.via = None
1043 parent.configuration.servers.append(self.server)
1045 def edit(self, mode, master=None):
1046 Frame.__init__(self, master)
1048 self.master.title('Fetchmail host ' + self.server.pollname);
1049 self.master.iconname('Fetchmail host ' + self.server.pollname);
1050 self.post(Server, 'server')
1051 self.makeWidgets(self.server.pollname, mode)
1052 self.keepalive = [] # Use this to anchor the PhotoImage object
1053 make_icon_window(self, fetchmail_icon)
1056 # self.wait_window()
1060 for username in self.subwidgets.keys():
1061 self.subwidgets[username].destruct()
1062 del self.parent.subwidgets[self.server.pollname]
1063 self.master.destroy()
1066 if ConfirmQuit(self, 'server option editing'):
1070 self.fetch(Server, 'server')
1071 for username in self.subwidgets.keys():
1072 self.subwidgets[username].save()
1075 def defaultPort(self):
1076 proto = self.protocol.get()
1077 # Callback to reset the port number whenever the protocol type changes.
1078 # We used to only reset the port if it had a default (zero) value.
1079 # This turns out to be a bad idea especially in Novice mode -- if
1080 # you set POP3 and then set IMAP, the port invisibly remained 110.
1081 # Now we reset unconditionally on the theory that if you're setting
1082 # a custom port number you should be in expert mode and playing
1083 # close enough attention to notice this...
1084 self.service.set(defaultports[proto])
1085 if not proto in ("POP3", "APOP", "KPOP"): self.uidl.state = DISABLED
1087 def user_edit(self, username, mode):
1088 self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1090 def user_delete(self, username):
1091 if self.subwidgets.has_key(username):
1092 self.subwidgets[username].destruct()
1093 del self.server[username]
1095 def makeWidgets(self, host, mode):
1096 topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1098 leftwin = Frame(self);
1101 if mode != 'novice':
1102 ctlwin = Frame(leftwin, relief=RAISED, bd=5)
1103 Label(ctlwin, text="Run Controls").pack(side=TOP)
1104 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
1105 LabeledEntry(ctlwin, 'True name of ' + host + ':',
1106 self.via, leftwidth).pack(side=TOP, fill=X)
1107 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
1108 self.interval, leftwidth).pack(side=TOP, fill=X)
1109 LabeledEntry(ctlwin, 'Server timeout (seconds):',
1110 self.timeout, leftwidth).pack(side=TOP, fill=X)
1111 Button(ctlwin, text='Help', fg='blue',
1112 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
1115 # Compute the available protocols from the compile-time options
1116 protolist = ['auto']
1117 if 'pop2' in feature_options:
1118 protolist.append("POP2")
1119 if 'pop3' in feature_options:
1120 protolist = protolist + ["POP3", "APOP", "KPOP"]
1121 if 'sdps' in feature_options:
1122 protolist.append("SDPS")
1123 if 'imap' in feature_options:
1124 protolist.append("IMAP")
1125 if 'etrn' in feature_options:
1126 protolist.append("ETRN")
1127 if 'odmr' in feature_options:
1128 protolist.append("ODMR")
1130 protwin = Frame(leftwin, relief=RAISED, bd=5)
1131 Label(protwin, text="Protocol").pack(side=TOP)
1132 ButtonBar(protwin, '',
1133 self.protocol, protolist, 2,
1135 if mode != 'novice':
1136 LabeledEntry(protwin, 'On server TCP/IP service:',
1137 self.service, leftwidth).pack(side=TOP, fill=X)
1139 Checkbutton(protwin,
1140 text="POP3: track `seen' with client-side UIDLs?",
1141 variable=self.uidl).pack(side=TOP)
1142 Button(protwin, text='Probe for supported protocols', fg='blue',
1143 command=self.autoprobe).pack(side=LEFT)
1144 Button(protwin, text='Help', fg='blue',
1145 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
1146 protwin.pack(fill=X)
1148 userwin = Frame(leftwin, relief=RAISED, bd=5)
1149 Label(userwin, text="User entries for " + host).pack(side=TOP)
1150 ListEdit("New user: ",
1151 map(lambda x: x.remote, self.server.users),
1152 lambda u, m=mode, s=self: s.user_edit(u, m),
1153 lambda u, s=self: s.user_delete(u),
1155 userwin.pack(fill=X)
1157 leftwin.pack(side=LEFT, anchor=N, fill=X);
1159 if mode != 'novice':
1160 rightwin = Frame(self);
1162 mdropwin = Frame(rightwin, relief=RAISED, bd=5)
1163 Label(mdropwin, text="Multidrop options").pack(side=TOP)
1164 LabeledEntry(mdropwin, 'Envelope address header:',
1165 self.envelope, '22').pack(side=TOP, fill=X)
1166 LabeledEntry(mdropwin, 'Envelope headers to skip:',
1167 self.envskip, '22').pack(side=TOP, fill=X)
1168 LabeledEntry(mdropwin, 'Name prefix to strip:',
1169 self.qvirtual, '22').pack(side=TOP, fill=X)
1170 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
1171 variable=self.dns).pack(side=TOP)
1172 Label(mdropwin, text="DNS aliases").pack(side=TOP)
1173 ListEdit("New alias: ", self.server.aka, None, None, mdropwin, None)
1174 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
1175 ListEdit("New domain: ",
1176 self.server.localdomains, None, None, mdropwin, multihelp)
1177 mdropwin.pack(fill=X)
1179 if os_type in ('linux', 'freebsd'):
1180 secwin = Frame(rightwin, relief=RAISED, bd=5)
1181 Label(secwin, text="Security").pack(side=TOP)
1182 # Don't actually let users set this. KPOP sets it implicitly
1183 ButtonBar(secwin, 'Authorization mode:',
1184 self.auth, authlist, 2, None).pack(side=TOP)
1185 if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
1186 LabeledEntry(secwin, 'IP range to check before poll:',
1187 self.interface, leftwidth).pack(side=TOP, fill=X)
1188 if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
1189 LabeledEntry(secwin, 'Interface to monitor:',
1190 self.monitor, leftwidth).pack(side=TOP, fill=X)
1191 # Someday this should handle Kerberos 5 too
1192 if 'kerberos' in feature_options:
1193 LabeledEntry(secwin, 'Principal:',
1194 self.principal, '12').pack(side=TOP, fill=X)
1195 # ESMTP authentication
1196 LabeledEntry(secwin, 'ESMTP name:',
1197 self.esmtpname, '12').pack(side=TOP, fill=X)
1198 LabeledEntry(secwin, 'ESMTP password:',
1199 self.esmtppassword, '12').pack(side=TOP, fill=X)
1200 Button(secwin, text='Help', fg='blue',
1201 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
1204 rightwin.pack(side=LEFT, anchor=N);
1206 def autoprobe(self):
1207 # Note: this only handles case (1) near fetchmail.c:1032
1208 # We're assuming people smart enough to set up ssh tunneling
1209 # won't need autoprobing.
1211 realhost = self.server.via
1213 realhost = self.server.pollname
1215 for protocol in ("IMAP","POP3","POP2"):
1216 service = defaultports[protocol]
1217 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1219 sock.connect((realhost, ianaservices[service]))
1220 greetline = sock.recv(1024)
1226 confwin = Toplevel()
1227 if greetline == None:
1228 title = "Autoprobe of " + realhost + " failed"
1230 Fetchmailconf didn't find any mailservers active.
1231 This could mean the host doesn't support any,
1232 or that your Internet connection is down, or
1233 that the host is so slow that the probe timed
1234 out before getting a response.
1238 # OK, now try to recognize potential problems
1240 if protocol == "POP2":
1241 warnings = warnings + """
1242 It appears you have somehow found a mailserver running only POP2.
1243 Congratulations. Have you considered a career in archaeology?
1245 Unfortunately, stock fetchmail binaries don't include POP2 support anymore.
1246 Unless the first line of your fetchmail -V output includes the string "POP2",
1247 you'll have to build it from sources yourself with the configure
1248 switch --enable-POP2.
1252 ### POP3 servers start here
1254 if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
1255 warnings = warnings + """
1256 This appears to be an old version of the UC Davis POP server. These are
1257 dangerously unreliable (among other problems, they may drop your mailbox
1258 on the floor if your connection is interrupted during the session).
1260 It is strongly recommended that you find a better POP3 server. The fetchmail
1261 FAQ includes pointers to good ones.
1264 if string.find(greetline, "comcast.net") > 0:
1265 warnings = warnings + """
1266 The Comcast Maillennium POP3 server only returns the first 80K of a long
1267 message retrieved with TOP. Its response to RETR is normal, so use the
1271 # Steve VanDevender <stevev@efn.org> writes:
1272 # The only system I have seen this happen with is cucipop-1.31
1273 # under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
1274 # 2.x and probably quite a few other systems. It appears to be a
1275 # bug or bad interaction with the SunOS realloc() -- it turns out
1276 # that internally cucipop does allocate a certain data structure in
1277 # multiples of 16, using realloc() to bump it up to the next
1278 # multiple if it needs more.
1280 # The distinctive symptom is that when there are 16 messages in the
1281 # inbox, you can RETR and DELE all 16 messages successfully, but on
1282 # QUIT cucipop returns something like "-ERR Error locking your
1283 # mailbox" and aborts without updating it.
1285 # The cucipop banner looks like:
1287 # +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1289 if string.find(greetline, "Cubic Circle") > 0:
1290 warnings = warnings + """
1291 I see your server is running cucipop. Better make sure the server box
1292 isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
1293 under that version, and doesn't cope with the result gracefully. Newer
1294 SunOS and Solaris machines run cucipop OK.
1296 Also, some versions of cucipop don't assert an exclusive lock on your
1297 mailbox when it's being queried. This means that if you have more than
1298 one fetchmail query running against the same mailbox, bad things can happen.
1300 if string.find(greetline, "David POP3 Server") > 0:
1301 warnings = warnings + """
1302 This POP3 server is badly broken. You should get rid of it -- and the
1303 brain-dead Microsoft operating system it rode in on.
1306 # The greeting line on the server known to be buggy is:
1307 # +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
1309 if string.find(greetline, "FTGate") > 0:
1310 warnings = warnings + """
1311 This POP server has a weird bug; it says OK twice in response to TOP.
1312 Its response to RETR is normal, so use the `fetchall' option.
1315 if string.find(greetline, " geonet.de") > 0:
1316 warnings = warnings + """
1317 You appear to be using geonet. As of late 2002, the TOP command on
1318 geonet's POP3 is broken. Use the fetchall option.
1321 if string.find(greetline, "OpenMail") > 0:
1322 warnings = warnings + """
1323 You appear to be using some version of HP OpenMail. Many versions of
1324 OpenMail do not process the "TOP" command correctly; the symptom is that
1325 only the header and first line of each message is retrieved. To work
1326 around this bug, turn on `fetchall' on all user entries associated with
1330 if string.find(greetline, "Escape character is") > 0:
1331 warnings = warnings + """
1332 Your greeting line looks like it was written by a fetid pile of
1333 camel dung identified to me as `popa3d written by Solar Designer'.
1334 Beware! The UIDL support in this thing is known to be completely broken,
1335 and other things probably are too.
1338 if string.find(greetline, "MercuryP/NLM v1.48") > 0:
1339 warnings = warnings + """
1340 This is not a POP3 server. It has delusions of being one, but after
1341 RETR all messages are automatically marked to be deleted. The only
1342 way to prevent this is to issue an RSET before leaving the server.
1343 Fetchmail does this, but we suspect this is probably broken in lots
1347 if string.find(greetline, "POP-Max") > 0:
1348 warnings = warnings + """
1349 The Mail Max POP3 server screws up on mail with attachments. It
1350 reports the message size with attachments included, but doesn't
1351 download them on a RETR or TOP (this violates the IMAP RFCs). It also
1352 doesn't implement TOP correctly. You should get rid of it -- and the
1353 brain-dead NT server it rode in on.
1356 if string.find(greetline, "POP3 Server Ready") > 0:
1357 warnings = warnings + """
1358 Some server that uses this greeting line has been observed to choke on
1359 TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
1362 if string.find(greetline, "QPOP") > 0:
1363 warnings = warnings + """
1364 This appears to be a version of Eudora qpopper. That's good. Fetchmail
1365 knows all about qpopper. However, be aware that the 2.53 version of
1366 qpopper does something odd that causes fetchmail to hang with a socket
1367 error on very large messages. This is probably not a fetchmail bug, as
1368 it has been observed with fetchpop. The fix is to upgrade to qpopper
1369 3.0beta or a more recent version. Better yet, switch to IMAP.
1372 if string.find(greetline, " sprynet.com") > 0:
1373 warnings = warnings + """
1374 You appear to be using a SpryNet server. In mid-1999 it was reported that
1375 the SpryNet TOP command marks messages seen. Therefore, for proper error
1376 recovery in the event of a line drop, it is strongly recommended that you
1377 turn on `fetchall' on all user entries associated with this server.
1380 if string.find(greetline, "TEMS POP3") > 0:
1381 warnings = warnings + """
1382 Your POP3 server has "TEMS" in its header line. At least one such
1383 server does not process the "TOP" command correctly; the symptom is
1384 that fetchmail hangs when trying to retrieve mail. To work around
1385 this bug, turn on `fetchall' on all user entries associated with this
1389 if string.find(greetline, " spray.se") > 0:
1390 warnings = warnings + """
1391 Your POP3 server has "spray.se" in its header line. In May 2000 at
1392 least one such server did not process the "TOP" command correctly; the
1393 symptom is that messages are treated as headerless. To work around
1394 this bug, turn on `fetchall' on all user entries associated with this
1398 if string.find(greetline, " usa.net") > 0:
1399 warnings = warnings + """
1400 You appear to be using USA.NET's free mail service. Their POP3 servers
1401 (at least as of the 2.2 version in use mid-1998) are quite flaky, but
1402 fetchmail can compensate. They seem to require that fetchall be switched on
1403 (otherwise you won't necessarily see all your mail, not even new mail).
1404 They also botch the TOP command the fetchmail normally uses for retrieval
1405 (it only retrieves about 10 lines rather than the number specified).
1406 Turning on fetchall will disable the use of TOP.
1408 Therefore, it is strongly recommended that you turn on `fetchall' on all
1409 user entries associated with this server.
1412 if string.find(greetline, " Novonyx POP3") > 0:
1413 warnings = warnings + """
1414 Your mailserver is running Novonyx POP3. This server, at least as of
1415 version 2.17, seems to have problems handling and reporting seen bits.
1416 You may have to use the fetchall option.
1419 if string.find(greetline, " IMS POP3") > 0:
1420 warnings = warnings + """
1421 Some servers issuing the greeting line 'IMS POP3' have been known to
1422 do byte-stuffing incorrectly. This means that if a message you receive
1423 has a . (period) at start of line, fetchmail will become confused and
1424 probably wedge itself. (This bug was recorded on IMS POP3 0.86.)
1428 ### IMAP servers start here
1430 if string.find(greetline, "GroupWise") > 0:
1431 warnings = warnings + """
1432 The Novell GroupWise IMAP server would be better named GroupFoolish;
1433 it is (according to the designer of IMAP) unusably broken. Among
1434 other things, it doesn't include a required content length in its
1435 BODY[TEXT] response.<p>
1437 Fetchmail works around this problem, but we strongly recommend voting
1438 with your dollars for a server that isn't brain-dead. If you stick
1439 with code as shoddy as GroupWise seems to be, you will probably pay
1440 for it with other problems.<p>
1443 if string.find(greetline, "InterChange") > 0:
1444 warnings = warnings + """
1446 The InterChange IMAP server at release levels below 3.61.08 screws up
1447 on mail with attachments. It doesn't fetch them if you give it a
1448 BODY[TEXT] request, though it does if you request RFC822.TEXT.
1449 According to the IMAP RFCs and their maintainer these should be
1450 equivalent -- and we can't drop the BODY[TEXT] form because M$
1451 Exchange (quite legally under RFC2062) rejectsit. The InterChange
1452 folks claim to have fixed this bug in 3.61.08.
1455 if string.find(greetline, "Imail") > 0:
1456 warnings = warnings + """
1457 We've seen a bug report indicating that this IMAP server (at least as of
1458 version 5.0.7) returns an invalid body size for messages with MIME
1459 attachments; the effect is to drop the attachments on the floor. We
1460 recommend you upgrade to a non-broken IMAP server.
1463 if string.find(greetline, "Domino IMAP4") > 0:
1464 warnings = warnings + """
1465 Your IMAP server appears to be Lotus Domino. This server, at least up
1466 to version 4.6.2a, has a bug in its generation of MIME boundaries (see
1467 the details in the fetchmail FAQ). As a result, even MIME aware MUAs
1468 will see attachments as part of the message text. If your Domino server's
1469 POP3 facility is enabled, we recommend you fall back on it.
1473 ### Checks for protocol variants start here
1475 closebrak = string.find(greetline, ">")
1476 if closebrak > 0 and greetline[closebrak+1] == "\r":
1477 warnings = warnings + """
1478 It looks like you could use APOP on this server and avoid sending it your
1479 password in clear. You should talk to the mailserver administrator about
1483 if string.find(greetline, "IMAP2bis") > 0:
1484 warnings = warnings + """
1485 IMAP2bis servers have a minor problem; they can't peek at messages without
1486 marking them seen. If you take a line hit during the retrieval, the
1487 interrupted message may get left on the server, marked seen.
1489 To work around this, it is recommended that you set the `fetchall'
1490 option on all user entries associated with this server, so any stuck
1491 mail will be retrieved next time around.
1493 To fix this bug, upgrade to an IMAP4 server. The fetchmail FAQ includes
1494 a pointer to an open-source implementation.
1497 if string.find(greetline, "IMAP4rev1") > 0:
1498 warnings = warnings + """
1499 I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
1500 remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
1501 has therefore been extremely well tested with this class of server.
1505 warnings = warnings + """
1506 Fetchmail doesn't know anything special about this server type.
1510 # Display success window with warnings
1511 title = "Autoprobe of " + realhost + " succeeded"
1512 confirm = "The " + protocol + " server said:\n\n" + greetline + warnings
1513 self.protocol.set(protocol)
1514 self.service.set(defaultports[protocol])
1515 confwin.title(title)
1516 confwin.iconname(title)
1517 Label(confwin, text=title).pack()
1518 Message(confwin, text=confirm, width=600).pack()
1519 Button(confwin, text='Done',
1520 command=lambda x=confwin: x.destroy(), bd=2).pack()
1523 # User editing stuff
1527 'title' : 'User option help',
1528 'banner': 'User options',
1530 You may use this panel to set options
1531 that may differ between individual
1534 Once you have a user configuration set
1535 up as you like it, you can select `OK' to
1536 store it in the user list maintained in
1537 the site configuration window.
1539 If you wish to discard the changes you have
1540 made to user options, select `Quit'.
1544 'title' : 'Local name help',
1545 'banner': 'Local names',
1547 The local name(s) in a user entry are the
1548 people on the client machine who should
1549 receive mail from the poll described.
1551 Note: if a user entry has more than one
1552 local name, messages will be retrieved
1553 in multidrop mode. This complicates
1554 the configuration issues; see the manual
1555 page section on multidrop mode.
1557 Warning: Be careful with local names
1558 such as foo@bar.com, as that can cause
1559 the mail to be sent to foo@bar.com instead
1560 of sending it to your local system.
1563 class UserEdit(Frame, MyWidget):
1564 def __init__(self, username, parent):
1565 self.parent = parent
1567 for user in parent.server.users:
1568 if user.remote == username:
1570 if self.user == None:
1572 self.user.remote = username
1573 self.user.localnames = [username]
1574 parent.server.users.append(self.user)
1576 def edit(self, mode, master=None):
1577 Frame.__init__(self, master)
1579 self.master.title('Fetchmail user ' + self.user.remote
1580 + ' querying ' + self.parent.server.pollname);
1581 self.master.iconname('Fetchmail user ' + self.user.remote);
1582 self.post(User, 'user')
1583 self.makeWidgets(mode, self.parent.server.pollname)
1584 self.keepalive = [] # Use this to anchor the PhotoImage object
1585 make_icon_window(self, fetchmail_icon)
1588 # self.wait_window()
1592 # Yes, this test can fail -- if you delete the parent window.
1593 if self.parent.subwidgets.has_key(self.user.remote):
1594 del self.parent.subwidgets[self.user.remote]
1595 self.master.destroy()
1598 if ConfirmQuit(self, 'user option editing'):
1603 for x in self.user.localnames: ok = ok + (string.find(x, '@') != -1)
1604 if ok == 0 or Dialog(self,
1605 title = "Really accept an embedded '@' ?",
1606 text = "Local names with an embedded '@', such as in foo@bar "
1607 "might result in your mail being sent to foo@bar.com "
1608 "instead of your local system.\n Are you sure you want "
1609 "a local user name with an '@' in it?",
1610 bitmap = 'question',
1611 strings = ('Yes', 'No'),
1612 default = 1).num == 0:
1613 self.fetch(User, 'user')
1616 def makeWidgets(self, mode, servername):
1617 dispose_window(self,
1618 "User options for " + self.user.remote + " querying " + servername,
1621 if mode != 'novice':
1622 leftwin = Frame(self);
1626 secwin = Frame(leftwin, relief=RAISED, bd=5)
1627 Label(secwin, text="Authentication").pack(side=TOP)
1628 LabeledEntry(secwin, 'Password:',
1629 self.password, '12').pack(side=TOP, fill=X)
1630 secwin.pack(fill=X, anchor=N)
1632 if 'ssl' in feature_options or 'ssl' in dictmembers:
1633 sslwin = Frame(leftwin, relief=RAISED, bd=5)
1634 Checkbutton(sslwin, text="Use SSL?",
1635 variable=self.ssl).pack(side=TOP, fill=X)
1636 LabeledEntry(sslwin, 'SSL key:',
1637 self.sslkey, '14').pack(side=TOP, fill=X)
1638 LabeledEntry(sslwin, 'SSL certificate:',
1639 self.sslcert, '14').pack(side=TOP, fill=X)
1640 Checkbutton(sslwin, text="Check server SSL certificate?",
1641 variable=self.sslcertck).pack(side=TOP, fill=X)
1642 LabeledEntry(sslwin, 'SSL trusted certificate directory:',
1643 self.sslcertpath, '14').pack(side=TOP, fill=X)
1644 LabeledEntry(sslwin, 'SSL key fingerprint:',
1645 self.sslfingerprint, '14').pack(side=TOP, fill=X)
1646 sslwin.pack(fill=X, anchor=N)
1648 names = Frame(leftwin, relief=RAISED, bd=5)
1649 Label(names, text="Local names").pack(side=TOP)
1650 ListEdit("New name: ",
1651 self.user.localnames, None, None, names, localhelp)
1652 names.pack(fill=X, anchor=N)
1654 if mode != 'novice':
1655 targwin = Frame(leftwin, relief=RAISED, bd=5)
1656 Label(targwin, text="Forwarding Options").pack(side=TOP)
1657 Label(targwin, text="Listeners to forward to").pack(side=TOP)
1658 ListEdit("New listener:",
1659 self.user.smtphunt, None, None, targwin, None)
1660 Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
1661 ListEdit("Domains:",
1662 self.user.fetchdomains, None, None, targwin, None)
1663 LabeledEntry(targwin, 'Append to MAIL FROM line:',
1664 self.smtpaddress, '26').pack(side=TOP, fill=X)
1665 LabeledEntry(targwin, 'Set RCPT To address:',
1666 self.smtpname, '26').pack(side=TOP, fill=X)
1667 LabeledEntry(targwin, 'Connection setup command:',
1668 self.preconnect, '26').pack(side=TOP, fill=X)
1669 LabeledEntry(targwin, 'Connection wrapup command:',
1670 self.postconnect, '26').pack(side=TOP, fill=X)
1671 LabeledEntry(targwin, 'Local delivery agent:',
1672 self.mda, '26').pack(side=TOP, fill=X)
1673 LabeledEntry(targwin, 'BSMTP output file:',
1674 self.bsmtp, '26').pack(side=TOP, fill=X)
1675 LabeledEntry(targwin, 'Listener spam-block codes:',
1676 self.antispam, '26').pack(side=TOP, fill=X)
1677 LabeledEntry(targwin, 'Pass-through properties:',
1678 self.properties, '26').pack(side=TOP, fill=X)
1679 Checkbutton(targwin, text="Use LMTP?",
1680 variable=self.lmtp).pack(side=TOP, fill=X)
1681 targwin.pack(fill=X, anchor=N)
1683 if mode != 'novice':
1684 leftwin.pack(side=LEFT, fill=X, anchor=N)
1685 rightwin = Frame(self)
1689 optwin = Frame(rightwin, relief=RAISED, bd=5)
1690 Label(optwin, text="Processing Options").pack(side=TOP)
1691 Checkbutton(optwin, text="Suppress deletion of messages after reading",
1692 variable=self.keep).pack(side=TOP, anchor=W)
1693 Checkbutton(optwin, text="Fetch old messages as well as new",
1694 variable=self.fetchall).pack(side=TOP, anchor=W)
1695 if mode != 'novice':
1696 Checkbutton(optwin, text="Flush seen messages before retrieval",
1697 variable=self.flush).pack(side=TOP, anchor=W)
1698 Checkbutton(optwin, text="Flush oversized messages before retrieval",
1699 variable=self.limitflush).pack(side=TOP, anchor=W)
1700 Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply",
1701 variable=self.rewrite).pack(side=TOP, anchor=W)
1702 Checkbutton(optwin, text="Force CR/LF at end of each line",
1703 variable=self.forcecr).pack(side=TOP, anchor=W)
1704 Checkbutton(optwin, text="Strip CR from end of each line",
1705 variable=self.stripcr).pack(side=TOP, anchor=W)
1706 Checkbutton(optwin, text="Pass 8 bits even though SMTP says 7BIT",
1707 variable=self.pass8bits).pack(side=TOP, anchor=W)
1708 Checkbutton(optwin, text="Undo MIME armoring on header and body",
1709 variable=self.mimedecode).pack(side=TOP, anchor=W)
1710 Checkbutton(optwin, text="Drop Status lines from forwarded messages",
1711 variable=self.dropstatus).pack(side=TOP, anchor=W)
1712 Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
1713 variable=self.dropdelivered).pack(side=TOP, anchor=W)
1716 if mode != 'novice':
1717 limwin = Frame(rightwin, relief=RAISED, bd=5)
1718 Label(limwin, text="Resource Limits").pack(side=TOP)
1719 LabeledEntry(limwin, 'Message size limit:',
1720 self.limit, '30').pack(side=TOP, fill=X)
1721 LabeledEntry(limwin, 'Size warning interval:',
1722 self.warnings, '30').pack(side=TOP, fill=X)
1723 LabeledEntry(limwin, 'Max messages to fetch per poll:',
1724 self.fetchlimit, '30').pack(side=TOP, fill=X)
1725 LabeledEntry(limwin, 'Max message sizes to fetch per transaction:',
1726 self.fetchsizelimit, '30').pack(side=TOP, fill=X)
1727 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1728 LabeledEntry(limwin, 'Use fast UIDL:',
1729 self.fastuidl, '30').pack(side=TOP, fill=X)
1730 LabeledEntry(limwin, 'Max messages to forward per poll:',
1731 self.batchlimit, '30').pack(side=TOP, fill=X)
1732 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1733 LabeledEntry(limwin, 'Interval between expunges:',
1734 self.expunge, '30').pack(side=TOP, fill=X)
1735 Checkbutton(limwin, text="Idle after each poll (IMAP only)",
1736 variable=self.idle).pack(side=TOP, anchor=W)
1739 if self.parent.server.protocol == 'IMAP':
1740 foldwin = Frame(rightwin, relief=RAISED, bd=5)
1741 Label(foldwin, text="Remote folders (IMAP only)").pack(side=TOP)
1742 ListEdit("New folder:", self.user.mailboxes,
1743 None, None, foldwin, None)
1744 foldwin.pack(fill=X, anchor=N)
1746 if mode != 'novice':
1747 rightwin.pack(side=LEFT)
1753 # Top-level window that offers either novice or expert mode
1754 # (but not both at once; it disappears when one is selected).
1757 class Configurator(Frame):
1758 def __init__(self, outfile, master, onexit, parent):
1759 Frame.__init__(self, master)
1760 self.outfile = outfile
1761 self.onexit = onexit
1762 self.parent = parent
1763 self.master.title('fetchmail configurator');
1764 self.master.iconname('fetchmail configurator');
1766 self.keepalive = [] # Use this to anchor the PhotoImage object
1767 make_icon_window(self, fetchmail_icon)
1769 Message(self, text="""
1770 Use `Novice Configuration' for basic fetchmail setup;
1771 with this, you can easily set up a single-drop connection
1772 to one remote mail server.
1773 """, width=600).pack(side=TOP)
1774 Button(self, text='Novice Configuration',
1775 fg='blue', command=self.novice).pack()
1777 Message(self, text="""
1778 Use `Expert Configuration' for advanced fetchmail setup,
1779 including multiple-site or multidrop connections.
1780 """, width=600).pack(side=TOP)
1781 Button(self, text='Expert Configuration',
1782 fg='blue', command=self.expert).pack()
1784 Message(self, text="""
1785 Or you can just select `Quit' to leave the configurator now and
1786 return to the main panel.
1787 """, width=600).pack(side=TOP)
1788 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1789 master.protocol("WM_DELETE_WINDOW", self.leave)
1792 self.master.destroy()
1793 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1796 self.master.destroy()
1797 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1800 self.master.destroy()
1803 # Run a command in a scrolling text widget, displaying its output
1805 class RunWindow(Frame):
1806 def __init__(self, command, master, parent):
1807 Frame.__init__(self, master)
1808 self.master = master
1809 self.master.title('fetchmail run window');
1810 self.master.iconname('fetchmail run window');
1813 text="Running "+command,
1814 bd=2).pack(side=TOP, pady=10)
1815 self.keepalive = [] # Use this to anchor the PhotoImage object
1816 make_icon_window(self, fetchmail_icon)
1818 # This is a scrolling text window
1819 textframe = Frame(self)
1820 scroll = Scrollbar(textframe)
1821 self.textwidget = Text(textframe, setgrid=TRUE)
1822 textframe.pack(side=TOP, expand=YES, fill=BOTH)
1823 self.textwidget.config(yscrollcommand=scroll.set)
1824 self.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
1825 scroll.config(command=self.textwidget.yview)
1826 scroll.pack(side=RIGHT, fill=BOTH)
1827 textframe.pack(side=TOP)
1829 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1831 self.update() # Draw widget before executing fetchmail
1833 # Always look for a runnable command in the directory we're running in
1834 # first. This avoids some obscure version-skew errors that can occur
1835 # if you pick up an old fetchmail from the standard system locations.
1836 os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"]
1837 child_stdout = os.popen(command + " 2>&1 </dev/null", "r")
1839 ch = child_stdout.read(1)
1842 self.textwidget.insert(END, ch)
1843 self.textwidget.insert(END, "Done.")
1844 self.textwidget.see(END);
1847 self.master.destroy()
1849 # Here's where we choose either configuration or launching
1851 class MainWindow(Frame):
1852 def __init__(self, outfile, master=None):
1853 Frame.__init__(self, master)
1854 self.outfile = outfile
1855 self.master.title('fetchmail launcher');
1856 self.master.iconname('fetchmail launcher');
1859 text='Fetchmailconf ' + version,
1860 bd=2).pack(side=TOP, pady=10)
1861 self.keepalive = [] # Use this to anchor the PhotoImage object
1862 make_icon_window(self, fetchmail_icon)
1865 ## Test icon display with the following:
1866 # icon_image = PhotoImage(data=fetchmail_icon)
1867 # Label(self, image=icon_image).pack(side=TOP, pady=10)
1868 # self.keepalive.append(icon_image)
1870 Message(self, text="""
1871 Use `Configure fetchmail' to tell fetchmail about the remote
1872 servers it should poll (the host name, your username there,
1873 whether to use POP or IMAP, and so forth).
1874 """, width=600).pack(side=TOP)
1875 self.configbutton = Button(self, text='Configure fetchmail',
1876 fg='blue', command=self.configure)
1877 self.configbutton.pack()
1879 Message(self, text="""
1880 Use `Run fetchmail' to run fetchmail with debugging enabled.
1881 This is a good way to test out a new configuration.
1882 """, width=600).pack(side=TOP)
1883 Button(self, text='Run fetchmail',fg='blue', command=self.test).pack()
1885 Message(self, text="""
1886 Use `Run fetchmail' to run fetchmail in foreground.
1887 Progress messages will be shown, but not debug messages.
1888 """, width=600).pack(side=TOP)
1889 Button(self, text='Run fetchmail', fg='blue', command=self.run).pack()
1891 Message(self, text="""
1892 Or you can just select `Quit' to exit the launcher now.
1893 """, width=600).pack(side=TOP)
1894 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1896 def configure(self):
1897 self.configbutton.configure(state=DISABLED)
1898 Configurator(self.outfile, Toplevel(),
1899 lambda self=self: self.configbutton.configure(state=NORMAL),
1902 cmd = "fetchmail -N -d0 --nosyslog -v"
1904 cmd = cmd + " -f " + rcfile
1905 RunWindow(cmd, Toplevel(), self)
1908 cmd = "fetchmail -N -d0"
1910 cmd = cmd + " -f " + rcfile
1911 RunWindow(cmd, Toplevel(), self)
1916 # Functions for turning a dictionary into an instantiated object tree.
1918 def intersect(list1, list2):
1919 # Compute set intersection of lists
1926 def setdiff(list1, list2):
1927 # Compute set difference of lists
1934 def copy_instance(toclass, fromdict):
1935 # Initialize a class object of given type from a conformant dictionary.
1936 for fld in fromdict.keys():
1937 if not fld in dictmembers:
1938 dictmembers.append(fld)
1939 # The `optional' fields are the ones we can ignore for purposes of
1940 # conformability checking; they'll still get copied if they are
1941 # present in the dictionary.
1942 optional = ('interface', 'monitor',
1943 'esmtpname', 'esmtppassword',
1944 'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
1945 'sslcertpath', 'sslfingerprint', 'showdots')
1946 class_sig = setdiff(toclass.__dict__.keys(), optional)
1948 dict_keys = setdiff(fromdict.keys(), optional)
1950 common = intersect(class_sig, dict_keys)
1951 if 'typemap' in class_sig:
1952 class_sig.remove('typemap')
1953 if tuple(class_sig) != tuple(dict_keys):
1954 print "Fields don't match what fetchmailconf expected:"
1955 # print "Class signature: " + `class_sig`
1956 # print "Dictionary keys: " + `dict_keys`
1957 diff = setdiff(class_sig, common)
1959 print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
1960 diff = setdiff(dict_keys, common)
1962 print "Not matched in dictionary keys: " + `diff`
1965 for x in fromdict.keys():
1966 setattr(toclass, x, fromdict[x])
1969 # And this is the main sequence. How it works:
1971 # First, call `fetchmail --configdump' and trap the output in a tempfile.
1972 # This should fill it with a Python initializer for a variable `fetchmailrc'.
1973 # Run execfile on the file to pull fetchmailrc into Python global space.
1974 # You don't want static data, though; you want, instead, a tree of objects
1975 # with the same data members and added appropriate methods.
1977 # This is what the copy_instance function() is for. It tries to copy a
1978 # dictionary field by field into a class, aborting if the class and dictionary
1979 # have different data members (except for any typemap member in the class;
1980 # that one is strictly for use by the MyWidget supperclass).
1982 # Once the object tree is set up, require user to choose novice or expert
1983 # mode and instantiate an edit object for the configuration. Class methods
1984 # will take it all from there.
1986 # Options (not documented because they're for fetchmailconf debuggers only):
1987 # -d: Read the configuration and dump it to stdout before editing. Dump
1988 # the edited result to stdout as well.
1989 # -f: specify the run control file to read.
1991 if __name__ == '__main__':
1993 if not os.environ.has_key("DISPLAY"):
1994 print "fetchmailconf must be run under X"
1997 fetchmail_icon = """
1998 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
1999 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
2000 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
2001 CJR7Y/+UMdbOxnNrY97Ove/Wvd7GrZyEa961jL2Ua9alc86ca7WEUntSKcaMSqVjGNZ7GGM5CNa1
2002 jPfOnN6tc3taMffeve/WtWtaQv/OjGtSMYRzWv/erda1hM6te7WUY62MWs61jP/vzv/ntda9jL2l
2003 czEhAO/n1oyEc//elDEpGEo5EOfexpyUe+/epefevffvxnNrQpyUStbWzsbGvZyclN7ezmNjWv//
2004 5/f33qWllNbWve/vzv//1ufnve/vvf//xvf3vefnrf//taWlc0pKMf//pbW1Y///jKWlWq2tWsbG
2005 Y///c97eUvf3Ut7nc+/3a87We8bOjOfv1u/37/f//621tb3Gxtbn52Nra87n53uUlJTv/6W9xuf3
2006 /8bW3iExOXu11tbv/5TW/4TO/63e/zmt/1KUxlK1/2u9/wCM/73GzrXG1gBKjACE/87e72NzhCkx
2007 OaXO92OMtUql/xCE/wApUtbe57W9xnN7hHut52Ot/xBSnABKnABavQB7/2ul7zF71gBr77XO73Oc
2008 1lqc9yFSlBApSimE/wAYOQApY0J7zlKM5wAxhABS1gBj/6W95wAhWgA5nAAYSgBS7wBS/wBK9wAp
2009 jABC5wBK/wApnABC/wApxgAhtYSMtQAQYwAp/3OE74SMxgAYxlpjvWNr70pS/wgQ3sbGzs7O1qWl
2010 3qWl70pKe0JC/yEhlCkp/wgI/wAAEAAAIQAAKQAAOQAASgAAUgAAYwAAawAAlAAAnAAApQAArQAA
2011 zgAA1gAA5wAA9wAA/0pC/xgQ52Na9ykhe4R7zikhYxgQSjEpQgAAACwAAAAAPAAoAAAI/wABCBxI
2012 sKDBgwgTKiRIYKHDhxARIvgXsaLFhGgEUBSYoKPHjyBDihxJkuS/kwNLqlzJcuTJjQBaypxpEiVH
2013 mjhxvkyZs2fLnTd9ehxAtKjRo0ZrwhTasUsENhYHKOUpk1E3j11mxCBiQVLEBlJd2owp9iVRjwUs
2014 zMCQ5IcLD4saPVxjIKxIoGTvvqSoyFEFGTBeqEhyxAoSFR/USGKVcEGBAwDshsSr1OYTEyhQpJiS
2015 ZcoUKWOQtJDRJFSaggzUGBgoGSTlsjahlPCRIkWVKT16THHRIoqIISBIEUgAYIGBhgRbf3ytFygU
2016 FZp9UDmxQkkMCRwyZKDBQy4aApABhP8XqNwj88l7BVpQYZtF5iArWgwAgGZBq24HU7OeGhQ90PVA
2017 aKZZCiiUMJ9ArSTEwGqR8ZeXfzbV0MIIMQTBwoUdxDDfAm8sZFyDZVEF4UYSKBEBD0+k6IEFPMxH
2018 3FzldXSea+kBgANJSOWIlIMhXZXAXv+c1WM3PuJEpH8iuhbAkv+MdENPRHaTRkdF/jiWSKCAwlKW
2019 VbbkY5Q0LgUSKExgoYBKCjCxARpdltQNKHaUoYAddnR53lVRnJLKBWh4RIEGCZx5FSOv1OLNDUVe
2020 deZHaWiZAB35fIOGNtbEUeV5oGAByzPOrBPFGt3kwEgxITACSg5oLGGLMg60oQAjaNz/oAAcN4Ai
2021 a0c3kHFDK3jYsw4g9sRzBgPLXdkRrBrQ8gsWQUxCCRZX9IJNBQ1s8IgCdeBCzBYN6IBIN2TUsQYd
2022 dXhDBxdzlAHOHHKEcocZdWwDjx8MTCmjsR2FMAstw1RyiSzHqPLALaOwk8QmzCzDCSi0xJKMMk4E
2023 Yw8389iTDT32GAKOPf7YY0Aa9tATyD3w/EGsefgmgEYUtPiChLKWQDMBJtEUgYkzH2RiTgGfTMCI
2024 Mlu0Yc85hNiDziH2tMqOGL72QY47gshLb7Fi4roELcjoQIsxWpDwQyfS2OCJMkLI4YUmyhgxSTVg
2025 CP2FHPZ80UDcieBjStNPD5LPOyZT/y0iHGiMwswexDSzRiRq6KIMJBc4M8skwKAyChia2KPH3P24
2026 YU8/lFhOTj152OPOHuXMU4g48vCRiN/9rZGLMdS4csUu1JzDgxuipOMDHMKsAwEnq/ByzTrrZMNO
2027 OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
2028 xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
2029 gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
2031 # The base64 data in the string above was generated by the following procedure:
2034 # print base64.encodestring(open("fetchmail.gif", "rb").read())
2038 (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV", ["help",
2040 dump = rcfile = None;
2041 for (switch, val) in options:
2042 if (switch == '-d'):
2044 elif (switch == '-f'):
2046 elif (switch == '-h' or switch == '--help'):
2048 Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|--help|-V|--version}
2049 -d - dump configuration (for debugging)
2050 -f fmrc - read alternate fetchmailrc file
2051 --help, -h - print this help text and quit
2052 --version, -V - print fetchmailconf version and quit
2055 elif (switch == '-V' or switch == '--version'):
2056 print "fetchmailconf %s" % version
2058 Copyright (C) 1997 - 2003 Eric S. Raymond
2059 Copyright (C) 2005 - 2006 Matthias Andree
2060 fetchmailconf comes with ABSOLUTELY NO WARRANTY. This is free software, you are
2061 welcome to redistribute it under certain conditions. Please see the file
2062 COPYING in the source or documentation directory for details. """
2065 # Get client host's FQDN
2066 hostname = socket.gethostbyaddr(socket.gethostname())[0]
2069 ConfigurationDefaults = Configuration()
2070 ServerDefaults = Server()
2071 UserDefaults = User()
2073 # Read the existing configuration. We set the umask to 077 to make sure
2074 # that group & other read/write permissions are shut off -- we wouldn't
2075 # want crackers to snoop password information out of the tempfile.
2076 tmpfile = tempfile.mktemp()
2078 cmd = "umask 077 && fetchmail </dev/null -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2080 cmd = "umask 077 && fetchmail </dev/null --configdump --nosyslog >" + tmpfile
2085 print "`" + cmd + "' run failure, status " + `s`
2088 print "Unknown error while running fetchmail --configdump"
2095 print "Can't read configuration output of fetchmail --configdump."
2101 # The tricky part -- initializing objects from the configuration global
2102 # `Configuration' is the top level of the object tree we're going to mung.
2103 # The dictmembers list is used to track the set of fields the dictionary
2104 # contains; in particular, we can use it to tell whether things like the
2105 # monitor, interface, ssl, sslkey, or sslcert fields are present.
2107 Fetchmailrc = Configuration()
2108 copy_instance(Fetchmailrc, fetchmailrc)
2109 Fetchmailrc.servers = [];
2110 for server in fetchmailrc['servers']:
2112 copy_instance(Newsite, server)
2113 Fetchmailrc.servers.append(Newsite)
2115 for user in server['users']:
2117 copy_instance(Newuser, user)
2118 Newsite.users.append(Newuser)
2120 # We may want to display the configuration and quit
2122 print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2124 # The theory here is that -f alone sets the rcfile location,
2125 # but -d and -f together mean the new configuration should go to stdout.
2126 if not rcfile and not dump:
2127 rcfile = os.environ["HOME"] + "/.fetchmailrc"
2129 # OK, now run the configuration edit
2130 root = MainWindow(rcfile)
2133 # The following sets edit modes for GNU EMACS