~ari-tczew/ubuntu/dapper/fetchmail/fix-CVE-2008-2711

« back to all changes in this revision

Viewing changes to fetchmailconf

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2006-02-07 12:12:13 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060207121213-onurwfrzdlzlzxnt
Tags: 6.3.2-2ubuntu1
* Resynchronise with Debian. This brings the new upstream version to dapper
  since upstream support for 6.2 was dropped.
* Drop debian/patches/CVE-2005-4348.dpatch, upstream now.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
#
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
7
 
#
8
 
# Changes by Matthias Andree, in 2005:
9
 
#
10
 
# 1.43.1 - unsuccessful attempt to fix a password exposure bug
11
 
#
12
 
#        thanks to Thomas Wolff and Miloslav Trmac for pointing
13
 
#        out the fix was insufficient
14
 
#
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
18
 
#
19
 
version = "1.43.2"
20
 
 
21
 
from Tkinter import *
22
 
from Dialog import *
23
 
import sys, time, os, string, socket, getopt, tempfile
24
 
 
25
 
#
26
 
# Define the data structures the GUIs will be tossing around
27
 
#
28
 
class Configuration:
29
 
    def __init__(self):
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'),
43
 
            ('idfile',    'String'),
44
 
            ('postmaster',      'String'),
45
 
            ('bouncemail',      'Boolean'),
46
 
            ('spambounce',      'Boolean'),
47
 
            ('properties',      'String'),
48
 
            ('syslog',    'Boolean'),
49
 
            ('invisible',       'Boolean'))
50
 
 
51
 
    def __repr__(self):
52
 
        str = "";
53
 
        if self.syslog != ConfigurationDefaults.syslog:
54
 
           str = str + ("set syslog\n")
55
 
        elif self.logfile:
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,));
61
 
        if self.bouncemail:
62
 
            str = str + ("set bouncemail\n")
63
 
        else:
64
 
            str = str + ("set nobouncemail\n")
65
 
        if self.spambounce:
66
 
            str = str + ("set spambounce\n")
67
 
        else:
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)
75
 
        return str
76
 
 
77
 
    def __delitem__(self, name):
78
 
        for si in range(len(self.servers)):
79
 
            if self.servers[si].pollname == name:
80
 
                del self.servers[si]
81
 
                break
82
 
 
83
 
    def __str__(self):
84
 
        return "[Configuration: " + repr(self) + "]"
85
 
 
86
 
