]> code.delx.au - gnu-emacs/blob - test/manual/etags/pyt-src/server.py
Merge from origin/emacs-25
[gnu-emacs] / test / manual / etags / pyt-src / server.py
1 #!/usr/bin/python
2 #
3 # NOTE: THIS PROGRAM DOES NOT WORK!
4 # It is intended as a regression test source for the Python support in etags.
5 # If you want a working version, you'll find it in the fetchmail distribution.
6 #
7
8 from Tkinter import *
9 from Dialog import *
10 import sys
11 import time
12 import os
13
14 #
15 # Define the data structures the GUIs will be tossing around
16 #
17 class Controls:
18 def __init__(self):
19 self.foreground = FALSE; # Run in background
20 self.daemon = 300 # Default to 5-minute timeout
21 self.syslog = FALSE # Use syslogd for logging?
22 self.logfile = None # No logfile, initially
23
24 def __repr__(self):
25 str = "";
26 if self.syslog:
27 str = str + ("set syslog\n")
28 elif self.logfile:
29 str = str + ("set logfile \"%s\"\n" % (self.logfile,));
30 if not self.foreground and self.daemon:
31 str = str + ("set daemon %s\n" % (self.daemon,))
32 return str + "\n"
33
34 def __str__(self):
35 return "[Server: " + repr(self) + "]"
36
37 class Server:
38 def __init__(self):
39 self.pollname = None # Poll label
40 self.via = None # True name of host
41 self.active = TRUE # Poll status
42 self.interval = 0 # Skip interval
43 self.protocol = 'auto' # Default to auto protocol
44 self.port = 0 # Port number to use
45 self.uidl = FALSE # Don't use RFC1725 UIDLs by default
46 self.auth = "password" # Default to password authentication
47 self.timeout = 300 # 5-minute timeout
48 self.envelope = "Received" # Envelope-address header
49 self.aka = [] # List of DNS aka names
50 self.dns = TRUE # Enable DNS lookup on multidrop
51 self.localdomains = [] # Domains to be considered local
52 self.interface = None # IP address and range
53 self.monitor = None # IP address and range
54 self.userlist = [] # List of user entries for site
55 self.typemap = (
56 ('pollname', 'String'),
57 ('via', 'String'),
58 ('active', 'Boolean'),
59 ('interval', 'Int'),
60 ('protocol', 'String'),
61 ('interval', 'Int'),
62 ('port', 'Int'),
63 ('uidl', 'Boolean'),
64 ('auth', 'String'),
65 ('timeout', 'Int'),
66 ('envelope', 'String'),
67 # leave aka out
68 ('dns', 'Boolean'),
69 # leave localdomains out
70 ('interface', 'String'),
71 ('monitor', 'String'))
72
73 def dump(self, folded):
74 str = ""
75 if self.active: str = str + "poll"
76 else: str = str + "skip"
77 str = str + (" " + self.pollname)
78 if self.via != self.pollname:
79 str = str + " via " + self.via
80 if self.protocol != ServerDefaults.protocol:
81 str = str + " with proto " + self.protocol
82 if self.port != defaultports[self.protocol]:
83 str = str + " port " + `self.port`
84 if self.timeout != ServerDefaults.timeout:
85 str = str + " timeout " + `self.timeout`
86 if self.interval != ServerDefaults.interval:
87 str = str + " interval " + `self.interval`
88 if self.envelope != ServerDefaults.envelope:
89 str = str + " envelope " + self.envelope
90 if self.auth != ServerDefaults.auth:
91 str = str + " auth " + self.auth
92 if self.dns != ServerDefaults.dns or self.uidl != ServerDefaults.uidl:
93 str = str + " and options"
94 if self.dns != ServerDefaults.dns:
95 str = str + flag2str(self.dns, 'dns')
96 if self.uidl != ServerDefaults.uidl:
97 str = str + flag2str(self.uidl, 'uidl')
98 if folded: str = str + "\n\t"
99 else: str = str + " "
100
101 if self.aka:
102 str = str + "aka"
103 for x in self.aka:
104 str = str + " " + x
105 if self.aka and self.localdomains: str = str + " "
106 if self.localdomains:
107 str = str + ("localdomains")
108 for x in self.localdomains:
109 str = str + " " + x
110 if (self.aka or self.localdomains):
111 if folded:
112 str = str + "\n\t"
113 else:
114 str = str + " "
115
116 if self.interface: str = str + " interface " + self.interface
117 if self.monitor: str = str + " monitor " + self.monitor
118 if (self.interface or self.monitor):
119 if folded:
120 str = str + "\n"
121
122 if str[-1] == "\t": str = str[0:-1]
123 return str;
124
125 def __repr__(self):
126 return self.dump(TRUE)
127
128 def __str__(self):
129 return "[Server: " + self.dump(FALSE) + "]"
130
131 class User:
132 def __init__(self):
133 self.username = "" # Remote username
134 self.localnames = None # Local names
135 self.password = "" # Password for mail account access
136 self.smpthost = 'localhost' # Host to forward to
137 self.mda = "" # Mail Delivery Agent
138 self.preconnect = "" # Connection setup
139 self.postconnect = "" # Connection wrapup
140 self.keep = FALSE # Keep messages
141 self.flush = FALSE # Flush messages
142 self.fetchall = FALSE # Fetch old messages
143 self.rewrite = TRUE # Rewrite message headers
144 self.forcecr = FALSE # Force LF -> CR/LF
145 self.stripcr = FALSE # Strip CR
146 self.pass8bits = FALSE # Force BODY=7BIT
147 self.dropstatus = FALSE # Force BODY=7BIT
148 self.limit = 0 # Message size limit
149 self.fetchlimit = 0 # Max messages fetched per batch
150 self.batchlimit = 0 # Max message forwarded per batch
151 self.typemap = (
152 ('username', 'String')
153 ('folder', 'String')
154 # leave out localnames
155 ('password', 'String')
156 ('smtphost', 'String')
157 ('preconnect', 'String')
158 ('postconnect', 'String')
159 ('mda', 'String')
160 ('keep', 'Boolean')
161 ('flush', 'Boolean')
162 ('fetchall', 'Boolean')
163 ('rewrite', 'Boolean')
164 ('forcecr', 'Boolean')
165 ('stripcr', 'Boolean')
166 ('pass8bits', 'Boolean')
167 ('dropstatus', 'Boolean')
168 ('limit', 'Int')
169 ('fetchlimit', 'Int')
170 ('batchlimit', 'Int'))
171
172 def __repr__(self):
173 str = ""
174 str = str + "user " + self.user;
175 if self.password: str = str + "with password " + self.password
176 if self.localnames:
177 str = str + "localnames"
178 for x in self.localnames:
179 str = str + " " + x
180 if (self.keep or self.flush or self.fetchall or self.rewrite or
181 self.forcecr or self.stripcr or self.pass8bits or self.dropstatus):
182 str = str + " options"
183 if self.keep != UserDefaults.keep:
184 str = str + flag2str(self.keep, 'keep')
185 if self.flush != UserDefaults.flush:
186 str = str + flag2str(self.flush, 'flush')
187 if self.fetchall != UserDefaults.fetchall:
188 str = str + flag2str(self.fetchall, 'fetchall')
189 if self.rewrite != UserDefaults.rewrite:
190 str = str + flag2str(self.rewrite, 'rewrite')
191 if self.forcecr != UserDefaults.forcecr:
192 str = str + flag2str(self.forcecr, 'forcecr')
193 if self.stripcr != UserDefaults.stripcr:
194 str = str + flag2str(self.stripcr, 'stripcr')
195 if self.pass8bits != UserDefaults.pass8bits:
196 str = str + flag2str(self.pass8bits, 'pass8bits')
197 if self.dropstatus != UserDefaults.dropstatus:
198 str = str + flag2str(self.dropstatus, 'dropstatus')
199 if self.limit != UserDefaults.limit:
200 str = str + " limit " + `self.limit`
201 if self.fetchlimit != UserDefaults.fetchlimit:
202 str = str + " fetchlimit " + `self.fetchlimit`
203 if self.batchlimit != UserDefaults.batchlimit:
204 str = str + " batchlimit " + `self.batchlimit`
205
206 def __str__(self):
207 return "[User: " + repr(self) + "]"
208
209 #
210 # Helper code
211 #
212
213 defaultports = {"auto":0,
214 "POP2":109,
215 "POP3":110, "APOP":110, "KPOP":1109, "IMAP":143,
216 "IMAP-K4":143,
217 "ETRN":25}
218
219 protolist = ("auto", "POP2", "POP3", "APOP", "KPOP", "IMAP", "IMAP-K4", "ETRN")
220
221 authlist = ("password", "kerberos")
222
223 def flag2str(value, string):
224 # make a string representation of a .fetchmailrc flag or negated flag
225 str = ""
226 if value != None:
227 str = str + (" ")
228 if value == FALSE: str = str + ("no ")
229 str = str + string;
230 return str
231
232 class LabeledEntry(Frame):
233 # widget consisting of entry field with caption to left
234 def bind(self, key, action):
235 self.E.bind(key, action)
236 def focus_set(self):
237 self.E.focus_set()
238 def __init__(self, Master, text, textvar, width):
239 Frame.__init__(self, Master)
240 self.L = Label(self, {'text':text, 'width':width, 'anchor':'w'})
241 self.E = Entry(self, {'textvar':textvar})
242 self.L.pack({'side':'left'})
243 self.E.pack({'side':'left', 'expand':'1', 'fill':'x'})
244
245 def ButtonBar(frame, legend, ref, alternatives, command):
246 # horizontal bar of radio buttons, caption to left, picking from a string list
247 bar = Frame(frame)
248 Label(bar, text=legend).pack(side=LEFT)
249 for alt in alternatives:
250 Radiobutton(bar,
251 {'text':alt, 'variable':ref, 'value':alt, 'command':command}).pack(side=LEFT)
252 bar.pack(side=TOP);
253 return bar
254
255 def helpwin(helpdict):
256 # help message window with a self-destruct button
257 helpwin = Toplevel()
258 helpwin.title(helpdict['title'])
259 helpwin.iconname(helpdict['title'])
260 Label(helpwin, text=helpdict['banner']).pack()
261 textwin = Message(helpwin, text=helpdict['text'], width=600)
262 textwin.pack()
263 Button(helpwin, text='Done',
264 command=lambda x=helpwin: Widget.destroy(x),
265 relief=SUNKEN, bd=2).pack()
266
267 class ListEdit(Frame):
268 # edit a list of values (duplicates not allowed) with a supplied editor hook
269 def __init__(self, newlegend, list, editor, master):
270 self.editor = editor
271 self.list = list
272
273 # Set up a widget to accept new sites
274 self.newval = StringVar(master)
275 newwin = LabeledEntry(master, newlegend, self.newval, '16')
276 newwin.bind('<Double-1>', self.handleNew)
277 newwin.bind('<Return>', self.handleNew)
278 newwin.pack(side=TOP, fill=X, anchor=E)
279
280 # Create the sitelist for site-configuration selection
281 listframe = Frame(master)
282 scroll = Scrollbar(listframe)
283 listwidget = Listbox(listframe, height=0, selectmode='browse')
284 if list:
285 for dnsname in list:
286 listwidget.insert('end', dnsname)
287 listframe.pack(side=TOP, expand=YES, fill=BOTH)
288 listwidget.config(yscrollcommand=scroll.set, relief=SUNKEN)
289 listwidget.pack(side=LEFT, expand=YES, fill=BOTH)
290 scroll.config(command=listwidget.yview, relief=SUNKEN)
291 scroll.pack(side=RIGHT, fill=BOTH)
292 listwidget.config(selectmode=SINGLE, setgrid=TRUE)
293 listwidget.bind('<Double-1>', self.handleList);
294 listwidget.bind('<Return>', self.handleList);
295 self.listwidget = listwidget
296
297 bf = Frame(master);
298 if self.editor:
299 Button(bf, text='Edit', command=self.editItem).pack(side=LEFT)
300 Button(bf, text='Delete', command=self.deleteItem).pack(side=RIGHT)
301 bf.pack(fill=X)
302
303 def handleList(self, event):
304 self.editItem();
305
306 def handleNew(self, event):
307 item = self.newval.get()
308 entire = self.listwidget.get(0, self.listwidget.index('end'));
309 if item and (not entire) or (not item in self.listwidget.get(0, self.listwidget.index('end'))):
310 self.listwidget.insert('end', item)
311 if self.list != None: self.list.append(item)
312 self.newval.set('')
313
314 def editItem(self):
315 index = self.listwidget.curselection()[0]
316 if index and self.editor:
317 label = self.listwidget.get(index);
318 apply(self.editor, (label,))
319
320 def deleteItem(self):
321 index = self.listwidget.curselection()[0]
322 if index:
323 self.listwidget.delete(index)
324 if self.list != None: del self.list[index]
325
326 def ConfirmQuit(frame, context):
327 ans = Dialog(frame,
328 title = 'Quit?',
329 text = 'Really quit ' + context + ' without saving?',
330 bitmap = 'question',
331 strings = ('Yes', 'No'),
332 default = 1)
333 return ans.num == 0
334 #
335 # First, code to set the global fetchmail run controls.
336 #
337
338 confighelp = {
339 'title' : 'Fetchmail configurator help',
340 'banner': 'Configurator help',
341 'text' : """
342 In the `Configurator Controls' panel, you can:
343
344 Press `Save' to save the new fetchmail configuration you have created.
345 Press `Quit' to exit without saving.
346 Press `Help' to bring up this help message.
347
348 In the `Configurator Controls' panel, you can set the following options that
349 control how fetchmail runs:
350
351 Poll interval
352 Number of seconds to wait between polls in the background.
353 Ignored if the `Run in Foreground?' option is on.
354
355 Logfile
356 If empty, emit progress and error messages to stderr.
357 Otherwise this gives the name of the files to write to.
358 This field is ignored if the "Log to syslog?" option is on.
359
360 In the `Remote Mail Configurations' panel, you can:
361
362 1. Enter the name of a new remote mail server you want fetchmail to query.
363
364 To do this, simply enter a label for the poll configuration in the
365 `New Server:' box. The label should be a DNS name of the server (unless
366 you are using ssh or some other tunneling method and will fill in the `via'
367 option on the site configuration screen).
368
369 2. Change the configuration of an existing site.
370
371 To do this, find the site's label in the listbox and double-click it.
372 This will take you to a site configuration dialogue.
373 """}
374
375 class ControlEdit(Frame):
376 def PostControls(self):
377 self.foreground = BooleanVar(self)
378 self.foreground.set(self.controls.foreground)
379 self.daemon = StringVar(self)
380 self.daemon.set(`self.controls.daemon`)
381 self.syslog = BooleanVar(self)
382 self.syslog.set(self.controls.syslog);
383 self.logfile = StringVar(self)
384 if self.controls.logfile: self.logfile.set(self.controls.logfile);
385
386 gf = Frame(self, relief=RAISED, bd = 5)
387
388 Label(gf,
389 text='Fetchmail Run Controls',
390 bd=2).pack(side=TOP, pady=10)
391
392 df = Frame(gf, relief=RAISED, bd=2)
393
394 # Run in foreground?
395 Checkbutton(df,
396 {'text':'Run in foreground?',
397 'variable':self.foreground,
398 'relief':GROOVE}).pack(side=LEFT,anchor=W)
399
400 # Set the poll interval
401 de = LabeledEntry(df, ' Poll interval:', self.daemon, '14')
402 de.pack(side=RIGHT, anchor=E)
403
404 df.pack();
405
406 sf = Frame(gf, relief=RAISED, bd=2)
407
408 # Use syslog for logging?
409 Checkbutton(sf,
410 {'text':'Log to syslog?',
411 'variable':self.syslog,
412 'relief':GROOVE}).pack(side=LEFT, anchor=W)
413
414 # Set the logfile
415 log = LabeledEntry(sf, ' Logfile:', self.logfile, '14')
416 log.pack(side=RIGHT, anchor=E)
417
418 sf.pack(fill=X)
419 gf.pack(fill=X)
420
421 def GatherControls(self):
422 self.controls.daemon = self.daemon.get()
423 self.controls.foreground = self.foreground.get()
424 self.controls.logfile = self.logfile.get()
425 self.controls.syslog = self.syslog.get()
426
427 #
428 # Server editing stuff.
429 #
430 serverhelp = {
431 'title' : 'Server options help',
432 'banner': 'Server Options',
433 'text' : """
434 The server options screen controls fetchmail
435 options that apply to one of your mailservers.
436
437 Once you have a mailserver configuration set
438 up as you like it, you can select `Save' to
439 store it in the server list maintained in
440 the main configuration window.
441
442 If you wish to discard changes to a server
443 configuration, select `Quit'.
444 """}
445
446 controlhelp = {
447 'title' : 'Run Control help',
448 'banner': 'Run Controls',
449 'text' : """
450 If the `Poll normally' checkbox is on, the host is polled as part of
451 the normal operation of fetchmail when it is run with no arguments.
452 If it is off, fetchmail will only query this host when it is given as
453 a command-line argument.
454
455 The `True name of server' box should specify the actual DNS name
456 to query. By default this is the same as the poll name.
457
458 Normally each host described in the file is queried once each
459 poll cycle. If `Cycles to skip between polls' is greater than 0,
460 that's the number of poll cycles that are skipped between the
461 times this post is actually polled.
462
463 The `Server timeout' is the number of seconds fetchmail will wait
464 for a reply from the mailserver before concluding it is hung and
465 giving up.
466 """}
467
468 protohelp = {
469 'title' : 'Protocol and Port help',
470 'banner': 'Protocol and Port',
471 'text' : """
472 These options control the remote-mail protocol
473 and TCP/IP service port used to query this
474 server.
475
476 The `Protocol' button bar offers you a choice of
477 all the different protocols available. The `auto'
478 protocol is a special mode that probes the host
479 ports for POP3 and IMAP to see if either is
480 available.
481
482 Normally the TCP/IP service port to use is
483 dictated by the protocol choice. The `Port'
484 field lets you set a non-standard port.
485 """}
486
487 sechelp = {
488 'title' : 'Security option help',
489 'banner': 'Security',
490 'text' : """
491 These options control the security procedure used
492 to protect mail transfer
493
494 Normally the mail fetch is validated using an
495 ordinary password logon. If your server speaks
496 MIT Kerberos IV it is possible to pre-authenticate
497 the exxchange with a Kerberos ticket.
498
499 The `interface' and `monitor' options are available
500 only for Linux systems. See the fetchmail manual page
501 for details on these.
502 """}
503
504 multihelp = {
505 'title' : 'Multidrop option help',
506 'banner': 'Multidrop',
507 'text' : """
508 These options are only useful with multidrop mode.
509 See the manual page for extended discussion.
510 """}
511
512 class ServerEdit(Frame):
513 def __init__(self, host, sitelist, master=None):
514 Frame.__init__(self, master)
515 Pack.config(self)
516 self.master.title('Fetchmail host ' + host);
517 self.master.iconname('Fetchmail host ' + host);
518 self.server = Server()
519 self.server.pollname = host
520 self.server.via = host
521 self.sitelist = sitelist
522 self.post()
523 self.createWidgets(host)
524
525 def post(self):
526 # we can't abstract this away, execs would happen in the wrong scope
527 for x in self.server.typemap:
528 target = "self." + x[0]
529 source = "self.server." + x[0]
530 if x[1] == 'Boolean':
531 exec target + " = BooleanVar(self)"
532 if eval(source):
533 exec target + ".set(" + source + ")"
534 elif x[1] == 'String':
535 exec target + " = StringVar(self)"
536 if eval(source):
537 exec target + ".set(" + source + ")"
538 elif x[1] == 'Int':
539 exec target + " = IntVar(self)"
540 if eval(source):
541 exec target + ".set(" + source + ")"
542
543 def gather(self):
544 for x in self.server.typemap:
545 setattr(self.server, x[0], getattr(self, x[0]).get())
546
547 def nosave(self):
548 if ConfirmQuit(self, 'server option editing'):
549 Widget.destroy(self.master)
550
551 def save(self):
552 self.gather()
553 self.sitelist.append(self.server)
554 Widget.destroy(self.master)
555
556 def refreshPort(self):
557 proto = self.protocol.get()
558 self.port.set(defaultports[proto])
559 if not proto in ("POP3", "APOP", "KPOP"): self.uidl = FALSE
560
561 def createWidgets(self, host):
562 topwin = Frame(self, relief=RAISED, bd=5)
563 Label(topwin, text="Server options for " + host).pack(side=TOP,pady=10)
564 Button(topwin, text='Save', fg='blue',
565 command=self.save).pack(side=LEFT)
566 Button(topwin, text='Quit', fg='blue',
567 command=self.nosave).pack(side=LEFT)
568 Button(topwin, text='Help', fg='blue',
569 command=lambda: helpwin(serverhelp)).pack(side=RIGHT)
570 topwin.pack(fill=X)
571
572 ctlwin = Frame(self, relief=RAISED, bd=5)
573 Label(ctlwin, text="Run Controls").pack(side=TOP)
574 Checkbutton(ctlwin, text='Poll ' + host + ' normally?', variable=self.active).pack(side=TOP)
575 LabeledEntry(ctlwin, 'True name of ' + host + ':',
576 self.via, '30').pack(side=TOP, fill=X)
577 LabeledEntry(ctlwin, 'Cycles to skip between polls:',
578 self.interval, '30').pack(side=TOP, fill=X)
579 LabeledEntry(ctlwin, 'Server timeout (seconds):',
580 self.timeout, '30').pack(side=TOP, fill=X)
581 Button(ctlwin, text='Help', fg='blue',
582 command=lambda: helpwin(controlhelp)).pack(side=RIGHT)
583 ctlwin.pack(fill=X)
584
585 protwin = Frame(self, relief=RAISED, bd=5)
586 Label(protwin, text="Protocol and Port").pack(side=TOP)
587 pb = ButtonBar(protwin, 'Protocol:', self.protocol, protolist, self.refreshPort)
588 LabeledEntry(protwin, 'TCP/IP service port to query:',
589 self.port, '30').pack(side=TOP, fill=X)
590 Checkbutton(protwin,
591 text="Track seen POP3 messages with client-side UIDL list?",
592 variable=self.uidl).pack(side=TOP)
593 Button(protwin, text='Help', fg='blue',
594 command=lambda: helpwin(protohelp)).pack(side=RIGHT)
595 protwin.pack(fill=X)
596
597 secwin = Frame(self, relief=RAISED, bd=5)
598 Label(secwin, text="Security").pack(side=TOP)
599 ButtonBar(secwin, 'Authorization mode:',
600 self.auth, authlist, None).pack(side=TOP)
601
602 if os.popen("uname").readlines()[0] == 'Linux\n':
603 LabeledEntry(secwin, 'Interface to check before polling:',
604 self.interface, '30').pack(side=TOP, fill=X)
605 LabeledEntry(secwin, 'IP addresses to watch for activity:',
606 self.monitor, '30').pack(side=TOP, fill=X)
607
608 Button(secwin, text='Help', fg='blue',
609 command=lambda: helpwin(sechelp)).pack(side=RIGHT)
610 secwin.pack(fill=X)
611
612 mdropwin = Frame(self, relief=RAISED, bd=5)
613 Label(mdropwin, text="Multidrop options").pack(side=TOP)
614 LabeledEntry(mdropwin, 'Envelope address header:',
615 self.envelope, '30').pack(side=TOP, fill=X)
616 Checkbutton(mdropwin, text="Enable multidrop DNS lookup?",
617 variable=self.dns).pack(side=TOP)
618 Label(mdropwin, text="DNS aliases").pack(side=TOP)
619 ListEdit("New site alias: ", self.server.aka, None, mdropwin)
620 Label(mdropwin, text="Domains to be considered local").pack(side=TOP)
621 ListEdit("New local domain: ", self.server.localdomains, None,mdropwin)
622 Button(mdropwin, text='Help', fg='blue',
623 command=lambda: helpwin(multihelp)).pack(side=RIGHT)
624 mdropwin.pack(fill=X)
625
626 userwin = Frame(self, relief=RAISED, bd=5)
627 Label(userwin, text="User entries for " + host).pack(side=TOP)
628 ListEdit("New user: ", None, self.edituser, userwin)
629 userwin.pack(fill=X)
630
631 def edituser(self, user):
632 UserEdit(user, self.server.userlist, Toplevel())
633
634 #
635 # User editing stuff
636 #
637
638 userhelp = {
639 'title' : 'User option help',
640 'banner': 'User options',
641 'text' : """
642 FIXME
643 """}
644
645 class UserEdit(Frame):
646 def __init__(self, user, userlist, master=None):
647 Frame.__init__(self, master)
648 Pack.config(self)
649 self.master.title('Fetchmail user ' + user);
650 self.master.iconname('Fetchmail user ' + user);
651 self.user = User()
652 self.user.remote = user
653 self.user.localnames = [user]
654 self.userlist = userlist
655 self.post()
656 self.createWidgets(user)
657
658 def post(self):
659 # we can't abstract this away, execs would happen in the wrong scope
660 for x in self.user.typemap:
661 target = "self." + x[0]
662 source = "self.user." + x[0]
663 if x[1] == 'Boolean':
664 exec target + " = BooleanVar(self)"
665 if eval(source):
666 exec target + ".set(" + source + ")"
667 elif x[1] == 'String':
668 exec target + " = StringVar(self)"
669 if eval(source):
670 exec target + ".set(" + source + ")"
671 elif x[1] == 'Int':
672 exec target + " = IntVar(self)"
673 if eval(source):
674 exec target + ".set(" + source + ")"
675
676 def gather(self):
677 for x in self.user.typemap:
678 setattr(self.user, x[0], getattr(self, x[0]).get())
679
680 def nosave(self):
681 if ConfirmQuit(self, 'user option editing'):
682 Widget.destroy(self.master)
683
684 def save(self):
685 self.gather()
686 self.userlist.append(self.user)
687 Widget.destroy(self.master)
688
689 def createWidgets(self):
690 topwin = Frame(self, relief=RAISED, bd=5)
691 Label(topwin,
692 text="User options for " + self.user.remote).pack(side=TOP,pady=10)
693 Button(topwin, text='Save', fg='blue',
694 command=self.save).pack(side=LEFT)
695 Button(topwin, text='Quit', fg='blue',
696 command=self.nosave).pack(side=LEFT)
697 Button(topwin, text='Help', fg='blue',
698 command=lambda: helpwin(userhelp)).pack(side=RIGHT)
699 topwin.pack(fill=X)
700
701 secwin = Frame(self, relief=RAISED, bd=5)
702 Label(secwin, text="Authentication").pack(side=TOP)
703 LabeledEntry(mdropwin, 'Password:',
704 self.password, '30').pack(side=TOP, fill=X)
705 LabeledEntry(mdropwin, 'Remote folder:',
706 self.folder, '30').pack(side=TOP, fill=X)
707 secwin.pack(fill=X)
708
709 names = Frame(self, relief=RAISED, bd=5)
710 Label(names, text="Local names").pack(side=TOP)
711 ListEdit("New local name: ", self.localnames, None, names)
712 names.pack(fill=X)
713
714 targwin = Frame(self, relief=RAISED, bd=5)
715 Label(targwin, text="Forwarding Options").pack(side=TOP)
716 LabeledEntry(targwin, 'System to forward to:',
717 self.smtphost, '30').pack(side=TOP, fill=X)
718 LabeledEntry(targwin, 'Connection setup command:',
719 self.preconnect, '30').pack(side=TOP, fill=X)
720 LabeledEntry(targwin, 'Connection wrapup command:',
721 self.postconnect, '30').pack(side=TOP, fill=X)
722 LabeledEntry(targwin, 'Local delivery agent:',
723 self.mda, '30').pack(side=TOP, fill=X)
724 targwin.pack(fill=X)
725
726 optwin = Frame(self, relief=RAISED, bd=5)
727 Checkbutton(optwin, "Suppress deletion of messages after reading",
728 self.keep)
729 Checkbutton(optwin, "Flush seen messages before retrieval",
730 self.flush)
731 Checkbutton(optwin, "Fetch old messages as well as new",
732 self.fetchall)
733 Checkbutton(optwin, "Rewrite To/Cc/Bcc messages to enable reply",
734 self.rewrite)
735 Checkbutton(optwin, "Force CR/LF at end of each line",
736 self.forcecr)
737 Checkbutton(optwin, "Strip CR from end of eacgh line",
738 self.stripcr)
739 Checkbutton(optwin, "Pass 8 bits even theough SMTP says 7BIT",
740 self.pass8bits)
741 Checkbutton(optwin, "Drop Status lines from forwarded messages",
742 self.dropstatus)
743 optwin.pack(fill=X)
744
745 limwin = Frame(self, relief=RAISED, bd=5)
746 Label(limwin, text="Resource Limits").pack(side=TOP)
747 LabeledEntry(limwin, 'Message size limit:',
748 self.limit, '30').pack(side=TOP, fill=X)
749 LabeledEntry(limwin, 'Maximum messages to fetch each poll:',
750 self.fetchlimit, '30').pack(side=TOP, fill=X)
751 LabeledEntry(limwin, 'Maximum messages to forward each poll:',
752 self.batchlimit, '30').pack(side=TOP, fill=X)
753 limwin.pack(fill=X)
754
755 #
756 # Configure drives the configuration dialogue. It may call multiple
757 # instances of ServerEdit to do its job.
758 #
759
760 class Configure(Frame, ControlEdit):
761 def __init__(self, master=None):
762 Frame.__init__(self, master)
763 self.master.title('fetchmail configurator');
764 self.master.iconname('fetchmail configurator');
765 Pack.config(self)
766 self.MakeDispose()
767 self.controls = Controls()
768 self.PostControls()
769 self.MakeSitelist(master)
770 self.sites = []
771
772 def MakeDispose(self):
773 # Set the disposal of the given configuration
774 dispose = Frame(self, relief=RAISED, bd=5);
775 Label(dispose,
776 text='Configurator Controls',
777 bd=2).pack(side=TOP, pady=10)
778 Button(dispose, text='Save', fg='blue',
779 command=self.save).pack(side=LEFT)
780 Button(dispose, text='Quit', fg='blue',
781 command=self.nosave).pack(side=LEFT)
782 Button(dispose, text='Help', fg='blue',
783 command=lambda: helpwin(confighelp)).pack(side=RIGHT)
784 dispose.pack(side=TOP, fill=X);
785
786 def MakeSitelist(self, master):
787 lf = Frame(master, relief=RAISED, bd=5)
788 Label(lf,
789 text='Remote Mail Server Configurations',
790 bd=2).pack(side=TOP, pady=10)
791 ListEdit('New Server:', None, self.editsite, lf)
792 lf.pack(fill=X)
793
794 def editsite(self, site):
795 ServerEdit(site, self.sites, Toplevel())
796
797 def save(self):
798 self.GatherControls()
799 sys.stdout.write("# Configuration created %s\n" % time.ctime(time.time()))
800 sys.stdout.write(`self.controls`)
801 for site in self.sites:
802 sys.stdout.write(`site`)
803 for user in self.sites.userlist:
804 sys.stdout.write(`user`)
805 self.quit()
806
807 def nosave(self):
808 if ConfirmQuit(self, "configuration editor"):
809 self.quit()
810
811 if __name__ == '__main__':
812 ServerDefaults = Server()
813 UserDefaults = User()
814 Configure().mainloop()
815
816 # The following sets edit modes for GNU EMACS
817 # Local Variables:
818 # mode:python
819 # End: