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