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 8from Tkinter import * 9from Dialog import * 10import sys 11import time 12import os 13 14# 15# Define the data structures the GUIs will be tossing around 16# 17class 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 37class 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 131class 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 213defaultports = {"auto":0, 214 "POP2":109, 215 "POP3":110, "APOP":110, "KPOP":1109, "IMAP":143, 216 "IMAP-K4":143, 217 "ETRN":25} 218 219protolist = ("auto", "POP2", "POP3", "APOP", "KPOP", "IMAP", "IMAP-K4", "ETRN") 220 221authlist = ("password", "kerberos") 222 223def 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 232class 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 245def 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 255def 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 267class 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 326def 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 338confighelp = { 339 'title' : 'Fetchmail configurator help', 340 'banner': 'Configurator help', 341 'text' : """ 342In the `Configurator Controls' panel, you can: 343 344Press `Save' to save the new fetchmail configuration you have created. 345Press `Quit' to exit without saving. 346Press `Help' to bring up this help message. 347 348In the `Configurator Controls' panel, you can set the following options that 349control how fetchmail runs: 350 351Poll interval 352 Number of seconds to wait between polls in the background. 353 Ignored if the `Run in Foreground?' option is on. 354 355Logfile 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 360In the `Remote Mail Configurations' panel, you can: 361 3621. Enter the name of a new remote mail server you want fetchmail to query. 363 364To 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 366you are using ssh or some other tunneling method and will fill in the `via' 367option on the site configuration screen). 368 3692. Change the configuration of an existing site. 370 371To do this, find the site's label in the listbox and double-click it. 372This will take you to a site configuration dialogue. 373"""} 374 375class 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# 430serverhelp = { 431 'title' : 'Server options help', 432 'banner': 'Server Options', 433 'text' : """ 434The server options screen controls fetchmail 435options that apply to one of your mailservers. 436 437Once you have a mailserver configuration set 438up as you like it, you can select `Save' to 439store it in the server list maintained in 440the main configuration window. 441 442If you wish to discard changes to a server 443configuration, select `Quit'. 444"""} 445 446controlhelp = { 447 'title' : 'Run Control help', 448 'banner': 'Run Controls', 449 'text' : """ 450If the `Poll normally' checkbox is on, the host is polled as part of 451the normal operation of fetchmail when it is run with no arguments. 452If it is off, fetchmail will only query this host when it is given as 453a command-line argument. 454 455The `True name of server' box should specify the actual DNS name 456to query. By default this is the same as the poll name. 457 458Normally each host described in the file is queried once each 459poll cycle. If `Cycles to skip between polls' is greater than 0, 460that's the number of poll cycles that are skipped between the 461times this post is actually polled. 462 463The `Server timeout' is the number of seconds fetchmail will wait 464for a reply from the mailserver before concluding it is hung and 465giving up. 466"""} 467 468protohelp = { 469 'title' : 'Protocol and Port help', 470 'banner': 'Protocol and Port', 471 'text' : """ 472These options control the remote-mail protocol 473and TCP/IP service port used to query this 474server. 475 476The `Protocol' button bar offers you a choice of 477all the different protocols available. The `auto' 478protocol is a special mode that probes the host 479ports for POP3 and IMAP to see if either is 480available. 481 482Normally the TCP/IP service port to use is 483dictated by the protocol choice. The `Port' 484field lets you set a non-standard port. 485"""} 486 487sechelp = { 488 'title' : 'Security option help', 489 'banner': 'Security', 490 'text' : """ 491These options control the security procedure used 492to protect mail transfer 493 494Normally the mail fetch is validated using an 495ordinary password logon. If your server speaks 496MIT Kerberos IV it is possible to pre-authenticate 497the exxchange with a Kerberos ticket. 498 499The `interface' and `monitor' options are available 500only for Linux systems. See the fetchmail manual page 501for details on these. 502"""} 503 504multihelp = { 505 'title' : 'Multidrop option help', 506 'banner': 'Multidrop', 507 'text' : """ 508These options are only useful with multidrop mode. 509See the manual page for extended discussion. 510"""} 511 512class 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 638userhelp = { 639 'title' : 'User option help', 640 'banner': 'User options', 641 'text' : """ 642FIXME 643"""} 644 645class 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 760class 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 811if __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: 820