class Server:
87
 
    def __init__(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
113
 
        Server.typemap = (
114
 
            ('pollname',  'String'),
115
 
            ('via',       'String'),
116
 
            ('active',    'Boolean'),
117
 
            ('interval',  'Int'),
118
 
            ('protocol',  'String'),
119
 
            ('port',      'Int'),
120
 
            ('uidl',      'Boolean'),
121
 
            ('auth',      'String'),
122
 
            ('timeout',   'Int'),
123
 
            ('envelope',  'String'),
124
 
            ('envskip',   'Int'),
125
 
            ('qvirtual',  'String'),
126
 
            # leave aka out
127
 
            ('dns',       'Boolean'),
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'))
138
 
 
139
 
    def dump(self, folded):
140
 
        res = ""
141
 
        if self.active:   res = res + "poll"
142
 
        else:        res = res + "skip"
143
 
        res = res + (" " + self.pollname)
144
 
        if self.via:
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:
155
 
            if self.envskip:
156
 
                res = res + " envelope " + `self.envskip` + " " + self.envelope
157
 
            else:
158
 
                res = res + " envelope " + self.envelope
159
 
        if self.qvirtual:
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 + " "
171
 
 
172
 
        if self.aka:
173
 
             res = res + "aka"
174
 
             for x in self.aka:
175
 
                res = res + " " + x
176
 
        if self.aka and self.localdomains: res = res + " "
177
 
        if self.localdomains:
178
 
             res = res + ("localdomains")
179
 
             for x in self.localdomains:
180
 
                res = res + " " + x
181
 
        if (self.aka or self.localdomains):
182
 
            if folded:
183
 
                res = res + "\n    "
184
 
            else:
185
 
                res = res + " "
186
 
 
187
 
        if self.tracepolls:
188
 
           res = res + "tracepolls\n"
189
 
 
190
 
        if self.interface:
191
 
            res = res + " interface " + str(self.interface)
192
 
        if self.monitor:
193
 
            res = res + " monitor " + str(self.monitor)
194
 
        if self.plugin:
195
 
            res = res + " plugin " + `self.plugin`
196
 
        if self.plugout:
197
 
            res = res + " plugout " + `self.plugout`
198
 
        if self.netsec:
199
 
            res = res + " netsec " + str(self.netsec)
200
 
        if self.principal:
201
 
            res = res + " principal " + `self.principal`
202
 
        if self.esmtpname:
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:
207
 
            if folded:
208
 
                res = res + "\n"
209
 
 
210
 
        if res[-1] == " ": res = res[0:-1]
211
 
 
212
 
        for user in self.users:
213
 
            res = res + repr(user)
214
 
        res = res + "\n"
215
 
        return res;
216
 
 
217
 
    def __delitem__(self, name):
218
 
        for ui in range(len(self.users)):
219
 
            if self.users[ui].remote == name:
220
 
                del self.users[ui]
221
 
                break
222
 
 
223
 
    def __repr__(self):
224
 
        return self.dump(TRUE)
225
 
 
226
 
    def __str__(self):
227
 
        return "[Server: " + self.dump(FALSE) + "]"
228
 
 
229
 
class User:
230
 
    def __init__(self):
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"]
235
 
        else:
236
 
            print "Can't get your username!"
237
 
            sys.exit(1)
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
277
 
        User.typemap = (
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'),
286
 
            ('mda',      'String'),
287
 
            ('bsmtp',       'String'),
288
 
            ('lmtp',    'Boolean'),
289
 
            ('antispam',    'String'),
290
 
            ('keep',    'Boolean'),
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'),
300
 
            ('idle',    'Boolean'),
301
 
            ('limit',       'Int'),
302
 
            ('warnings',    'Int'),
303
 
            ('fetchlimit',  'Int'),
304
 
            ('fetchsizelimit',  'Int'),
305
 
            ('fastuidl',    'Int'),
306
 
            ('batchlimit',  'Int'),
307
 
            ('expunge',     'Int'),
308
 
            ('ssl',      'Boolean'),
309
 
            ('sslkey',      'String'),
310
 
            ('sslcert',     'String'),
311
 
            ('sslcertck',   'Boolean'),
312
 
            ('sslcertpath', 'String'),
313
 
            ('sslfingerprint', 'String'),
314
 
            ('properties',  'String'))
315
 
 
316
 
    def __repr__(self):
317
 
        res = "    "
318
 
        res = res + "user " + `self.remote` + " there ";
319
 
        if self.password:
320
 
            res = res + "with password " + `self.password` + " "
321
 
        if self.localnames:
322
 
            res = res + "is"
323
 
            for x in self.localnames:
324
 
                res = res + " " + `x`
325
 
            res = res + " here"
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`
388
 
        res = res + "\n"
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]
394
 
        if trimmed != []:
395
 
            res = res + "    smtphost "
396
 
            for x in trimmed:
397
 
                res = res + " " + x
398
 
                res = res + "\n"
399
 
        trimmed = self.fetchdomains
400
 
        if trimmed != [] and trimmed[len(trimmed) - 1] == hostname:
401
 
            trimmed = trimmed[0:len(trimmed) - 1]
402
 
        if trimmed != []:
403
 
            res = res + "    fetchdomains "
404
 
            for x in trimmed:
405
 
                res = res + " " + x
406
 
                res = res + "\n"
407
 
        if self.mailboxes:
408
 
             res = res + "    folder"
409
 
             for x in self.mailboxes:
410
 
                res = res + " " + x
411
 
             res = res + "\n"
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"
419
 
        return res;
420
 
 
421
 
    def __str__(self):
422
 
        return "[User: " + repr(self) + "]"
423
 
 
424
 
#
425
 
# Helper code
426
 
#
427
 
 
428
 
defaultports = {"auto":0,
429
 
                "POP2":109, 
430
 
                "POP3":110,
431
 
                "APOP":110,
432
 
                "KPOP":1109,
433
 
                "IMAP":143,
434
 
                "ETRN":25,
435
 
                "ODMR":366}
436
 
 
437
 
authlist = ("any", "password", "gssapi", "kerberos", "ssh", "otp")
438
 
 
439
 
listboxhelp = {
440
 
    'title' : 'List Selection Help',
441
 
    'banner': 'List Selection',
442
 
    'text' : """
443
 
You must select an item in the list box (by clicking on it). 
444
 
"""}
445
 
 
446
 
def flag2str(value, string):
447
 
# make a string representation of a .fetchmailrc flag or negated flag
448
 
    str = ""
449
 
    if value != None:
450
 
        str = str + (" ")
451
 
        if value == FALSE: str = str + ("no ")
452
 
        str = str + string;
453
 
    return str
454
 
 
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)
459
 
    def focus_set(self):
460
 
        self.E.focus_set()
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'})
467
 
 
468
 
def ButtonBar(frame, legend, ref, alternatives, depth, command):
469
 
# array of radio buttons, caption to left, picking from a string list
470
 
    bar = Frame(frame)
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], 
480
 
                         'variable':ref,
481
 
                         'value':alternatives[ind],
482
 
                         'command':command}).pack(side=TOP, anchor=W)
483
 
            else:
484
 
                # This is just a spacer
485
 
                Radiobutton(subframe,
486
 
                        {'text':" ",'state':DISABLED}).pack(side=TOP, anchor=W)
487
 
        subframe.pack(side=LEFT)
488
 
    bar.pack(side=TOP);
489
 
    return bar
490
 
 
491
 
def helpwin(helpdict):
492
 
# help message window with a self-destruct button
493
 
    helpwin = Toplevel()
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)
509
 
 
510
 
def make_icon_window(base, image):
511
 
    try:
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)
521
 
    except:
522
 
        pass
523
 
 
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):
527
 
        self.editor = editor
528
 
        self.deletor = deletor
529
 
        self.list = list
530
 
 
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)
537
 
 
538
 
        # Edit the existing list
539
 
        listframe = Frame(master)
540
 
        scroll = Scrollbar(listframe)
541
 
        self.listwidget = Listbox(listframe, height=0, selectmode='browse')
542
 
        if self.list:
543
 
            for x in self.list:
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);
553
 
 
554
 
        bf = Frame(master);
555
 
        if self.editor:
556
 
            Button(bf, text='Edit',   command=self.editItem).pack(side=LEFT)
557
 
        Button(bf, text='Delete', command=self.deleteItem).pack(side=LEFT)
558
 
        if helptxt:
559
 
            self.helptxt = helptxt
560
 
            Button(bf, text='Help', fg='blue',
561
 
                   command=self.help).pack(side=RIGHT)
562
 
        bf.pack(fill=X)
563
 
 
564
 
    def help(self):
565
 
        helpwin(self.helptxt)
566
 
 
567
 
    def handleList(self, event):
568
 
        self.editItem();
569
 
 
570
 
    def handleNew(self, event):
571
 
        item = self.newval.get()
572
 
        if item:
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)
577
 
                if self.editor:
578
 
                    apply(self.editor, (item,))
579
 
            self.newval.set('')
580
 
 
581
 
    def editItem(self):
582
 
        select = self.listwidget.curselection()
583
 
        if not select:
584
 
            helpwin(listboxhelp)
585
 
        else:
586
 
            index = select[0]
587
 
            if index and self.editor:
588
 
                label = self.listwidget.get(index);
589
 
                if self.editor:
590
 
                    apply(self.editor, (label,))
591
 
 
592
 
    def deleteItem(self):
593
 
        select = self.listwidget.curselection()
594
 
        if not select:
595
 
            helpwin(listboxhelp)
596
 
        else:
597
 
            index = string.atoi(select[0])
598
 
            label = self.listwidget.get(index);
599
 
            self.listwidget.delete(index)
600
 
            if self.list != None:
601
 
                del self.list[index]
602
 
            if self.deletor != None:
603
 
                apply(self.deletor, (label,))
604
 
 
605
 
def ConfirmQuit(frame, context):
606
 
    ans = Dialog(frame, 
607
 
                 title = 'Quit?',
608
 
                 text = 'Really quit ' + context + ' without saving?',
609
 
                 bitmap = 'question',
610
 
                 strings = ('Yes', 'No'),
611
 
                 default = 1)
612
 
    return ans.num == 0
613
 
 
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)
623
 
    dispose.pack(fill=X)
624
 
    return dispose
625
 
 
626
 
class MyWidget:
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))
634
 
            elif x[1] == 'Int':
635
 
                setattr(self, x[0], IntVar(self))
636
 
            source = getattr(getattr(self, field), x[0])
637
 
            if source:
638
 
                getattr(self, x[0]).set(source)
639
 
 
640
 
    def fetch(self, widgetclass, field):
641
 
        for x in widgetclass.typemap:
642
 
            setattr(getattr(self, field), x[0], getattr(self, x[0]).get())
643
 
 
644
 
#
645
 
# First, code to set the global fetchmail run controls.
646
 
#
647
 
 
648
 
configure_novice_help = {
649
 
    'title' : 'Fetchmail novice configurator help',
650
 
    'banner': 'Novice configurator help',
651
 
    'text' : """
652
 
In the `Novice Configurator Controls' panel, you can:
653
 
 
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.
657
 
 
658
 
In the `Novice Configuration' panels, you will set up the basic data
659
 
needed to create a simple fetchmail setup.  These include:
660
 
 
661
 
1. The name of the remote site you want to query.
662
 
 
663
 
2. Your login name on that site.
664
 
 
665
 
3. Your password on that site.
666
 
 
667
 
4. A protocol to use (POP, IMAP, ETRN, etc.)
668
 
 
669
 
5. A polling interval.
670
 
 
671
 
6. Options to fetch old messages as well as new, uor to suppress
672
 
   deletion of fetched message.
673
 
 
674
 
The novice-configuration code will assume that you want to forward mail
675
 
to a local sendmail listener with no special options.
676
 
"""}
677
 
 
678
 
configure_expert_help = {
679
 
    'title' : 'Fetchmail expert configurator help',
680
 
    'banner': 'Expert configurator help',
681
 
    'text' : """
682
 
In the `Expert Configurator Controls' panel, you can:
683
 
 
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.
687
 
 
688
 
In the `Run Controls' panel, you can set the following options that
689
 
control how fetchmail runs:
690
 
 
691
 
Poll interval
692
 
        Number of seconds to wait between polls in the background.
693
 
        If zero, fetchmail will run in foreground.
694
 
 
695
 
Logfile
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.
699
 
 
700
 
Idfile
701
 
        If empty, store seen-message IDs in .fetchids under user's home
702
 
        directory.  If nonempty, use given file name.
703
 
 
704
 
Postmaster
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'.
709
 
 
710
 
Bounces to sender?
711
 
        If this option is on (the default) error mail goes to the sender.
712
 
        Otherwise it goes to the postmaster.
713
 
 
714
 
Send spam bounces?
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).
718
 
 
719
 
Invisible
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
723
 
        the remote site.
724
 
 
725
 
In the `Remote Mail Configurations' panel, you can:
726
 
 
727
 
1. Enter the name of a new remote mail server you want fetchmail to query.
728
 
 
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).
733
 
 
734
 
2. Change the configuration of an existing site.
735
 
 
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.
738
 
"""}
739
 
 
740
 
 
741
 
class ConfigurationEdit(Frame, MyWidget):
742
 
    def __init__(self, configuration, outfile, master, onexit):
743
 
        self.subwidgets = {}
744
 
        self.configuration = configuration
745
 
        self.outfile = outfile
746
 
        self.container = master
747
 
        self.onexit = onexit
748
 
        ConfigurationEdit.mode_to_help = {
749
 
            'novice':configure_novice_help, 'expert':configure_expert_help
750
 
            }
751
 
 
752
 
    def server_edit(self, sitename):
753
 
        self.subwidgets[sitename] = ServerEdit(sitename, self).edit(self.mode, Toplevel())
754
 
 
755
 
    def server_delete(self, sitename):
756
 
        try:
757
 
            for user in self.subwidgets.keys():
758
 
                user.destruct()
759
 
            del self.configuration[sitename]
760
 
        except:
761
 
            pass
762
 
 
763
 
    def edit(self, mode):
764
 
        self.mode = 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)
771
 
        Pack.config(self)
772
 
        self.post(Configuration, 'configuration')
773
 
 
774
 
        dispose_window(self,
775
 
                       'Configurator ' + self.mode + ' Controls',
776
 
                       ConfigurationEdit.mode_to_help[self.mode],
777
 
                       'Save')
778
 
 
779
 
        gf = Frame(self, relief=RAISED, bd = 5)
780
 
        Label(gf,
781
 
                text='Fetchmail Run Controls', 
782
 
                bd=2).pack(side=TOP, pady=10)
783
 
 
784
 
        df = Frame(gf)
785
 
 
786
 
        ff = Frame(df)
787
 
        if self.mode != 'novice':
788
 
            # Set the postmaster
789
 
            log = LabeledEntry(ff, '     Postmaster:', self.postmaster, '14')
790
 
            log.pack(side=RIGHT, anchor=E)
791
 
 
792
 
        # Set the poll interval
793
 
        de = LabeledEntry(ff, '     Poll interval:', self.poll_interval, '14')
794
 
        de.pack(side=RIGHT, anchor=E)
795
 
        ff.pack()
796
 
 
797
 
        df.pack()
798
 
 
799
 
        if self.mode != 'novice':
800
 
            pf = Frame(gf)
801
 
            Checkbutton(pf,
802
 
                {'text':'Bounces to sender?',
803
 
                'variable':self.bouncemail,
804
 
                'relief':GROOVE}).pack(side=LEFT, anchor=W)
805
 
            pf.pack(fill=X)
806
 
 
807
 
            sb = Frame(gf)
808
 
            Checkbutton(sb,
809
 
                {'text':'send spam bounces?',
810
 
                'variable':self.spambounce,
811
 
                'relief':GROOVE}).pack(side=LEFT, anchor=W)
812
 
            sb.pack(fill=X)
813
 
 
814
 
            sf = Frame(gf)
815
 
            Checkbutton(sf,
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)
821
 
            sf.pack(fill=X)
822
 
 
823
 
            Checkbutton(gf,
824
 
                {'text':'Invisible mode?',
825
 
                'variable':self.invisible,
826
 
                 'relief':GROOVE}).pack(side=LEFT, anchor=W)
827
 
            # Set the idfile
828
 
            log = LabeledEntry(gf, '     Idfile:', self.idfile, '14')
829
 
            log.pack(side=RIGHT, anchor=E)
830
 
 
831
 
        gf.pack(fill=X)
832
 
 
833
 
        # Expert mode allows us to edit multiple sites
834
 
        lf = Frame(self, relief=RAISED, bd=5)
835
 
        Label(lf,
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),
842
 
                lf, remotehelp)
843
 
        lf.pack(fill=X)
844
 
 
845
 
    def destruct(self):
846
 
        for sitename in self.subwidgets.keys():
847
 
            self.subwidgets[sitename].destruct()        
848
 
        self.master.destroy()
849
 
        self.onexit()
850
 
 
851
 
    def nosave(self):
852
 
        if ConfirmQuit(self, self.mode + " configuration editor"):
853
 
            self.destruct()
854
 
 
855
 
    def save(self):
856
 
        for sitename in self.subwidgets.keys():
857
 
            self.subwidgets[sitename].save()
858
 
        self.fetch(Configuration, 'configuration')
859
 
        fm = None
860
 
        if not self.outfile:
861
 
            fm = sys.stdout
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?',
865
 
                 bitmap = 'question',
866
 
                 strings = ('Yes', 'No'),
867
 
                 default = 1).num == 0:
868
 
            try:
869
 
                os.rename(self.outfile, self.outfile + "~")
870
 
            # Pre-1.5.2 compatibility...
871
 
            except os.error:
872
 
                pass
873
 
            old_umask = os.umask(077)
874
 
            fm = open(self.outfile, 'w')
875
 
            os.umask(old_umask)
876
 
        if fm:
877
 
            if fm != sys.stdout:
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`)
882
 
            if self.outfile:
883
 
                fm.close()
884
 
            self.destruct()
885
 
 
886
 
#
887
 
# Server editing stuff.
888
 
#
889
 
remotehelp = {
890
 
    'title' : 'Remote site help',
891
 
    'banner': 'Remote sites',
892
 
    'text' : """
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.
896
 
 
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
901
 
site.
902
 
"""}
903
 
 
904
 
serverhelp = {
905
 
    'title' : 'Server options help',
906
 
    'banner': 'Server Options',
907
 
    'text' : """
908
 
The server options screen controls fetchmail 
909
 
options that apply to one of your mailservers.
910
 
 
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.
915
 
 
916
 
If you wish to discard changes to a server 
917
 
configuration, select `Quit'.
918
 
"""}
919
 
 
920
 
controlhelp = {
921
 
    'title' : 'Run Control help',
922
 
    'banner': 'Run Controls',
923
 
    'text' : """
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.
928
 
 
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.
931
 
 
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.
936
 
 
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
939
 
giving up.
940
 
"""}
941
 
 
942
 
protohelp = {
943
 
    'title' : 'Protocol and Port help',
944
 
    'banner': 'Protocol and Port',
945
 
    'text' : """
946
 
These options control the remote-mail protocol
947
 
and TCP/IP service port used to query this
948
 
server.
949
 
 
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.
957
 
 
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
962
 
available.
963
 
 
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.
968
 
"""}
969
 
 
970
 
sechelp = {
971
 
    'title' : 'Security option help',
972
 
    'banner': 'Security',
973
 
    'text' : """
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.
980
 
 
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.
986
 
 
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.
990
 
 
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.
998
 
 
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.
1002
 
"""}
1003
 
 
1004
 
multihelp = {
1005
 
    'title' : 'Multidrop option help',
1006
 
    'banner': 'Multidrop',
1007
 
    'text' : """
1008
 
These options are only useful with multidrop mode.
1009
 
See the manual page for extended discussion.
1010
 
"""}
1011
 
 
1012
 
suserhelp = {
1013
 
    'title' : 'User list help',
1014
 
    'banner': 'User list',
1015
 
    'text' : """
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.
1019
 
 
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.
1025
 
"""}
1026
 
 
1027
 
class ServerEdit(Frame, MyWidget):
1028
 
    def __init__(self, host, parent):
1029
 
        self.parent = parent
1030
 
        self.server = None
1031
 
        self.subwidgets = {}
1032
 
        for site in parent.configuration.servers:
1033
 
            if site.pollname == host:
1034
 
                self.server = site
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)
1040
 
 
1041
 
    def edit(self, mode, master=None):
1042
 
        Frame.__init__(self, master)
1043
 
        Pack.config(self)
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)
1050
 
#       self.grab_set()
1051
 
#       self.focus_set()
1052
 
#       self.wait_window()
1053
 
        return self
1054
 
 
1055
 
    def destruct(self):
1056
 
        for username in self.subwidgets.keys():
1057
 
            self.subwidgets[username].destruct()        
1058
 
        del self.parent.subwidgets[self.server.pollname]
1059
 
        self.master.destroy()
1060
 
 
1061
 
    def nosave(self):
1062
 
        if ConfirmQuit(self, 'server option editing'):
1063
 
            self.destruct()
1064
 
 
1065
 
    def save(self):
1066
 
        self.fetch(Server, 'server')
1067
 
        for username in self.subwidgets.keys():
1068
 
            self.subwidgets[username].save()    
1069
 
        self.destruct()
1070
 
 
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 
1082
 
 
1083
 
    def user_edit(self, username, mode):
1084
 
        self.subwidgets[username] = UserEdit(username, self).edit(mode, Toplevel())
1085
 
 
1086
 
    def user_delete(self, username):
1087
 
        if self.subwidgets.has_key(username):
1088
 
            self.subwidgets[username].destruct()
1089
 
        del self.server[username]
1090
 
 
1091
 
    def makeWidgets(self, host, mode):
1092
 
        topwin = dispose_window(self, "Server options for querying " + host, serverhelp)
1093
 
 
1094
 
        leftwin = Frame(self);
1095
 
        leftwidth = '25';
1096
 
 
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)
1109
 
            ctlwin.pack(fill=X)
1110
 
 
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")
1125
 
 
1126
 
        protwin = Frame(leftwin, relief=RAISED, bd=5)
1127
 
        Label(protwin, text="Protocol").pack(side=TOP)
1128
 
        ButtonBar(protwin, '',
1129
 
                  self.protocol, protolist, 2,
1130
 
                  self.defaultPort) 
1131
 
        if mode != 'novice':
1132
 
            LabeledEntry(protwin, 'On server TCP/IP port:',
1133
 
                      self.port, leftwidth).pack(side=TOP, fill=X)
1134
 
            self.defaultPort()
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)
1143
 
 
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),
1150
 
                 userwin, suserhelp)
1151
 
        userwin.pack(fill=X)
1152
 
 
1153
 
        leftwin.pack(side=LEFT, anchor=N, fill=X);
1154
 
 
1155
 
        if mode != 'novice':
1156
 
            rightwin = Frame(self);
1157
 
 
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)
1174
 
 
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)
1201
 
                secwin.pack(fill=X)
1202
 
 
1203
 
            rightwin.pack(side=LEFT, anchor=N);
1204
 
 
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.
1209
 
        if self.server.via:
1210
 
            realhost = self.server.via
1211
 
        else:
1212
 
            realhost = self.server.pollname
1213
 
        greetline = None
1214
 
        for (protocol, port) in (("IMAP",143), ("POP3",110), ("POP2",109)):
1215
 
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1216
 
            try:
1217
 
                sock.connect((realhost, port))
1218
 
                greetline = sock.recv(1024)
1219
 
                sock.close()
1220
 
            except:
1221
 
                pass
1222
 
            else:
1223
 
                break
1224
 
        confwin = Toplevel()
1225
 
        if greetline == None:
1226
 
            title = "Autoprobe of " + realhost + " failed"
1227
 
            confirm = """
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.
1233
 
"""
1234
 
        else:
1235
 
            warnings = ''
1236
 
            # OK, now try to recognize potential problems
1237
 
 
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?
1242
 
 
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.
1247
 
 
1248
 
"""
1249
 
 
1250
 
### POP3 servers start here
1251
 
 
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).
1257
 
 
1258
 
It is strongly recommended that you find a better POP3 server.  The fetchmail
1259
 
FAQ includes pointers to good ones.
1260
 
 
1261
 
"""
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
1266
 
`fetchall' option.
1267
 
 
1268
 
"""
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.
1277
 
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.
1282
 
1283
 
# The cucipop banner looks like:
1284
 
1285
 
# +OK Cubic Circle's v1.31 1998/05/13 POP3 ready <6229000062f95036@wakko>
1286
 
#
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.
1293
 
 
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.
1297
 
"""
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.
1302
 
 
1303
 
"""
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)
1306
 
#
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.
1311
 
 
1312
 
"""
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.
1317
 
 
1318
 
"""
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
1325
 
this server.  
1326
 
 
1327
 
"""
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.
1334
 
 
1335
 
"""
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
1342
 
of other ways, too.
1343
 
 
1344
 
"""
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.
1352
 
 
1353
 
"""
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.
1358
 
 
1359
 
"""
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.
1368
 
 
1369
 
"""
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.  
1376
 
 
1377
 
"""
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
1384
 
server.
1385
 
 
1386
 
"""
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
1393
 
server.
1394
 
 
1395
 
"""
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.
1405
 
 
1406
 
Therefore, it is strongly recommended that you turn on `fetchall' on all
1407
 
user entries associated with this server.  
1408
 
 
1409
 
"""
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.
1415
 
 
1416
 
"""
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.)
1423
 
 
1424
 
"""
1425
 
 
1426
 
### IMAP servers start here
1427
 
 
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>
1434
 
 
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>
1439
 
 
1440
 
"""
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.
1446
 
 
1447
 
"""
1448
 
            if string.find(greetline, "InterChange") > 0:
1449
 
                warnings = warnings + """
1450
 
 
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.
1458
 
 
1459
 
"""
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.
1466
 
 
1467
 
"""
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.
1475
 
 
1476
 
"""
1477
 
 
1478
 
### Checks for protocol variants start here
1479
 
 
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
1485
 
this.
1486
 
 
1487
 
"""
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.
1493
 
 
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.
1497
 
 
1498
 
To fix this bug, upgrade to an IMAP4 server.  The fetchmail FAQ includes
1499
 
a pointer to an open-source implementation.
1500
 
 
1501
 
"""
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.
1507
 
 
1508
 
"""
1509
 
            if warnings == '':
1510
 
                warnings = warnings + """
1511
 
Fetchmail doesn't know anything special about this server type.
1512
 
 
1513
 
"""
1514
 
 
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()
1525
 
        
1526
 
#
1527
 
# User editing stuff
1528
 
#
1529
 
 
1530
 
userhelp = {
1531
 
    'title' : 'User option help',
1532
 
    'banner': 'User options',
1533
 
    'text' : """
1534
 
You may use this panel to set options
1535
 
that may differ between individual
1536
 
users on your site.
1537
 
 
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.
1542
 
 
1543
 
If you wish to discard the changes you have
1544
 
made to user options, select `Quit'.
1545
 
"""}
1546
 
 
1547
 
localhelp = {
1548
 
    'title' : 'Local name help',
1549
 
    'banner': 'Local names',
1550
 
    'text' : """
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.
1554
 
 
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.
1560
 
 
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.
1565
 
"""}
1566
 
 
1567
 
class UserEdit(Frame, MyWidget):
1568
 
    def __init__(self, username, parent):
1569
 
        self.parent = parent
1570
 
        self.user = None
1571
 
        for user in parent.server.users:
1572
 
            if user.remote == username:
1573
 
                self.user = user
1574
 
        if self.user == None:
1575
 
            self.user = User()
1576
 
            self.user.remote = username
1577
 
            self.user.localnames = [username]
1578
 
            parent.server.users.append(self.user)
1579
 
 
1580
 
    def edit(self, mode, master=None):
1581
 
        Frame.__init__(self, master)
1582
 
        Pack.config(self)
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)
1590
 
#       self.grab_set()
1591
 
#       self.focus_set()
1592
 
#       self.wait_window()
1593
 
        return self
1594
 
 
1595
 
    def destruct(self):
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()
1600
 
 
1601
 
    def nosave(self):
1602
 
        if ConfirmQuit(self, 'user option editing'):
1603
 
            self.destruct()
1604
 
 
1605
 
    def save(self):
1606
 
        ok = 0
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')
1618
 
                self.destruct()
1619
 
 
1620
 
    def makeWidgets(self, mode, servername):
1621
 
        dispose_window(self,
1622
 
                        "User options for " + self.user.remote + " querying " + servername,
1623
 
                        userhelp)
1624
 
 
1625
 
        if mode != 'novice':
1626
 
            leftwin = Frame(self);
1627
 
        else:
1628
 
            leftwin = self
1629
 
 
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)
1635
 
 
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)
1651
 
 
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)
1657
 
 
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)
1686
 
 
1687
 
        if mode != 'novice':
1688
 
            leftwin.pack(side=LEFT, fill=X, anchor=N)
1689
 
            rightwin = Frame(self)
1690
 
        else:
1691
 
            rightwin = self
1692
 
 
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)
1716
 
        optwin.pack(fill=X)
1717
 
 
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)
1739
 
            limwin.pack(fill=X)
1740
 
 
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)
1747
 
 
1748
 
        if mode != 'novice':
1749
 
            rightwin.pack(side=LEFT)
1750
 
        else:
1751
 
            self.pack()
1752
 
 
1753
 
 
1754
 
#
1755
 
# Top-level window that offers either novice or expert mode
1756
 
# (but not both at once; it disappears when one is selected).
1757
 
#
1758
 
 
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');
1767
 
        Pack.config(self)
1768
 
        self.keepalive = []     # Use this to anchor the PhotoImage object
1769
 
        make_icon_window(self, fetchmail_icon)
1770
 
 
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()
1778
 
 
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()
1785
 
 
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)
1792
 
 
1793
 
    def novice(self):
1794
 
        self.master.destroy()
1795
 
        ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('novice')
1796
 
 
1797
 
    def expert(self):
1798
 
        self.master.destroy()
1799
 
        ConfigurationEdit(Fetchmailrc, self.outfile, Toplevel(), self.onexit).edit('expert')
1800
 
 
1801
 
    def leave(self):
1802
 
        self.master.destroy()
1803
 
        self.onexit()
1804
 
 
1805
 
# Run a command in a scrolling text widget, displaying its output
1806
 
 
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');
1813
 
        Pack.config(self)
1814
 
        Label(self,
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)
1819
 
 
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)
1830
 
 
1831
 
        Button(self, text='Quit', fg='blue', command=self.leave).pack()
1832
 
 
1833
 
        self.update()   # Draw widget before executing fetchmail
1834
 
 
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")
1840
 
        while 1:
1841
 
            ch = child_stdout.read(1)
1842
 
            if not ch:
1843
 
                break
1844
 
            self.textwidget.insert(END, ch)
1845
 
        self.textwidget.insert(END, "Done.")
1846
 
        self.textwidget.see(END);
1847
 
 
1848
 
    def leave(self):
1849
 
        self.master.destroy()
1850
 
 
1851
 
# Here's where we choose either configuration or launching
1852
 
 
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');
1859
 
        Pack.config(self)
1860
 
        Label(self,
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)
1865
 
        self.debug = 0
1866
 
 
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)
1871
 
 
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()
1880
 
 
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()
1886
 
 
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()
1892
 
 
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()
1897
 
 
1898
 
    def configure(self):
1899
 
        self.configbutton.configure(state=DISABLED)
1900
 
        Configurator(self.outfile, Toplevel(),
1901
 
                     lambda self=self: self.configbutton.configure(state=NORMAL),
1902
 
                     self)
1903
 
    def test(self):
1904
 
        RunWindow("fetchmail -d0 -v --nosyslog", Toplevel(), self)
1905
 
 
1906
 
    def run(self):
1907
 
        RunWindow("fetchmail -d0", Toplevel(), self)
1908
 
 
1909
 
    def leave(self):
1910
 
        self.quit()
1911
 
 
1912
 
# Functions for turning a dictionary into an instantiated object tree.
1913
 
 
1914
 
def intersect(list1, list2):
1915
 
# Compute set intersection of lists
1916
 
    res = []
1917
 
    for x in list1:
1918
 
        if x in list2:
1919
 
            res.append(x)
1920
 
    return res
1921
 
 
1922
 
def setdiff(list1, list2):
1923
 
# Compute set difference of lists
1924
 
    res = []
1925
 
    for x in list1:
1926
 
        if not x in list2:
1927
 
            res.append(x)
1928
 
    return res
1929
 
 
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)
1943
 
    class_sig.sort()
1944
 
    dict_keys = setdiff(fromdict.keys(), optional)
1945
 
    dict_keys.sort()
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)
1954
 
        if diff:
1955
 
            print "Not matched in class `" + toclass.__class__.__name__ + "' signature: " + `diff`
1956
 
        diff = setdiff(dict_keys, common)
1957
 
        if diff:
1958
 
            print "Not matched in dictionary keys: " + `diff`
1959
 
        sys.exit(1)
1960
 
    else:
1961
 
        for x in fromdict.keys():
1962
 
            setattr(toclass, x, fromdict[x])
1963
 
 
1964
 
#
1965
 
# And this is the main sequence.  How it works:  
1966
 
#
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.
1972
 
#
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).
1977
 
#
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.
1981
 
#
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.
1986
 
 
1987
 
if __name__ == '__main__': 
1988
 
 
1989
 
    if not os.environ.has_key("DISPLAY"):
1990
 
        print "fetchmailconf must be run under X"
1991
 
        sys.exit(1)
1992
 
 
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
2026
 
"""
2027
 
# The base64 data in the string above was generated by the following procedure:
2028
 
#
2029
 
# import base64
2030
 
# print base64.encodestring(open("fetchmail.gif", "rb").read())
2031
 
#
2032
 
 
2033
 
    # Process options
2034
 
    (options, arguments) = getopt.getopt(sys.argv[1:], "df:")
2035
 
    dump = rcfile = None;
2036
 
    for (switch, val) in options:
2037
 
        if (switch == '-d'):
2038
 
            dump = TRUE
2039
 
        elif (switch == '-f'):
2040
 
            rcfile = val
2041
 
 
2042
 
    # Get client host's FQDN
2043
 
    hostname = socket.gethostbyaddr(socket.gethostname())[0]
2044
 
 
2045
 
    # Compute defaults
2046
 
    ConfigurationDefaults = Configuration()
2047
 
    ServerDefaults = Server()
2048
 
    UserDefaults = User()
2049
 
 
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()
2054
 
    if rcfile:
2055
 
        cmd = "umask 077; fetchmail -f " + rcfile + " --configdump --nosyslog >" + tmpfile
2056
 
    else:
2057
 
        cmd = "umask 077; fetchmail --configdump --nosyslog >" + tmpfile
2058
 
        
2059
 
    try:
2060
 
        s = os.system(cmd)
2061
 
        if s != 0:
2062
 
            print "`" + cmd + "' run failure, status " + `s`
2063
 
            raise SystemExit
2064
 
    except:
2065
 
        print "Unknown error while running fetchmail --configdump"
2066
 
        os.remove(tmpfile)
2067
 
        sys.exit(1)
2068
 
 
2069
 
    try:
2070
 
        execfile(tmpfile)
2071
 
    except:
2072
 
        print "Can't read configuration output of fetchmail --configdump."
2073
 
        os.remove(tmpfile)
2074
 
        sys.exit(1)
2075
 
        
2076
 
    os.remove(tmpfile)
2077
 
 
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.
2083
 
    dictmembers = []
2084
 
    Fetchmailrc = Configuration()
2085
 
    copy_instance(Fetchmailrc, fetchmailrc)
2086
 
    Fetchmailrc.servers = [];
2087
 
    for server in fetchmailrc['servers']:
2088
 
        Newsite = Server()
2089
 
        copy_instance(Newsite, server)
2090
 
        Fetchmailrc.servers.append(Newsite)
2091
 
        Newsite.users = [];
2092
 
        for user in server['users']:
2093
 
            Newuser = User()
2094
 
            copy_instance(Newuser, user)
2095
 
            Newsite.users.append(Newuser)
2096
 
 
2097
 
    # We may want to display the configuration and quit
2098
 
    if dump:
2099
 
        print "This is a dump of the configuration we read:\n"+`Fetchmailrc`
2100
 
 
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"
2105
 
 
2106
 
    # OK, now run the configuration edit
2107
 
    root = MainWindow(rcfile)
2108
 
    root.mainloop()
2109
 
 
2110
 
# The following sets edit modes for GNU EMACS
2111
 
# Local Variables:
2112
 
# mode:python
2113
 
# End: