3 # A GUI configurator for generating fetchmail configuration files.
4 # by Eric S. Raymond, <esr@snark.thyrsus.com>,
5 # Matthias Andree <matthias.andree@gmx.de>
6 # Requires Python with Tkinter, and the following OS-dependent services:
7 # posix, posixpath, socket
12 import sys, time, os, string, socket, getopt, tempfile
15 # Define the data structures the GUIs will be tossing around
19 self.poll_interval = 0 # Normally, run in foreground
20 self.logfile = None # No logfile, initially
21 self.idfile = os.environ["HOME"] + "/.fetchids" # Default idfile, initially
22 self.postmaster = None # No last-resort address, initially
23 self.bouncemail = TRUE # Bounce errors to users
24 self.spambounce = FALSE # Bounce spam errors
25 self.softbounce = TRUE # Treat permanent error as temporary
26 self.properties = None # No exiguous properties
27 self.invisible = FALSE # Suppress Received line & spoof?
28 self.syslog = FALSE # Use syslogd for logging?
29 self.servers = [] # List of included sites
30 Configuration.typemap = (
31 ('poll_interval', 'Int'),
32 ('logfile', 'String'),
34 ('postmaster', 'String'),
35 ('bouncemail', 'Boolean'),
36 ('spambounce', 'Boolean'),
37 ('softbounce', 'Boolean'),
38 ('properties', 'String'),
39 ('syslog', 'Boolean'),
40 ('invisible', 'Boolean'))
44 if self.syslog != ConfigurationDefaults.syslog:
45 str = str + ("set syslog\n")
47 str = str + ("set logfile \"%s\"\n" % (self.logfile,));
48 if self.idfile != ConfigurationDefaults.idfile:
49 str = str + ("set idfile \"%s\"\n" % (self.idfile,));
50 if self.postmaster != ConfigurationDefaults.postmaster:
51 str = str + ("set postmaster \"%s\"\n" % (self.postmaster,));
53 str = str + ("set bouncemail\n")
55 str = str + ("set nobouncemail\n")
57 str = str + ("set spambounce\n")
59 str = str + ("set no spambounce\n")
61 str = str + ("set softbounce\n")
63 str = str + ("set no softbounce\n")
64 if self.properties != ConfigurationDefaults.properties:
65 str = str + ("set properties \"%s\"\n" % (self.properties,));
66 if self.poll_interval > 0:
67 str = str + "set daemon " + `self.poll_interval` + "\n"
69 str = str + ("set invisible\n")
70 for site in self.servers:
71 str = str + repr(site)
74 def __delitem__(self, name):
75 for si in range(len(self.servers)):
76 if self.servers[si].pollname == name:
81 return "[Configuration: " + repr(self) + "]"
85 self.pollname = None # Poll label
86 self.via = None # True name of host
87 self.active = TRUE # Poll status
88 self.interval = 0 # Skip interval
89 self.protocol = 'auto' # Default to auto protocol
90 self.service = None # Service name to use
91 self.uidl = FALSE # Don't use RFC1725 UIDLs by default
92 self.auth = 'any' # Default to password authentication
93 self.timeout = 300 # 5-minute timeout
94 self.envelope = 'Received' # Envelope-address header
95 self.envskip = 0 # Number of envelope headers to skip
96 self.qvirtual = None # Name prefix to strip
97 self.aka = [] # List of DNS aka names
98 self.dns = TRUE # Enable DNS lookup on multidrop
99 self.localdomains = [] # Domains to be considered local
100 self.interface = None # IP address and range
101 self.monitor = None # IP address and range
102 self.plugin = None # Plugin command for going to server
103 self.plugout = None # Plugin command for going to listener
104 self.principal = None # Kerberos principal
105 self.esmtpname = None # ESMTP 2554 name
106 self.esmtppassword = None # ESMTP 2554 password
107 self.tracepolls = FALSE # Add trace-poll info to headers
108 self.badheader = FALSE # Pass messages with bad headers on?
109 self.retrieveerror = 'abort' # Policy when message retrieval errors encountered
110 self.users = [] # List of user entries for site
112 ('pollname', 'String'),
114 ('active', 'Boolean'),
116 ('protocol', 'String'),
117 ('service', 'String'),
121 ('envelope', 'String'),
123 ('qvirtual', 'String'),
126 # leave localdomains out
127 ('interface', 'String'),
128 ('monitor', 'String'),
129 ('plugin', 'String'),
130 ('plugout', 'String'),
131 ('esmtpname', 'String'),
132 ('esmtppassword', 'String'),
133 ('principal', 'String'),
134 ('tracepolls','Boolean'),
135 ('badheader', 'Boolean'),
136 ('retrieveerror', 'String'))
138 def dump(self, folded):
140 if self.active: res = res + "poll"
141 else: res = res + "skip"
142 res = res + (" " + self.pollname)
144 res = res + (" via " + str(self.via) + "\n");
145 if self.protocol != ServerDefaults.protocol:
146 res = res + " with proto " + self.protocol
147 if self.service and self.protocol and self.service != defaultports[self.protocol] and defaultports[self.protocol] and self.service != ianaservices[defaultports[self.protocol]]:
148 res = res + " service " + self.service
149 if self.timeout != ServerDefaults.timeout:
150 res = res + " timeout " + `self.timeout`
151 if self.interval != ServerDefaults.interval:
152 res = res + " interval " + `self.interval`
153 if self.envelope != ServerDefaults.envelope or self.envskip != ServerDefaults.envskip:
155 res = res + " envelope " + `self.envskip` + " " + self.envelope
157 res = res + " envelope " + self.envelope
159 res = res + (" qvirtual " + str(self.qvirtual) + "\n");
160 if self.auth != ServerDefaults.auth:
161 res = res + " auth " + self.auth
162 if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl:
163 res = res + " and options"
164 if self.dns != ServerDefaults.dns:
165 res = res + flag2str(self.dns, 'dns')
166 if self.uidl != ServerDefaults.uidl:
167 res = res + flag2str(self.uidl, 'uidl')
168 if folded: res = res + "\n "
169 else: res = res + " "
175 if self.aka and self.localdomains: res = res + " "
176 if self.localdomains:
177 res = res + ("localdomains")
178 for x in self.localdomains:
180 if (self.aka or self.localdomains):
187 res = res + "tracepolls\n"
190 res = res + " interface " + str(self.interface)
192 res = res + " monitor " + str(self.monitor)
194 res = res + " plugin " + `self.plugin`
196 res = res + " plugout " + `self.plugout`
198 res = res + " principal " + `self.principal`
200 res = res + " esmtpname " + `self.esmtpname`
201 if self.esmtppassword:
202 res = res + " esmtppassword " + `self.esmtppassword`
203 if self.interface or self.monitor or self.principal or self.plugin or self.plugout:
208 res = res + "bad-header accept "
209 if self.retrieveerror == 'continue':
210 res = res + "retrieve-error continue "
211 if self.retrieveerror == 'markseen':
212 res = res + "retrieve-error markseen "
213 if self.badheader or self.retrieveerror != ServerDefaults.retrieveerror:
217 if res[-1] == " ": res = res[0:-1]
219 for user in self.users:
220 res = res + repr(user)
224 def __delitem__(self, name):
225 for ui in range(len(self.users)):
226 if self.users[ui].remote == name:
231 return self.dump(TRUE)
234 return "[Server: " + self.dump(FALSE) + "]"
238 if os.environ.has_key("USER"):
239 self.remote = os.environ["USER"] # Remote username
240 elif os.environ.has_key("LOGNAME"):
241 self.remote = os.environ["LOGNAME"]
243 print "Can't get your username!"
245 self.localnames = [self.remote,]# Local names
246 self.password = None # Password for mail account access
247 self.mailboxes = [] # Remote folders to retrieve from
248 self.smtphunt = [] # Hosts to forward to
249 self.fetchdomains = [] # Domains to fetch from
250 self.smtpaddress = None # Append this to MAIL FROM line
251 self.smtpname = None # Use this for RCPT TO
252 self.preconnect = None # Connection setup
253 self.postconnect = None # Connection wrapup
254 self.mda = None # Mail Delivery Agent
255 self.bsmtp = None # BSMTP output file
256 self.lmtp = FALSE # Use LMTP rather than SMTP?
257 self.antispam = "" # Listener's spam-block code
258 self.keep = FALSE # Keep messages
259 self.flush = FALSE # Flush messages
260 self.limitflush = FALSE # Flush oversized messages
261 self.fetchall = FALSE # Fetch old messages
262 self.rewrite = TRUE # Rewrite message headers
263 self.forcecr = FALSE # Force LF -> CR/LF
264 self.stripcr = FALSE # Strip CR
265 self.pass8bits = FALSE # Force BODY=7BIT
266 self.mimedecode = FALSE # Undo MIME armoring
267 self.dropstatus = FALSE # Drop incoming Status lines
268 self.dropdelivered = FALSE # Drop incoming Delivered-To lines
269 self.idle = FALSE # IDLE after poll
270 self.limit = 0 # Message size limit
271 self.warnings = 3600 # Size warning interval (see tunable.h)
272 self.fetchlimit = 0 # Max messages fetched per batch
273 self.fetchsizelimit = 100 # Max message sizes fetched per transaction
274 self.fastuidl = 4 # Do fast uidl 3 out of 4 times
275 self.batchlimit = 0 # Max message forwarded per batch
276 self.expunge = 0 # Interval between expunges (IMAP)
277 self.ssl = 0 # Enable Seccure Socket Layer
278 self.sslkey = None # SSL key filename
279 self.sslcert = None # SSL certificate filename
280 self.sslproto = None # Force SSL?
281 self.sslcertck = 0 # Enable strict SSL cert checking
282 self.sslcertpath = None # Path to trusted certificates
283 self.sslcommonname = None # SSL CommonName to expect
284 self.sslfingerprint = None # SSL key fingerprint to check
285 self.properties = None # Extension properties
287 ('remote', 'String'),
288 # leave out mailboxes and localnames
289 ('password', 'String'),
290 # Leave out smtphunt, fetchdomains
291 ('smtpaddress', 'String'),
292 ('smtpname', 'String'),
293 ('preconnect', 'String'),
294 ('postconnect', 'String'),
298 ('antispam', 'String'),
300 ('flush', 'Boolean'),
301 ('limitflush', 'Boolean'),
302 ('fetchall', 'Boolean'),
303 ('rewrite', 'Boolean'),
304 ('forcecr', 'Boolean'),
305 ('stripcr', 'Boolean'),
306 ('pass8bits', 'Boolean'),
307 ('mimedecode', 'Boolean'),
308 ('dropstatus', 'Boolean'),
309 ('dropdelivered', 'Boolean'),
313 ('fetchlimit', 'Int'),
314 ('fetchsizelimit', 'Int'),
316 ('batchlimit', 'Int'),
319 ('sslkey', 'String'),
320 ('sslcert', 'String'),
321 ('sslcertck', 'Boolean'),
322 ('sslcertpath', 'String'),
323 ('sslcommonname', 'String'),
324 ('sslfingerprint', 'String'),
325 ('properties', 'String'))
329 res = res + "user " + `self.remote` + " there ";
331 res = res + "with password " + `self.password` + " "
334 for x in self.localnames:
335 res = res + " " + `x`
337 if (self.keep != UserDefaults.keep
338 or self.flush != UserDefaults.flush
339 or self.limitflush != UserDefaults.limitflush
340 or self.fetchall != UserDefaults.fetchall
341 or self.rewrite != UserDefaults.rewrite
342 or self.forcecr != UserDefaults.forcecr
343 or self.stripcr != UserDefaults.stripcr
344 or self.pass8bits != UserDefaults.pass8bits
345 or self.mimedecode != UserDefaults.mimedecode
346 or self.dropstatus != UserDefaults.dropstatus
347 or self.dropdelivered != UserDefaults.dropdelivered
348 or self.idle != UserDefaults.idle):
349 res = res + " options"
350 if self.keep != UserDefaults.keep:
351 res = res + flag2str(self.keep, 'keep')
352 if self.flush != UserDefaults.flush:
353 res = res + flag2str(self.flush, 'flush')
354 if self.limitflush != UserDefaults.limitflush:
355 res = res + flag2str(self.limitflush, 'limitflush')
356 if self.fetchall != UserDefaults.fetchall:
357 res = res + flag2str(self.fetchall, 'fetchall')
358 if self.rewrite != UserDefaults.rewrite:
359 res = res + flag2str(self.rewrite, 'rewrite')
360 if self.forcecr != UserDefaults.forcecr:
361 res = res + flag2str(self.forcecr, 'forcecr')
362 if self.stripcr != UserDefaults.stripcr:
363 res = res + flag2str(self.stripcr, 'stripcr')
364 if self.pass8bits != UserDefaults.pass8bits:
365 res = res + flag2str(self.pass8bits, 'pass8bits')
366 if self.mimedecode != UserDefaults.mimedecode:
367 res = res + flag2str(self.mimedecode, 'mimedecode')
368 if self.dropstatus != UserDefaults.dropstatus:
369 res = res + flag2str(self.dropstatus, 'dropstatus')
370 if self.dropdelivered != UserDefaults.dropdelivered:
371 res = res + flag2str(self.dropdelivered, 'dropdelivered')
372 if self.idle != UserDefaults.idle:
373 res = res + flag2str(self.idle, 'idle')
374 if self.limit != UserDefaults.limit:
375 res = res + " limit " + `self.limit`
376 if self.warnings != UserDefaults.warnings:
377 res = res + " warnings " + `self.warnings`
378 if self.fetchlimit != UserDefaults.fetchlimit:
379 res = res + " fetchlimit " + `self.fetchlimit`
380 if self.fetchsizelimit != UserDefaults.fetchsizelimit:
381 res = res + " fetchsizelimit " + `self.fetchsizelimit`
382 if self.fastuidl != UserDefaults.fastuidl:
383 res = res + " fastuidl " + `self.fastuidl`
384 if self.batchlimit != UserDefaults.batchlimit:
385 res = res + " batchlimit " + `self.batchlimit`
386 if self.ssl and self.ssl != UserDefaults.ssl:
387 res = res + flag2str(self.ssl, 'ssl')
388 if self.sslkey and self.sslkey != UserDefaults.sslkey:
389 res = res + " sslkey " + `self.sslkey`
390 if self.sslcert and self.sslcert != UserDefaults.sslcert:
391 res = res + " sslcert " + `self.sslcert`
392 if self.sslproto and self.sslproto != UserDefaults.sslproto:
393 res = res + " sslproto " + `self.sslproto`
394 if self.sslcertck and self.sslcertck != UserDefaults.sslcertck:
395 res = res + flag2str(self.sslcertck, 'sslcertck')
396 if self.sslcertpath and self.sslcertpath != UserDefaults.sslcertpath:
397 res = res + " sslcertpath " + `self.sslcertpath`
398 if self.sslcommonname and self.sslcommonname != UserDefaults.sslcommonname:
399 res = res + " sslcommonname " + `self.sslcommonname`
400 if self.sslfingerprint and self.sslfingerprint != UserDefaults.sslfingerprint:
401 res = res + " sslfingerprint " + `self.sslfingerprint`
402 if self.expunge != UserDefaults.expunge:
403 res = res + " expunge " + `self.expunge`
405 trimmed = self.smtphunt;
406 if trimmed != [] and trimmed[len(trimmed) - 1] == "localhost":
407 trimmed = trimmed[0:len(trimmed) - 1]
408 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
409 trimmed = trimmed[0:len(trimmed) - 1]
411 res = res + " smtphost "
415 trimmed = self.fetchdomains
416 if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
417 trimmed = trimmed[0:len(trimmed) - 1]
419 res = res + " fetchdomains "
424 res = res + " folder"
425 for x in self.mailboxes:
426 res = res + ' "%s"' % x
428 for fld in ('smtpaddress', 'preconnect', 'postconnect', 'mda', 'bsmtp', 'properties'):
429 if getattr(self, fld):
430 res = res + " %s %s\n" % (fld, `getattr(self, fld)`)
431 if self.lmtp != UserDefaults.lmtp:
432 res = res + flag2str(self.lmtp, 'lmtp')
433 if self.antispam != UserDefaults.antispam:
434 res = res + " antispam " + self.antispam + "\n"
438 return "[User: " + repr(self) + "]"
444 # IANA port assignments and bogus 1109 entry
445 ianaservices = {"pop2":109,
452 # fetchmail protocol to IANA service name
453 defaultports = {"auto":None,
462 authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp",
466 'title' : 'List Selection Help',
467 'banner': 'List Selection',
469 You must select an item in the list box (by clicking on it).
472 def flag2str(value, string):
473 # make a string representation of a .fetchmailrc flag or negated flag
477 if value == FALSE: str = str + ("no ")
481 class LabeledEntry(Frame):
482 # widget consisting of entry field with caption to left
483 def bind(self, key, action):
484 self.E.bind(key, action)
487 def __init__(self, Master, text, textvar, lwidth, ewidth=12):
488 Frame.__init__(self, Master)
489 self.L = Label(self, {'text':text, 'width':lwidth, 'anchor':'w'})
490 self.E = Entry(self, {'textvar':textvar, 'width':ewidth})
491 self.L.pack({'side':'left'})
492 self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
494 def ButtonBar(frame, legend, ref, alternatives, depth, command):
495 # array of radio buttons, caption to left, picking from a string list
497 width = (len(alternatives)+1) / depth;
498 Label(bar, text=legend).pack(side=LEFT)
499 for column in range(width):
500 subframe = Frame(bar)
501 for row in range(depth):
502 ind = width * row + column
503 if ind < len(alternatives):
504 Radiobutton(subframe,
505 {'text':alternatives[ind],
507 'value':alternatives[ind],
508 'command':command}).pack(side=TOP, anchor=W)
510 # This is just a spacer
511 Radiobutton(subframe,
512 {'text':" ",'state':DISABLED}).pack(side=TOP, anchor=W)
513 subframe.pack(side=LEFT)
517 def helpwin(helpdict):
518 # help message window with a self-destruct button
520 helpwin.title(helpdict['title'])
521 helpwin.iconname(helpdict['title'])
522 Label(helpwin, text=helpdict['banner']).pack()
523 textframe = Frame(helpwin)
524 scroll = Scrollbar(textframe)
525 helpwin.textwidget = Text(textframe, setgrid=TRUE)
526 textframe.pack(side=TOP, expand=YES, fill=BOTH)
527 helpwin.textwidget.config(yscrollcommand=scroll.set)
528 helpwin.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
529 scroll.config(command=helpwin.textwidget.yview)
530 scroll.pack(side=RIGHT, fill=BOTH)
531 helpwin.textwidget.insert(END, helpdict['text']);
532 Button(helpwin, text='Done',
533 command=lambda x=helpwin: x.destroy(), bd=2).pack()
534 textframe.pack(side=TOP)
536 def make_icon_window(base, image):
538 # Some older pythons will error out on this
539 icon_image = PhotoImage(data=image)
540 icon_window = Toplevel()
541 Label(icon_window, image=icon_image, bg='black').pack()
542 base.master.iconwindow(icon_window)
543 # Avoid TkInter brain death. PhotoImage objects go out of
544 # scope when the enclosing function returns. Therefore
545 # we have to explicitly link them to something.
546 base.keepalive.append(icon_image)
550 class ListEdit(Frame):
551 # edit a list of values (duplicates not allowed) with a supplied editor hook
552 def __init__(self, newlegend, list, editor, deletor, master, helptxt):
554 self.deletor = deletor
557 # Set up a widget to accept new elements
558 self.newval = StringVar(master)
559 newwin = LabeledEntry(master, newlegend, self.newval, '12')
560 newwin.bind('<Double-1>', self.handleNew)
561 newwin.bind('<Return>', self.handleNew)
562 newwin.pack(side=TOP, fill=X, anchor=E)
564 # Edit the existing list
565 listframe = Frame(master)
566 scroll = Scrollbar(listframe)
567 self.listwidget = Listbox(listframe, height=0, selectmode='browse')
570 self.listwidget.insert(END, x)
571 listframe.pack(side=TOP, expand=YES, fill=BOTH)
572 self.listwidget.config(yscrollcommand=scroll.set)
573 self.listwidget.pack(side=LEFT, expand=YES, fill=BOTH)
574 scroll.config(command=self.listwidget.yview)
575 scroll.pack(side=RIGHT, fill=BOTH)
576 self.listwidget.config(selectmode=SINGLE, setgrid=TRUE)
577 self.listwidget.bind('<Double-1>', self.handleList);
578 self.listwidget.bind('<Return>', self.handleList);
582 Button(bf, text='Edit', command=self.editItem).pack(side=LEFT)
583 Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT)
585 self.helptxt = helptxt
586 Button(bf, text='Help', fg='blue',
587 command=self.help).pack(side=RIGHT)
591 helpwin(self.helptxt)
593 def handleList(self, event):
596 def handleNew(self, event):
597 item = self.newval.get()
599 entire = self.listwidget.get(0, self.listwidget.index('end'));
600 if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
601 self.listwidget.insert('end', item)
602 if self.list != None: self.list.append(item)
604 apply(self.editor, (item,))
608 select = self.listwidget.curselection()
613 if index and self.editor:
614 label = self.listwidget.get(index);
616 apply(self.editor, (label,))
618 def deleteItem(self):
619 select = self.listwidget.curselection()
623 index = string.atoi(select[0])
624 label = self.listwidget.get(index);
625 self.listwidget.delete(index)
626 if self.list != None:
628 if self.deletor != None:
629 apply(self.deletor, (label,))
631 def ConfirmQuit(frame, context):
634 text = 'Really quit ' + context + ' without saving?',
636 strings = ('Yes', 'No'),
640 def dispose_window(master, legend, help, savelegend='OK'):
641 dispose = Frame(master, relief=RAISED, bd=5)
642 Label(dispose, text=legend).pack(side=TOP,pady=10)
643 Button(dispose, text=savelegend, fg='blue',
644 command=master.save).pack(side=LEFT)
645 Button(dispose, text='Quit', fg='blue',
646 command=master.nosave).pack(side=LEFT)
647 Button(dispose, text='Help', fg='blue',
648 command=lambda x=help: helpwin(x)).pack(side=RIGHT)
653 # Common methods for Tkinter widgets -- deals with Tkinter declaration
654 def post(self, widgetclass, field):
655 for x in widgetclass.typemap:
656 if x[1] == 'Boolean':
657 setattr(self, x[0], BooleanVar(self))
658 elif x[1] == 'String':
659 setattr(self, x[0], StringVar(self))
661 setattr(self, x[0], IntVar(self))
662 source = getattr(getattr(self, field), x[0])
664 getattr(self, x[0]).set(source)
666 def fetch(self, widgetclass, field):
667 for x in widgetclass.typemap:
668 setattr(getattr(self, field), x[0], getattr(self, x[0]).get())
671 # First, code to set the global fetchmail run controls.
674 configure_novice_help = {
675 'title' : 'Fetchmail novice configurator help',
676 'banner': 'Novice configurator help',
678 In the `Novice Configurator Controls' panel, you can:
680 Press `Save' to save the new fetchmail configuration you have created.
681 Press `Quit' to exit without saving.
682 Press `Help' to bring up this help message.
684 In the `Novice Configuration' panels, you will set up the basic data
685 needed to create a simple fetchmail setup. These include:
687 1. The name of the remote site you want to query.
689 2. Your login name on that site.
691 3. Your password on that site.
693 4. A protocol to use (POP, IMAP, ETRN, etc.)
695 5. A poll interval in seconds.
696 If 0, fetchmail will run in the foreground once when started.
697 If > 0, fetchmail will run in the background and start a new poll
698 cycle after the interval has elapsed.
700 6. Options to fetch old messages as well as new, or to suppress
701 deletion of fetched message.
703 The novice-configuration code will assume that you want to forward mail
704 to a local sendmail listener with no special options.
707 configure_expert_help = {
708 'title' : 'Fetchmail expert configurator help',
709 'banner': 'Expert configurator help',
711 In the `Expert Configurator Controls' panel, you can:
713 Press `Save' to save the new fetchmail configuration you have edited.
714 Press `Quit' to exit without saving.
715 Press `Help' to bring up this help message.
717 In the `Run Controls' panel, you can set the following options that
718 control how fetchmail runs:
721 Number of seconds to wait between polls in the background.
722 If zero, fetchmail will run in foreground.
725 If empty, emit progress and error messages to stderr.
726 Otherwise this gives the name of the files to write to.
727 This field is ignored if the "Log to syslog?" option is on.
730 If empty, store seen-message IDs in .fetchids under user's home
731 directory. If nonempty, use given file name.
734 Who to send multidrop mail to as a last resort if no address can
735 be matched. Normally empty; in this case, fetchmail treats the
736 invoking user as the address of last resort unless that user is
737 root. If that user is root, fetchmail sends to `postmaster'.
740 If this option is on (the default) error mail goes to the sender.
741 Otherwise it goes to the postmaster.
744 If this option is on, spam bounces are sent to the sender or
745 postmaster (depending on the "Bounces to sender?" option. Otherwise,
746 spam bounces are not sent (the default).
749 If this option is on, permanent delivery errors are treated as
750 temporary, i. e. mail is kept on the upstream server. Useful
751 during testing and after configuration changes, and on by
753 If this option is off, permanent delivery errors delete
754 undeliverable mail from the upstream.
757 If false (the default) fetchmail generates a Received line into
758 each message and generates a HELO from the machine it is running on.
759 If true, fetchmail generates no Received line and HELOs as if it were
762 In the `Remote Mail Configurations' panel, you can:
764 1. Enter the name of a new remote mail server you want fetchmail to query.
766 To do this, simply enter a label for the poll configuration in the
767 `New Server:' box. The label should be a DNS name of the server (unless
768 you are using ssh or some other tunneling method and will fill in the `via'
769 option on the site configuration screen).
771 2. Change the configuration of an existing site.
773 To do this, find the site's label in the listbox and double-click it.
774 This will take you to a site configuration dialogue.
778 class ConfigurationEdit(Frame, MyWidget):
779 def __init__(self, configuration, outfile, master, onexit):
781 self.configuration = configuration
782 self.outfile = outfile
783 self.container = master
785 ConfigurationEdit.mode_to_help = {
786 'novice':configure_novice_help, 'expert':configure_expert_help
789 def server_edit(self, sitename):
790 self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
792 def server_delete(self, sitename):
794 for user in self.subwidgets.keys():
796 del self.configuration[sitename]
800 def edit(self, mode):
802 Frame.__init__(self, self.container)
803 self.master.title('fetchmail ' + self.mode + ' configurator');
804 self.master.iconname('fetchmail ' + self.mode + ' configurator');
805 self.master.protocol('WM_DELETE_WINDOW', self.nosave)
806 self.keepalive = [] # Use this to anchor the PhotoImage object
807 make_icon_window(self, fetchmail_icon)
809 self.post(Configuration, 'configuration')
812 'Configurator ' + self.mode + ' Controls',
813 ConfigurationEdit.mode_to_help[self.mode],
816 gf = Frame(self, relief=RAISED, bd = 5)
818 text='Fetchmail Run Controls',
819 bd=2).pack(side=TOP, pady=10)
824 if self.mode != 'novice':
826 log = LabeledEntry(ff, ' Postmaster:', self.postmaster, '14')
827 log.pack(side=RIGHT, anchor=E)
829 # Set the poll interval
830 de = LabeledEntry(ff, ' Poll interval:', self.poll_interval, '14')
831 de.pack(side=RIGHT, anchor=E)
836 if self.mode != 'novice':
839 {'text':'Bounces to sender?',
840 'variable':self.bouncemail,
841 'relief':GROOVE}).pack(side=LEFT, anchor=W)
846 {'text':'Send spam bounces?',
847 'variable':self.spambounce,
848 'relief':GROOVE}).pack(side=LEFT, anchor=W)
853 {'text':'Treat permanent errors as temporary?',
854 'variable':self.softbounce,
855 'relief':GROOVE}).pack(side=LEFT, anchor=W)
860 {'text':'Log to syslog?',
861 'variable':self.syslog,
862 'relief':GROOVE}).pack(side=LEFT, anchor=W)
863 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
864 log.pack(side=RIGHT, anchor=E)
868 {'text':'Invisible mode?',
869 'variable':self.invisible,
870 'relief':GROOVE}).pack(side=LEFT, anchor=W)
872 log = LabeledEntry(gf, ' Idfile:', self.idfile, '14')
873 log.pack(side=RIGHT, anchor=E)
877 # Expert mode allows us to edit multiple sites
878 lf = Frame(self, relief=RAISED, bd=5)
880 text='Remote Mail Server Configurations',
881 bd=2).pack(side=TOP, pady=10)
882 ListEdit('New Server:',
883 map(lambda x: x.pollname, self.configuration.servers),
884 lambda site, self=self: self.server_edit(site),
885 lambda site, self=self: self.server_delete(site),
890 for sitename in self.subwidgets.keys():
891 self.subwidgets[sitename].destruct()
892 self.master.destroy()
896 if ConfirmQuit(self, self.mode + " configuration editor"):
900 for sitename in self.subwidgets.keys():
901 self.subwidgets[sitename].save()
902 self.fetch(Configuration, 'configuration')
906 elif not os.path.isfile(self.outfile) or Dialog(self,
907 title = 'Overwrite existing run control file?',
908 text = 'Really overwrite existing run control file?',
910 strings = ('Yes', 'No'),
911 default = 1).num == 0:
913 os.rename(self.outfile, self.outfile + "~")
914 # Pre-1.5.2 compatibility...
917 oldumask = os.umask(077)
918 fm = open(self.outfile, 'w')
923 os.chmod(self.outfile, 0600)
924 fm.write("# Configuration created %s by fetchmailconf %s\n" % (time.ctime(time.time()), version))
925 fm.write(`self.configuration`)
931 # Server editing stuff.
934 'title' : 'Remote site help',
935 'banner': 'Remote sites',
937 When you add a site name to the list here,
938 you initialize an entry telling fetchmail
939 how to poll a new site.
941 When you select a sitename (by double-
942 clicking it, or by single-clicking to
943 select and then clicking the Edit button),
944 you will open a window to configure that
949 'title' : 'Server options help',
950 'banner': 'Server Options',
952 The server options screen controls fetchmail
953 options that apply to one of your mailservers.
955 Once you have a mailserver configuration set
956 up as you like it, you can select `OK' to
957 store it in the server list maintained in
958 the main configuration window.
960 If you wish to discard changes to a server
961 configuration, select `Quit'.
965 'title' : 'Run Control help',
966 'banner': 'Run Controls',
968 If the `Poll normally' checkbox is on, the host is polled as part of
969 the normal operation of fetchmail when it is run with no arguments.
970 If it is off, fetchmail will only query this host when it is given as
971 a command-line argument.
973 The `Retrieve Error Policy' specifies how server errors during
974 message retrieval are handled. The default behaviour is to abort the
975 current session. Both the continue and markseen options will skip
976 the message with the error, but continue the session allowing for
977 downloading of subsequent messages. Additionally, the markseen
978 option will mark the skipped message as seen.
980 The `True name of server' box should specify the actual DNS name
981 to query. By default this is the same as the poll name.
983 Normally each host described in the file is queried once each
984 poll cycle. If `Cycles to skip between polls' is greater than 0,
985 that's the number of poll cycles that are skipped between the
986 times this post is actually polled.
988 The `Server timeout' is the number of seconds fetchmail will wait
989 for a reply from the mailserver before concluding it is hung and
994 'title' : 'Protocol and Port help',
995 'banner': 'Protocol and Port',
997 These options control the remote-mail protocol
998 and TCP/IP service port used to query this
1001 If you click the `Probe for supported protocols'
1002 button, fetchmail will try to find you the most
1003 capable server on the selected host (this will
1004 only work if you're conncted to the Internet).
1005 The probe only checks for ordinary IMAP and POP
1006 protocols; fortunately these are the most
1007 frequently supported.
1009 The `Protocol' button bar offers you a choice of
1010 all the different protocols available. The `auto'
1011 protocol is the default mode; it probes the host
1012 ports for POP3 and IMAP to see if either is
1015 Normally the TCP/IP service port to use is
1016 dictated by the protocol choice. The `Service'
1017 field (only present in expert mode) lets you
1018 set a non-standard service (port).
1022 'title' : 'Security option help',
1023 'banner': 'Security',
1025 The 'authorization mode' allows you to choose the
1026 mode that fetchmail uses to log in to your server. You
1027 can usually leave this at 'any', but you will have to pick
1028 'NTLM' and 'MSN' manually for the nonce.
1030 The 'interface' option allows you to specify a range
1031 of IP addresses to monitor for activity. If these
1032 addresses are not active, fetchmail will not poll.
1033 Specifying this may protect you from a spoofing attack
1034 if your client machine has more than one IP gateway
1035 address and some of the gateways are to insecure nets.
1037 The `monitor' option, if given, specifies the only
1038 device through which fetchmail is permitted to connect
1039 to servers. This option may be used to prevent
1040 fetchmail from triggering an expensive dial-out if the
1041 interface is not already active.
1043 The `interface' and `monitor' options are available
1044 only for Linux and freeBSD systems. See the fetchmail
1045 manual page for details on these.
1047 The ssl option enables SSL communication with a mailserver
1048 supporting Secure Sockets Layer. The sslkey and sslcert options
1049 declare key and certificate files for use with SSL.
1050 The sslcertck option enables strict checking of SSL server
1051 certificates (and sslcertpath gives the trusted certificate
1052 directory). The sslcommonname option helps if the server is
1053 misconfigured and returning "Server CommonName mismatch"
1054 warnings. With sslfingerprint, you can specify a finger-
1055 print the server's key is checked against.
1059 'title' : 'Multidrop option help',
1060 'banner': 'Multidrop',
1062 These options are only useful with multidrop mode.
1063 See the manual page for extended discussion.
1067 'title' : 'User list help',
1068 'banner': 'User list',
1070 When you add a user name to the list here,
1071 you initialize an entry telling fetchmail
1072 to poll the site on behalf of the new user.
1074 When you select a username (by double-
1075 clicking it, or by single-clicking to
1076 select and then clicking the Edit button),
1077 you will open a window to configure the
1078 user's options on that site.
1081 class ServerEdit(Frame, MyWidget):
1082 def __init__(self, host, parent):
1083 self.parent = parent
1085 self.subwidgets = {}
1086 for site in parent.configuration.servers:
1087 if site.pollname == host:
1089 if (self.server == None):
1090 self.server = Server()
1091 self.server.pollname = host
1092 self.server.via = None
1093 parent.configuration.servers.append(self.server)
1095 def edit(self, mode, master=None):
1096 Frame.__init__(self, master)
1098 self.master.title('Fetchmail host ' + self.server.pollname);
1099 self.master.iconname('Fetchmail host ' + self.server.pollname);
1100 self.post(Server, 'server')
1101 self.makeWidgets(self.server.pollname, mode)
1102 self.keepalive = [] # Use this to anchor the PhotoImage object
1103 make_icon_window(self, fetchmail_icon)
1106 # self.wait_window()
1110 for username in self.subwidgets.keys():
1111 self.subwidgets[username].destruct()
1112 del self.parent.subwidgets[self.server.pollname]
1113 self.master.destroy()
1116 if ConfirmQuit(self, 'server option editing'):
1120 self.fetch(Server, 'server')
1121 for username in self.subwidgets.keys():
1122 self.subwidgets[username].save()
1125 def defaultPort(self):
1126 proto = self.protocol.get()
1127 # Callback to reset the port number whenever the protocol type changes.
1128 # We used to only reset the port if it had a default (zero) value.
1129 # This turns out to be a bad idea especially in Novice mode -- if
1130 # you set POP3 and then set IMAP, the port invisibly remained 110.
1131 # Now we reset unconditionally on the theory that if you're setting
1132 # a custom port number you should be in expert mode and playing
1133 # close enough attention to notice this...
1134 self.service.set(defaultports[proto])
1135 if not proto in ("POP3", "APOP", "KPOP"): self.uidl.state = DISABLED
1137 def user_edit(self, username, mode):
1138 self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1140 def user_delete(self, username):
1141 if self.subwidgets.has_key(username):
1142 self.subwidgets[username].destruct()
1143 del self.server[username]
1145 def makeWidgets(self, host, mode):
1146 topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1148 leftwin = Frame(self);
1151 if mode != 'novice':
1152 ctlwin = Frame(leftwin, relief=RAISED, bd=5)
1153 Label(ctlwin, text="Run Controls").pack(side=TOP)
1154 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
1155 Checkbutton(ctlwin, text='Pass messages with bad headers?',
1156 variable=self.badheader).pack(side=TOP)
1157 retrieveerrorlist = ['abort', 'continue', 'markseen']
1158 Label(ctlwin, text="Retrieve Error Policy").pack(side=TOP)
1159 ButtonBar(ctlwin, '', self.retrieveerror, retrieveerrorlist, 1, None)
1160 LabeledEntry(ctlwin, 'True name of ' + host + ':',
1161 self.via, leftwidth).pack(side=TOP, fill=X)
1162 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
1163 self.interval, leftwidth).pack(side=TOP, fill=X)
1164 LabeledEntry(ctlwin, 'Server timeout (seconds):',
1165 self.timeout, leftwidth).pack(side=TOP, fill=X)
1166 Button(ctlwin, text='Help', fg='blue',
1167 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
1170 # Compute the available protocols from the compile-time options
1171 protolist = ['auto']
1172 if 'pop2' in feature_options:
1173 protolist.append("POP2")
1174 if 'pop3' in feature_options:
1175 protolist = protolist + ["POP3", "APOP", "KPOP"]
1176 if 'sdps' in feature_options:
1177 protolist.append("SDPS")
1178 if 'imap' in feature_options:
1179 protolist.append("IMAP")
1180 if 'etrn' in feature_options:
1181 protolist.append("ETRN")
1182 if 'odmr' in feature_options:
1183 protolist.append("ODMR")
1185 protwin = Frame(leftwin, relief=RAISED, bd=5)
1186 Label(protwin, text="Protocol").pack(side=TOP)
1187 ButtonBar(protwin, '',
1188 self.protocol, protolist, 2,
1190 if mode != 'novice':
1191 LabeledEntry(protwin, 'On server TCP/IP service:',
1192 self.service, leftwidth).pack(side=TOP, fill=X)
1194 Checkbutton(protwin,
1195 text="POP3: track `seen' with client-side UIDLs?",
1196 variable=self.uidl).pack(side=TOP)
1197 Button(protwin, text='Probe for supported protocols', fg='blue',
1198 command=self.autoprobe).pack(side=LEFT)
1199 Button(protwin, text='Help', fg='blue',
1200 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
1201 protwin.pack(fill=X)
1203 userwin = Frame(leftwin, relief=RAISED, bd=5)
1204 Label(userwin, text="User entries for " + host).pack(side=TOP)
1205 ListEdit("New user: ",
1206 map(lambda x: x.remote, self.server.users),
1207 lambda u, m=mode, s=self: s.user_edit(u, m),
1208 lambda u, s=self: s.user_delete(u),
1210 userwin.pack(fill=X)
1212 leftwin.pack(side=LEFT, anchor=N, fill=X);
1214 if mode != 'novice':
1215 rightwin = Frame(self);
1217 mdropwin = Frame(rightwin, relief=RAISED, bd=5)
1218 Label(mdropwin, text="Multidrop options").pack(side=TOP)
1219 LabeledEntry(mdropwin, 'Envelope address header:',
1220 self.envelope, '22').pack(side=TOP, fill=X)
1221 LabeledEntry(mdropwin, 'Envelope headers to skip:',
1222 self.envskip, '22').pack(side=TOP, fill=X)
1223 LabeledEntry(mdropwin, 'Name prefix to strip:',
1224 self.qvirtual, '22').pack(side=TOP, fill=X)
1225 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
1226 variable=self.dns).pack(side=TOP)
1227 Label(mdropwin, text="DNS aliases").pack(side=TOP)
1228 ListEdit("New alias: ", self.server.aka, None, None, mdropwin, None)
1229 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
1230 ListEdit("New domain: ",
1231 self.server.localdomains, None, None, mdropwin, multihelp)
1232 mdropwin.pack(fill=X)
1234 if os_type in ('linux', 'freebsd'):
1235 secwin = Frame(rightwin, relief=RAISED, bd=5)
1236 Label(secwin, text="Security").pack(side=TOP)
1237 # Don't actually let users set this. KPOP sets it implicitly
1238 ButtonBar(secwin, 'Authorization mode:',
1239 self.auth, authlist, 2, None).pack(side=TOP)
1240 if os_type == 'linux' or os_type == 'freebsd' or 'interface' in dictmembers:
1241 LabeledEntry(secwin, 'IP range to check before poll:',
1242 self.interface, leftwidth).pack(side=TOP, fill=X)
1243 if os_type == 'linux' or os_type == 'freebsd' or 'monitor' in dictmembers:
1244 LabeledEntry(secwin, 'Interface to monitor:',
1245 self.monitor, leftwidth).pack(side=TOP, fill=X)
1246 # Someday this should handle Kerberos 5 too
1247 if 'kerberos' in feature_options:
1248 LabeledEntry(secwin, 'Principal:',
1249 self.principal, '12').pack(side=TOP, fill=X)
1250 # ESMTP authentication
1251 LabeledEntry(secwin, 'ESMTP name:',
1252 self.esmtpname, '12').pack(side=TOP, fill=X)
1253 LabeledEntry(secwin, 'ESMTP password:',
1254 self.esmtppassword, '12').pack(side=TOP, fill=X)
1255 Button(secwin, text='Help', fg='blue',
1256 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
1259 rightwin.pack(side=LEFT, anchor=N);
1261 def autoprobe(self):
1262 # Note: this only handles case (1) near fetchmail.c:1032
1263 # We're assuming people smart enough to set up ssh tunneling
1264 # won't need autoprobing.
1266 realhost = self.server.via
1268 realhost = self.server.pollname
1270 for protocol in ("IMAP","POP3","POP2"):
1271 service = defaultports[protocol]
1272 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1274 sock.connect((realhost, ianaservices[service]))
1275 greetline = sock.recv(1024)
1281 confwin = Toplevel()
1282 if greetline == None:
1283 title = "Autoprobe of " + realhost + " failed"
1285 Fetchmailconf didn't find any mailservers active.
1286 This could mean the host doesn't support any,
1287 or that your Internet connection is down, or
1288 that the host is so slow that the probe timed
1289 out before getting a response.
1293 # OK, now try to recognize potential problems
1295 if protocol == "POP2":
1296 warnings = warnings + """
1297 It appears you have somehow found a mailserver running only POP2.
1298 Congratulations. Have you considered a career in archaeology?
1300 Unfortunately, stock fetchmail binaries don't include POP2 support anymore.
1301 Unless the first line of your fetchmail -V output includes the string "POP2",
1302 you'll have to build it from sources yourself with the configure
1303 switch --enable-POP2.
1307 ### POP3 servers start here
1309 if string.find(greetline, "1.003") > 0 or string.find(greetline, "1.004") > 0:
1310 warnings = warnings + """
1311 This appears to be an old version of the UC Davis POP server. These are
1312 dangerously unreliable (among other problems, they may drop your mailbox
1313 on the floor if your connection is interrupted during the session).
1315 It is strongly recommended that you find a better POP3 server. The fetchmail
1316 FAQ includes pointers to good ones.
1319 if string.find(greetline, "comcast.net") > 0:
1320 warnings = warnings + """
1321 The Comcast Maillennium POP3 server only returns the first 80K of a long
1322 message retrieved with TOP. Its response to RETR is normal, so use the
1326 # Steve VanDevender <stevev@efn.org> writes:
1327 # The only system I have seen this happen with is cucipop-1.31
1328 # under SunOS 4.1.4. cucipop-1.31 runs fine on at least Solaris
1329 # 2.x and probably quite a few other systems. It appears to be a
1330 # bug or bad interaction with the SunOS realloc() -- it turns out
1331 # that internally cucipop does allocate a certain data structure in
1332 # multiples of 16, using realloc() to bump it up to the next
1333 # multiple if it needs more.
1335 # The distinctive symptom is that when there are 16 messages in the
1336 # inbox, you can RETR and DELE all 16 messages successfully, but on
1337 # QUIT cucipop returns something like "-ERR Error locking your
1338 # mailbox" and aborts without updating it.
1340 # The cucipop banner looks like:
1342 # +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1344 if string.find(greetline, "Cubic Circle") > 0:
1345 warnings = warnings + """
1346 I see your server is running cucipop. Better make sure the server box
1347 isn't a SunOS 4.1.4 machine; cucipop tickles a bug in SunOS realloc()
1348 under that version, and doesn't cope with the result gracefully. Newer
1349 SunOS and Solaris machines run cucipop OK.
1351 Also, some versions of cucipop don't assert an exclusive lock on your
1352 mailbox when it's being queried. This means that if you have more than
1353 one fetchmail query running against the same mailbox, bad things can happen.
1355 if string.find(greetline, "David POP3 Server") > 0:
1356 warnings = warnings + """
1357 This POP3 server is badly broken. You should get rid of it -- and the
1358 brain-dead Microsoft operating system it rode in on.
1361 # The greeting line on the server known to be buggy is:
1362 # +OK POP3 server ready (running FTGate V2, 2, 1, 0 Jun 21 1999 09:55:01)
1364 if string.find(greetline, "FTGate") > 0:
1365 warnings = warnings + """
1366 This POP server has a weird bug; it says OK twice in response to TOP.
1367 Its response to RETR is normal, so use the `fetchall' option.
1370 if string.find(greetline, " geonet.de") > 0:
1371 warnings = warnings + """
1372 You appear to be using geonet. As of late 2002, the TOP command on
1373 geonet's POP3 is broken. Use the fetchall option.
1376 if string.find(greetline, "OpenMail") > 0:
1377 warnings = warnings + """
1378 You appear to be using some version of HP OpenMail. Many versions of
1379 OpenMail do not process the "TOP" command correctly; the symptom is that
1380 only the header and first line of each message is retrieved. To work
1381 around this bug, turn on `fetchall' on all user entries associated with
1385 if string.find(greetline, "Escape character is") > 0:
1386 warnings = warnings + """
1387 Your greeting line looks like it was written by a fetid pile of
1388 camel dung identified to me as `popa3d written by Solar Designer'.
1389 Beware! The UIDL support in this thing is known to be completely broken,
1390 and other things probably are too.
1393 if string.find(greetline, "MercuryP/NLM v1.48") > 0:
1394 warnings = warnings + """
1395 This is not a POP3 server. It has delusions of being one, but after
1396 RETR all messages are automatically marked to be deleted. The only
1397 way to prevent this is to issue an RSET before leaving the server.
1398 Fetchmail does this, but we suspect this is probably broken in lots
1402 if string.find(greetline, "POP-Max") > 0:
1403 warnings = warnings + """
1404 The Mail Max POP3 server screws up on mail with attachments. It
1405 reports the message size with attachments included, but doesn't
1406 download them on a RETR or TOP (this violates the IMAP RFCs). It also
1407 doesn't implement TOP correctly. You should get rid of it -- and the
1408 brain-dead NT server it rode in on.
1411 if string.find(greetline, "POP3 Server Ready") > 0:
1412 warnings = warnings + """
1413 Some server that uses this greeting line has been observed to choke on
1414 TOP %d 99999999. Use the fetchall option. if necessary, to force RETR.
1417 if string.find(greetline, "QPOP") > 0:
1418 warnings = warnings + """
1419 This appears to be a version of Eudora qpopper. That's good. Fetchmail
1420 knows all about qpopper. However, be aware that the 2.53 version of
1421 qpopper does something odd that causes fetchmail to hang with a socket
1422 error on very large messages. This is probably not a fetchmail bug, as
1423 it has been observed with fetchpop. The fix is to upgrade to qpopper
1424 3.0beta or a more recent version. Better yet, switch to IMAP.
1427 if string.find(greetline, " sprynet.com") > 0:
1428 warnings = warnings + """
1429 You appear to be using a SpryNet server. In mid-1999 it was reported that
1430 the SpryNet TOP command marks messages seen. Therefore, for proper error
1431 recovery in the event of a line drop, it is strongly recommended that you
1432 turn on `fetchall' on all user entries associated with this server.
1435 if string.find(greetline, "TEMS POP3") > 0:
1436 warnings = warnings + """
1437 Your POP3 server has "TEMS" in its header line. At least one such
1438 server does not process the "TOP" command correctly; the symptom is
1439 that fetchmail hangs when trying to retrieve mail. To work around
1440 this bug, turn on `fetchall' on all user entries associated with this
1444 if string.find(greetline, " spray.se") > 0:
1445 warnings = warnings + """
1446 Your POP3 server has "spray.se" in its header line. In May 2000 at
1447 least one such server did not process the "TOP" command correctly; the
1448 symptom is that messages are treated as headerless. To work around
1449 this bug, turn on `fetchall' on all user entries associated with this
1453 if string.find(greetline, " usa.net") > 0:
1454 warnings = warnings + """
1455 You appear to be using USA.NET's free mail service. Their POP3 servers
1456 (at least as of the 2.2 version in use mid-1998) are quite flaky, but
1457 fetchmail can compensate. They seem to require that fetchall be switched on
1458 (otherwise you won't necessarily see all your mail, not even new mail).
1459 They also botch the TOP command the fetchmail normally uses for retrieval
1460 (it only retrieves about 10 lines rather than the number specified).
1461 Turning on fetchall will disable the use of TOP.
1463 Therefore, it is strongly recommended that you turn on `fetchall' on all
1464 user entries associated with this server.
1467 if string.find(greetline, " Novonyx POP3") > 0:
1468 warnings = warnings + """
1469 Your mailserver is running Novonyx POP3. This server, at least as of
1470 version 2.17, seems to have problems handling and reporting seen bits.
1471 You may have to use the fetchall option.
1474 if string.find(greetline, " IMS POP3") > 0:
1475 warnings = warnings + """
1476 Some servers issuing the greeting line 'IMS POP3' have been known to
1477 do byte-stuffing incorrectly. This means that if a message you receive
1478 has a . (period) at start of line, fetchmail will become confused and
1479 probably wedge itself. (This bug was recorded on IMS POP3 0.86.)
1483 ### IMAP servers start here
1485 if string.find(greetline, "GroupWise") > 0:
1486 warnings = warnings + """
1487 The Novell GroupWise IMAP server would be better named GroupFoolish;
1488 it is (according to the designer of IMAP) unusably broken. Among
1489 other things, it doesn't include a required content length in its
1490 BODY[TEXT] response.<p>
1492 Fetchmail works around this problem, but we strongly recommend voting
1493 with your dollars for a server that isn't brain-dead. If you stick
1494 with code as shoddy as GroupWise seems to be, you will probably pay
1495 for it with other problems.<p>
1498 if string.find(greetline, "InterChange") > 0:
1499 warnings = warnings + """
1501 The InterChange IMAP server at release levels below 3.61.08 screws up
1502 on mail with attachments. It doesn't fetch them if you give it a
1503 BODY[TEXT] request, though it does if you request RFC822.TEXT.
1504 According to the IMAP RFCs and their maintainer these should be
1505 equivalent -- and we can't drop the BODY[TEXT] form because M$
1506 Exchange (quite legally under RFC2062) rejectsit. The InterChange
1507 folks claim to have fixed this bug in 3.61.08.
1510 if string.find(greetline, "Imail") > 0:
1511 warnings = warnings + """
1512 We've seen a bug report indicating that this IMAP server (at least as of
1513 version 5.0.7) returns an invalid body size for messages with MIME
1514 attachments; the effect is to drop the attachments on the floor. We
1515 recommend you upgrade to a non-broken IMAP server.
1518 if string.find(greetline, "Domino IMAP4") > 0:
1519 warnings = warnings + """
1520 Your IMAP server appears to be Lotus Domino. This server, at least up
1521 to version 4.6.2a, has a bug in its generation of MIME boundaries (see
1522 the details in the fetchmail FAQ). As a result, even MIME aware MUAs
1523 will see attachments as part of the message text. If your Domino server's
1524 POP3 facility is enabled, we recommend you fall back on it.
1528 ### Checks for protocol variants start here
1530 closebrak = string.find(greetline, ">")
1531 if closebrak > 0 and greetline[closebrak+1] == "\r":
1532 warnings = warnings + """
1533 It looks like you could use APOP on this server and avoid sending it your
1534 password in clear. You should talk to the mailserver administrator about
1538 if string.find(greetline, "IMAP2bis") > 0:
1539 warnings = warnings + """
1540 IMAP2bis servers have a minor problem; they can't peek at messages without
1541 marking them seen. If you take a line hit during the retrieval, the
1542 interrupted message may get left on the server, marked seen.
1544 To work around this, it is recommended that you set the `fetchall'
1545 option on all user entries associated with this server, so any stuck
1546 mail will be retrieved next time around.
1548 To fix this bug, upgrade to an IMAP4 server. The fetchmail FAQ includes
1549 a pointer to an open-source implementation.
1552 if string.find(greetline, "IMAP4rev1") > 0:
1553 warnings = warnings + """
1554 I see an IMAP4rev1 server. Excellent. This is (a) the best kind of
1555 remote-mail server, and (b) the one the fetchmail author uses. Fetchmail
1556 has therefore been extremely well tested with this class of server.
1560 warnings = warnings + """
1561 Fetchmail doesn't know anything special about this server type.
1565 # Display success window with warnings
1566 title = "Autoprobe of " + realhost + " succeeded"
1567 confirm = "The " + protocol + " server said:\n\n" + greetline + warnings
1568 self.protocol.set(protocol)
1569 self.service.set(defaultports[protocol])
1570 confwin.title(title)
1571 confwin.iconname(title)
1572 Label(confwin, text=title).pack()
1573 Message(confwin, text=confirm, width=600).pack()
1574 Button(confwin, text='Done',
1575 command=lambda x=confwin: x.destroy(), bd=2).pack()
1578 # User editing stuff
1582 'title' : 'User option help',
1583 'banner': 'User options',
1585 You may use this panel to set options
1586 that may differ between individual
1589 Once you have a user configuration set
1590 up as you like it, you can select `OK' to
1591 store it in the user list maintained in
1592 the site configuration window.
1594 If you wish to discard the changes you have
1595 made to user options, select `Quit'.
1599 'title' : 'Local name help',
1600 'banner': 'Local names',
1602 The local name(s) in a user entry are the
1603 people on the client machine who should
1604 receive mail from the poll described.
1606 Note: if a user entry has more than one
1607 local name, messages will be retrieved
1608 in multidrop mode. This complicates
1609 the configuration issues; see the manual
1610 page section on multidrop mode.
1612 Warning: Be careful with local names
1613 such as foo@bar.com, as that can cause
1614 the mail to be sent to foo@bar.com instead
1615 of sending it to your local system.
1618 class UserEdit(Frame, MyWidget):
1619 def __init__(self, username, parent):
1620 self.parent = parent
1622 for user in parent.server.users:
1623 if user.remote == username:
1625 if self.user == None:
1627 self.user.remote = username
1628 self.user.localnames = [username]
1629 parent.server.users.append(self.user)
1631 def edit(self, mode, master=None):
1632 Frame.__init__(self, master)
1634 self.master.title('Fetchmail user ' + self.user.remote
1635 + ' querying ' + self.parent.server.pollname);
1636 self.master.iconname('Fetchmail user ' + self.user.remote);
1637 self.post(User, 'user')
1638 self.makeWidgets(mode, self.parent.server.pollname)
1639 self.keepalive = [] # Use this to anchor the PhotoImage object
1640 make_icon_window(self, fetchmail_icon)
1643 # self.wait_window()
1647 # Yes, this test can fail -- if you delete the parent window.
1648 if self.parent.subwidgets.has_key(self.user.remote):
1649 del self.parent.subwidgets[self.user.remote]
1650 self.master.destroy()
1653 if ConfirmQuit(self, 'user option editing'):
1658 for x in self.user.localnames: ok = ok + (string.find(x, '@') != -1)
1659 if ok == 0 or Dialog(self,
1660 title = "Really accept an embedded '@' ?",
1661 text = "Local names with an embedded '@', such as in foo@bar "
1662 "might result in your mail being sent to foo@bar.com "
1663 "instead of your local system.\n Are you sure you want "
1664 "a local user name with an '@' in it?",
1665 bitmap = 'question',
1666 strings = ('Yes', 'No'),
1667 default = 1).num == 0:
1668 self.fetch(User, 'user')
1671 def makeWidgets(self, mode, servername):
1672 dispose_window(self,
1673 "User options for " + self.user.remote + " querying " + servername,
1676 if mode != 'novice':
1677 leftwin = Frame(self);
1681 secwin = Frame(leftwin, relief=RAISED, bd=5)
1682 Label(secwin, text="Authentication").pack(side=TOP)
1683 LabeledEntry(secwin, 'Password:',
1684 self.password, '12').pack(side=TOP, fill=X)
1685 secwin.pack(fill=X, anchor=N)
1687 if 'ssl' in feature_options or 'ssl' in dictmembers:
1688 sslwin = Frame(leftwin, relief=RAISED, bd=5)
1689 Checkbutton(sslwin, text="Use SSL?",
1690 variable=self.ssl).pack(side=TOP, fill=X)
1691 LabeledEntry(sslwin, 'SSL key:',
1692 self.sslkey, '14').pack(side=TOP, fill=X)
1693 LabeledEntry(sslwin, 'SSL certificate:',
1694 self.sslcert, '14').pack(side=TOP, fill=X)
1695 Checkbutton(sslwin, text="Check server SSL certificate?",
1696 variable=self.sslcertck).pack(side=TOP, fill=X)
1697 LabeledEntry(sslwin, 'SSL trusted certificate directory:',
1698 self.sslcertpath, '14').pack(side=TOP, fill=X)
1699 LabeledEntry(sslwin, 'SSL CommonName:',
1700 self.sslcommonname, '14').pack(side=TOP, fill=X)
1701 LabeledEntry(sslwin, 'SSL key fingerprint:',
1702 self.sslfingerprint, '14').pack(side=TOP, fill=X)
1703 sslwin.pack(fill=X, anchor=N)
1705 names = Frame(leftwin, relief=RAISED, bd=5)
1706 Label(names, text="Local names").pack(side=TOP)
1707 ListEdit("New name: ",
1708 self.user.localnames, None, None, names, localhelp)
1709 names.pack(fill=X, anchor=N)
1711 if mode != 'novice':
1712 targwin = Frame(leftwin, relief=RAISED, bd=5)
1713 Label(targwin, text="Forwarding Options").pack(side=TOP)
1714 Label(targwin, text="Listeners to forward to").pack(side=TOP)
1715 ListEdit("New listener:",
1716 self.user.smtphunt, None, None, targwin, None)
1717 Label(targwin, text="Domains to fetch from (ODMR/ETRN only)").pack(side=TOP)
1718 ListEdit("Domains:",
1719 self.user.fetchdomains, None, None, targwin, None)
1720 LabeledEntry(targwin, 'Use domain on RCPT TO line:',
1721 self.smtpaddress, '26').pack(side=TOP, fill=X)
1722 LabeledEntry(targwin, 'Set fixed RCPT TO address:',
1723 self.smtpname, '26').pack(side=TOP, fill=X)
1724 LabeledEntry(targwin, 'Connection setup command:',
1725 self.preconnect, '26').pack(side=TOP, fill=X)
1726 LabeledEntry(targwin, 'Connection wrapup command:',
1727 self.postconnect, '26').pack(side=TOP, fill=X)
1728 LabeledEntry(targwin, 'Local delivery agent:',
1729 self.mda, '26').pack(side=TOP, fill=X)
1730 LabeledEntry(targwin, 'BSMTP output file:',
1731 self.bsmtp, '26').pack(side=TOP, fill=X)
1732 LabeledEntry(targwin, 'Listener spam-block codes:',
1733 self.antispam, '26').pack(side=TOP, fill=X)
1734 LabeledEntry(targwin, 'Pass-through properties:',
1735 self.properties, '26').pack(side=TOP, fill=X)
1736 Checkbutton(targwin, text="Use LMTP?",
1737 variable=self.lmtp).pack(side=TOP, fill=X)
1738 targwin.pack(fill=X, anchor=N)
1740 if mode != 'novice':
1741 leftwin.pack(side=LEFT, fill=X, anchor=N)
1742 rightwin = Frame(self)
1746 optwin = Frame(rightwin, relief=RAISED, bd=5)
1747 Label(optwin, text="Processing Options").pack(side=TOP)
1748 Checkbutton(optwin, text="Suppress deletion of messages after reading",
1749 variable=self.keep).pack(side=TOP, anchor=W)
1750 Checkbutton(optwin, text="Fetch old messages as well as new",
1751 variable=self.fetchall).pack(side=TOP, anchor=W)
1752 if mode != 'novice':
1753 Checkbutton(optwin, text="Flush seen messages before retrieval",
1754 variable=self.flush).pack(side=TOP, anchor=W)
1755 Checkbutton(optwin, text="Flush oversized messages before retrieval",
1756 variable=self.limitflush).pack(side=TOP, anchor=W)
1757 Checkbutton(optwin, text="Rewrite To/Cc/Bcc messages to enable reply",
1758 variable=self.rewrite).pack(side=TOP, anchor=W)
1759 Checkbutton(optwin, text="Force CR/LF at end of each line",
1760 variable=self.forcecr).pack(side=TOP, anchor=W)
1761 Checkbutton(optwin, text="Strip CR from end of each line",
1762 variable=self.stripcr).pack(side=TOP, anchor=W)
1763 Checkbutton(optwin, text="Pass 8 bits even though SMTP says 7BIT",
1764 variable=self.pass8bits).pack(side=TOP, anchor=W)
1765 Checkbutton(optwin, text="Undo MIME armoring on header and body",
1766 variable=self.mimedecode).pack(side=TOP, anchor=W)
1767 Checkbutton(optwin, text="Drop Status lines from forwarded messages",
1768 variable=self.dropstatus).pack(side=TOP, anchor=W)
1769 Checkbutton(optwin, text="Drop Delivered-To lines from forwarded messages",
1770 variable=self.dropdelivered).pack(side=TOP, anchor=W)
1773 if mode != 'novice':
1774 limwin = Frame(rightwin, relief=RAISED, bd=5)
1775 Label(limwin, text="Resource Limits").pack(side=TOP)
1776 LabeledEntry(limwin, 'Message size limit:',
1777 self.limit, '30').pack(side=TOP, fill=X)
1778 LabeledEntry(limwin, 'Size warning interval:',
1779 self.warnings, '30').pack(side=TOP, fill=X)
1780 LabeledEntry(limwin, 'Max messages to fetch per poll:',
1781 self.fetchlimit, '30').pack(side=TOP, fill=X)
1782 LabeledEntry(limwin, 'Max message sizes to fetch per transaction:',
1783 self.fetchsizelimit, '30').pack(side=TOP, fill=X)
1784 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1785 LabeledEntry(limwin, 'Use fast UIDL:',
1786 self.fastuidl, '30').pack(side=TOP, fill=X)
1787 LabeledEntry(limwin, 'Max messages to forward per poll:',
1788 self.batchlimit, '30').pack(side=TOP, fill=X)
1789 if self.parent.server.protocol not in ('ETRN', 'ODMR'):
1790 LabeledEntry(limwin, 'Interval between expunges:',
1791 self.expunge, '30').pack(side=TOP, fill=X)
1792 Checkbutton(limwin, text="Idle after each poll (IMAP only)",
1793 variable=self.idle).pack(side=TOP, anchor=W)
1796 if self.parent.server.protocol == 'IMAP':
1797 foldwin = Frame(rightwin, relief=RAISED, bd=5)
1798 Label(foldwin, text="Remote folders (IMAP only)").pack(side=TOP)
1799 ListEdit("New folder:", self.user.mailboxes,
1800 None, None, foldwin, None)
1801 foldwin.pack(fill=X, anchor=N)
1803 if mode != 'novice':
1804 rightwin.pack(side=LEFT)
1810 # Top-level window that offers either novice or expert mode
1811 # (but not both at once; it disappears when one is selected).
1814 class Configurator(Frame):
1815 def __init__(self, outfile, master, onexit, parent):
1816 Frame.__init__(self, master)
1817 self.outfile = outfile
1818 self.onexit = onexit
1819 self.parent = parent
1820 self.master.title('fetchmail configurator');
1821 self.master.iconname('fetchmail configurator');
1823 self.keepalive = [] # Use this to anchor the PhotoImage object
1824 make_icon_window(self, fetchmail_icon)
1826 Message(self, text="""
1827 Use `Novice Configuration' for basic fetchmail setup;
1828 with this, you can easily set up a single-drop connection
1829 to one remote mail server.
1830 """, width=600).pack(side=TOP)
1831 Button(self, text='Novice Configuration',
1832 fg='blue', command=self.novice).pack()
1834 Message(self, text="""
1835 Use `Expert Configuration' for advanced fetchmail setup,
1836 including multiple-site or multidrop connections.
1837 """, width=600).pack(side=TOP)
1838 Button(self, text='Expert Configuration',
1839 fg='blue', command=self.expert).pack()
1841 Message(self, text="""
1842 Or you can just select `Quit' to leave the configurator now and
1843 return to the main panel.
1844 """, width=600).pack(side=TOP)
1845 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1846 master.protocol("WM_DELETE_WINDOW", self.leave)
1849 self.master.destroy()
1850 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1853 self.master.destroy()
1854 ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1857 self.master.destroy()
1860 # Run a command in a scrolling text widget, displaying its output
1862 class RunWindow(Frame):
1863 def __init__(self, command, master, parent):
1864 Frame.__init__(self, master)
1865 self.master = master
1866 self.master.title('fetchmail run window');
1867 self.master.iconname('fetchmail run window');
1870 text="Running "+command,
1871 bd=2).pack(side=TOP, pady=10)
1872 self.keepalive = [] # Use this to anchor the PhotoImage object
1873 make_icon_window(self, fetchmail_icon)
1875 # This is a scrolling text window
1876 textframe = Frame(self)
1877 scroll = Scrollbar(textframe)
1878 self.textwidget = Text(textframe, setgrid=TRUE)
1879 textframe.pack(side=TOP, expand=YES, fill=BOTH)
1880 self.textwidget.config(yscrollcommand=scroll.set)
1881 self.textwidget.pack(side=LEFT, expand=YES, fill=BOTH)
1882 scroll.config(command=self.textwidget.yview)
1883 scroll.pack(side=RIGHT, fill=BOTH)
1884 textframe.pack(side=TOP)
1886 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1888 self.update() # Draw widget before executing fetchmail
1890 # Always look for a runnable command in the directory we're running in
1891 # first. This avoids some obscure version-skew errors that can occur
1892 # if you pick up an old fetchmail from the standard system locations.
1893 os.environ["PATH"] = os.path.dirname(sys.argv[0]) + ":" + os.environ["PATH"]
1894 child_stdout = os.popen(command + " 2>&1 </dev/null", "r")
1896 ch = child_stdout.read(1)
1899 self.textwidget.insert(END, ch)
1900 self.textwidget.insert(END, "Done.")
1901 self.textwidget.see(END);
1904 self.master.destroy()
1906 # Here's where we choose either configuration or launching
1908 class MainWindow(Frame):
1909 def __init__(self, outfile, master=None):
1910 Frame.__init__(self, master)
1911 self.outfile = outfile
1912 self.master.title('fetchmail launcher');
1913 self.master.iconname('fetchmail launcher');
1916 text='Fetchmailconf ' + version,
1917 bd=2).pack(side=TOP, pady=10)
1918 self.keepalive = [] # Use this to anchor the PhotoImage object
1919 make_icon_window(self, fetchmail_icon)
1922 ## Test icon display with the following:
1923 # icon_image = PhotoImage(data=fetchmail_icon)
1924 # Label(self, image=icon_image).pack(side=TOP, pady=10)
1925 # self.keepalive.append(icon_image)
1927 Message(self, text="""
1928 Use `Configure fetchmail' to tell fetchmail about the remote
1929 servers it should poll (the host name, your username there,
1930 whether to use POP or IMAP, and so forth).
1931 """, width=600).pack(side=TOP)
1932 self.configbutton = Button(self, text='Configure fetchmail',
1933 fg='blue', command=self.configure)
1934 self.configbutton.pack()
1936 Message(self, text="""
1937 Use `Run fetchmail' to run fetchmail with debugging enabled.
1938 This is a good way to test out a new configuration.
1939 """, width=600).pack(side=TOP)
1940 Button(self, text='Run fetchmail',fg='blue', command=self.test).pack()
1942 Message(self, text="""
1943 Use `Run fetchmail' to run fetchmail in foreground.
1944 Progress messages will be shown, but not debug messages.
1945 """, width=600).pack(side=TOP)
1946 Button(self, text='Run fetchmail', fg='blue', command=self.run).pack()
1948 Message(self, text="""
1949 Or you can just select `Quit' to exit the launcher now.
1950 """, width=600).pack(side=TOP)
1951 Button(self, text='Quit', fg='blue', command=self.leave).pack()
1953 def configure(self):
1954 self.configbutton.configure(state=DISABLED)
1955 Configurator(self.outfile, Toplevel(),
1956 lambda self=self: self.configbutton.configure(state=NORMAL),
1959 cmd = "fetchmail -N -d0 --nosyslog -v"
1961 cmd = cmd + " -f " + rcfile
1962 RunWindow(cmd, Toplevel(), self)
1965 cmd = "fetchmail -N -d0"
1967 cmd = cmd + " -f " + rcfile
1968 RunWindow(cmd, Toplevel(), self)
1973 # Functions for turning a dictionary into an instantiated object tree.
1975 def intersect(list1, list2):
1976 # Compute set intersection of lists
1983 def setdiff(list1, list2):
1984 # Compute set difference of lists
1991 def copy_instance(toclass, fromdict):
1992 # Initialize a class object of given type from a conformant dictionary.
1993 for fld in fromdict.keys():
1994 if not fld in dictmembers:
1995 dictmembers.append(fld)
1996 # The `optional' fields are the ones we can ignore for purposes of
1997 # conformability checking; they'll still get copied if they are
1998 # present in the dictionary.
1999 optional = ('interface', 'monitor',
2000 'esmtpname', 'esmtppassword',
2001 'ssl', 'sslkey', 'sslcert', 'sslproto', 'sslcertck',
2002 'sslcertpath', 'sslcommonname', 'sslfingerprint', 'showdots')
2003 class_sig = setdiff(toclass.__dict__.keys(), optional)
2005 dict_keys = setdiff(fromdict.keys(), optional)
2007 common = intersect(class_sig, dict_keys)
2008 if 'typemap' in class_sig:
2009 class_sig.remove('typemap')
2010 if tuple(class_sig) != tuple(dict_keys):
2011 print "Fields don't match what fetchmailconf expected:"
2012 # print "Class signature: " + `class_sig`
2013 # print "Dictionary keys: " + `dict_keys`
2014 diff = setdiff(class_sig, common)
2016 print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
2017 diff = setdiff(dict_keys, common)
2019 print "Not matched in dictionary keys: " + `diff`
2022 for x in fromdict.keys():
2023 setattr(toclass, x, fromdict[x])
2026 # And this is the main sequence. How it works:
2028 # First, call `fetchmail --configdump' and trap the output in a tempfile.
2029 # This should fill it with a Python initializer for a variable `fetchmailrc'.
2030 # Run execfile on the file to pull fetchmailrc into Python global space.
2031 # You don't want static data, though; you want, instead, a tree of objects
2032 # with the same data members and added appropriate methods.
2034 # This is what the copy_instance function() is for. It tries to copy a
2035 # dictionary field by field into a class, aborting if the class and dictionary
2036 # have different data members (except for any typemap member in the class;
2037 # that one is strictly for use by the MyWidget supperclass).
2039 # Once the object tree is set up, require user to choose novice or expert
2040 # mode and instantiate an edit object for the configuration. Class methods
2041 # will take it all from there.
2043 # Options (not documented because they're for fetchmailconf debuggers only):
2044 # -d: Read the configuration and dump it to stdout before editing. Dump
2045 # the edited result to stdout as well.
2046 # -f: specify the run control file to read.
2048 if __name__ == '__main__':
2050 if not os.environ.has_key("DISPLAY"):
2051 print "fetchmailconf must be run under X"
2054 fetchmail_icon = """
2055 R0lGODdhPAAoAPcAAP///wgICBAQEISEhIyMjJSUlKWlpa2trbW1tcbGxs7Ozufn5+/v7//39yEY
2056 GNa9tUoxKZyEe1o5KTEQAN7OxpyMhIRjUvfn3pxSKYQ5EO/Wxv/WvWtSQrVzSmtCKWspAMatnP/e
2057 xu+1jIxSKaV7Wt6ca5xSGK2EY8aUa72MY86UY617UsaMWrV7SpRjOaVrOZRaKYxSIXNCGGs5EIRC
2058 CJR7Y/+UMdbOxnNrY97Ove/Wvd7GrZyEa961jL2Ua9alc86ca7WEUntSKcaMSqVjGNZ7GGM5CNa1
2059 jPfOnN6tc3taMffeve/WtWtaQv/OjGtSMYRzWv/erda1hM6te7WUY62MWs61jP/vzv/ntda9jL2l
2060 czEhAO/n1oyEc//elDEpGEo5EOfexpyUe+/epefevffvxnNrQpyUStbWzsbGvZyclN7ezmNjWv//
2061 5/f33qWllNbWve/vzv//1ufnve/vvf//xvf3vefnrf//taWlc0pKMf//pbW1Y///jKWlWq2tWsbG
2062 Y///c97eUvf3Ut7nc+/3a87We8bOjOfv1u/37/f//621tb3Gxtbn52Nra87n53uUlJTv/6W9xuf3
2063 /8bW3iExOXu11tbv/5TW/4TO/63e/zmt/1KUxlK1/2u9/wCM/73GzrXG1gBKjACE/87e72NzhCkx
2064 OaXO92OMtUql/xCE/wApUtbe57W9xnN7hHut52Ot/xBSnABKnABavQB7/2ul7zF71gBr77XO73Oc
2065 1lqc9yFSlBApSimE/wAYOQApY0J7zlKM5wAxhABS1gBj/6W95wAhWgA5nAAYSgBS7wBS/wBK9wAp
2066 jABC5wBK/wApnABC/wApxgAhtYSMtQAQYwAp/3OE74SMxgAYxlpjvWNr70pS/wgQ3sbGzs7O1qWl
2067 3qWl70pKe0JC/yEhlCkp/wgI/wAAEAAAIQAAKQAAOQAASgAAUgAAYwAAawAAlAAAnAAApQAArQAA
2068 zgAA1gAA5wAA9wAA/0pC/xgQ52Na9ykhe4R7zikhYxgQSjEpQgAAACwAAAAAPAAoAAAI/wABCBxI
2069 sKDBgwgTKiRIYKHDhxARIvgXsaLFhGgEUBSYoKPHjyBDihxJkuS/kwNLqlzJcuTJjQBaypxpEiVH
2070 mjhxvkyZs2fLnTd9ehxAtKjRo0ZrwhTasUsENhYHKOUpk1E3j11mxCBiQVLEBlJd2owp9iVRjwUs
2071 zMCQ5IcLD4saPVxjIKxIoGTvvqSoyFEFGTBeqEhyxAoSFR/USGKVcEGBAwDshsSr1OYTEyhQpJiS
2072 ZcoUKWOQtJDRJFSaggzUGBgoGSTlsjahlPCRIkWVKT16THHRIoqIISBIEUgAYIGBhgRbf3ytFygU
2073 FZp9UDmxQkkMCRwyZKDBQy4aApABhP8XqNwj88l7BVpQYZtF5iArWgwAgGZBq24HU7OeGhQ90PVA
2074 aKZZCiiUMJ9ArSTEwGqR8ZeXfzbV0MIIMQTBwoUdxDDfAm8sZFyDZVEF4UYSKBEBD0+k6IEFPMxH
2075 3FzldXSea+kBgANJSOWIlIMhXZXAXv+c1WM3PuJEpH8iuhbAkv+MdENPRHaTRkdF/jiWSKCAwlKW
2076 VbbkY5Q0LgUSKExgoYBKCjCxARpdltQNKHaUoYAddnR53lVRnJLKBWh4RIEGCZx5FSOv1OLNDUVe
2077 deZHaWiZAB35fIOGNtbEUeV5oGAByzPOrBPFGt3kwEgxITACSg5oLGGLMg60oQAjaNz/oAAcN4Ai
2078 a0c3kHFDK3jYsw4g9sRzBgPLXdkRrBrQ8gsWQUxCCRZX9IJNBQ1s8IgCdeBCzBYN6IBIN2TUsQYd
2079 dXhDBxdzlAHOHHKEcocZdWwDjx8MTCmjsR2FMAstw1RyiSzHqPLALaOwk8QmzCzDCSi0xJKMMk4E
2080 Yw8389iTDT32GAKOPf7YY0Aa9tATyD3w/EGsefgmgEYUtPiChLKWQDMBJtEUgYkzH2RiTgGfTMCI
2081 Mlu0Yc85hNiDziH2tMqOGL72QY47gshLb7Fi4roELcjoQIsxWpDwQyfS2OCJMkLI4YUmyhgxSTVg
2082 CP2FHPZ80UDcieBjStNPD5LPOyZT/y0iHGiMwswexDSzRiRq6KIMJBc4M8skwKAyChia2KPH3P24
2083 YU8/lFhOTj152OPOHuXMU4g48vCRiN/9rZGLMdS4csUu1JzDgxuipOMDHMKsAwEnq/ByzTrrZMNO
2084 OtO0k84+7KjzBjzplMJOOOOoo8846/ATxqJWinkkGUyEkMAaIezABQM3bMAEK1xEsUMDGjARRxhY
2085 xEGGHfPjEcccca6BRxhyuEMY7FCHMNDhf9140r2qRiVvdENQ3liUArzREW/0qRsRVIAGFfBADnLw
2086 gUSiYASJpMEHhilJTEnhAlGoQqYAZQ1AiqEMZ0jDGtqQImhwwA13yMMevoQAGvGhEAWHGMOAAAA7
2088 # The base64 data in the string above was generated by the following procedure:
2091 # print base64.encodestring(open("fetchmail.gif", "rb").read())
2095 (options, arguments) = getopt.getopt(sys.argv[1:], "df:hV", ["help",
2097 dump = rcfile = None;
2098 for (switch, val) in options:
2099 if (switch == '-d'):
2101 elif (switch == '-f'):
2103 elif (switch == '-h' or switch == '--help'):
2105 Usage: fetchmailconf {[-d] [-f fetchmailrc]|-h|--help|-V|--version}
2106 -d - dump configuration (for debugging)
2107 -f fmrc - read alternate fetchmailrc file
2108 --help, -h - print this help text and quit
2109 --version, -V - print fetchmailconf version and quit
2112 elif (switch == '-V' or switch == '--version'):
2113 print "fetchmailconf %s" % version
2115 Copyright (C) 1997 - 2003 Eric S. Raymond
2116 Copyright (C) 2005, 2006, 2008, 2009 Matthias Andree
2117 fetchmailconf comes with ABSOLUTELY NO WARRANTY. This is free software, you are
2118 welcome to redistribute it under certain conditions. Please see the file
2119 COPYING in the source or documentation directory for details."""
2122 # Get client host's FQDN
2123 hostname = socket.gethostbyaddr(socket.gethostname())[0]
2126 ConfigurationDefaults = Configuration()
2127 ServerDefaults = Server()
2128 UserDefaults = User()
2130 # Read the existing configuration. We set the umask to 077 to make sure
2131 # that group & other read/write permissions are shut off -- we wouldn't
2132 # want crackers to snoop password information out of the tempfile.
2133 tmpfile = tempfile.mktemp()
2135 cmd = "umask 077 && fetchmail </dev/null -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2137 cmd = "umask 077 && fetchmail </dev/null --configdump --nosyslog >" + tmpfile
2142 print "`" + cmd + "' run failure, status " + `s`
2145 print "Unknown error while running fetchmail --configdump"
2152 print "Can't read configuration output of fetchmail --configdump."
2158 # The tricky part -- initializing objects from the configuration global
2159 # `Configuration' is the top level of the object tree we're going to mung.
2160 # The dictmembers list is used to track the set of fields the dictionary
2161 # contains; in particular, we can use it to tell whether things like the
2162 # monitor, interface, ssl, sslkey, or sslcert fields are present.
2164 Fetchmailrc = Configuration()
2165 copy_instance(Fetchmailrc, fetchmailrc)
2166 Fetchmailrc.servers = [];
2167 for server in fetchmailrc['servers']:
2169 copy_instance(Newsite, server)
2170 Fetchmailrc.servers.append(Newsite)
2172 for user in server['users']:
2174 copy_instance(Newuser, user)
2175 Newsite.users.append(Newuser)
2177 # We may want to display the configuration and quit
2179 print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2181 # The theory here is that -f alone sets the rcfile location,
2182 # but -d and -f together mean the new configuration should go to stdout.
2183 if not rcfile and not dump:
2184 rcfile = os.environ["HOME"] + "/.fetchmailrc"
2186 # OK, now run the configuration edit
2187 root = MainWindow(rcfile)
2190 # The following sets edit modes for GNU EMACS