Dangermouse ----------- Negative key caching Handle spaces in locations Keys are not cached until they are used to verify another key or message. This has corollaries: On startup, user's public key is not written to the cache Domain keys are never cached Need to rethink this to decide whether to emulate Gale behavior (save a key if it is verified) or retain this behavior. FuguWin ------- Make user download win32api extensions in addition to py152, if they don't have either installed. Installer should use ShellExecute instead of CMD.exe. Fix select/copy/paste? Set up GALE_SYS_DIR and FUGU_DIR to point to different directories as appropriate. Peter sez: GALE_SYS_DIR on Windows is '/gale'. The user dir used to be '/', but I just looked and it seems to have changed to just . I'll probably change it back. I've been debating installing the user dir in \Windows\Profiles\ which is the place you are supposed to put user files on Windows, but I like to keep everything in one directory. PyGale features --------------- gkgen Make pygale API not require callbacks anywhere. Bugs ---- Sender colorization doesn't work on delayed puff verification (but I don't really care) No public/private puff warning on Windows? Is there a memory leak? I just turned on puff history persistence on all screens. Next time I quit, check the memory usage and see if it's the same after a restart. There may be public keys cached in memory, but that shouldn't total 10M. Things to test for symlink keys: (all should work in 0.9.9c) sending to sym.tlau@cs (a sym key to tlau@cs) doing so when the sym key isn't in the cache sending to symA.tlau@cs (a symlink loop with symB.tlau@cs) sending to symC.tlau@cs (a symlink to a nonexistent key) Also, an existing bug seems to be that if you encrypt a message to someone whose key isn't in the cache, the key doesn't get saved to the cache. In fact, how could it? You don't know whether the key you retrieved was valid or not. But it looks like gsend puts it in the cache nevertheless. Fugu doesn't currently cache keys, but perhaps it should. (Gsend caches keys, but will never overwrite a newer key with an older one.) Starting up multiple copies seems to hose the fuguconfig.xml file. Check for missing/bad private key either in setup wizard or when Fugu starts up. UI features ----------- An option to have the "subscribe to" do both . and / for you (sjh) Dock and undock the screen buttonbar; let it float so that people can bring up the main window only as necessary. Color icon on new puffs Slim mode XML configuration for puff displays, better configuration for colors. Ryan wants to disable the status bar Save puffs to file (need to write a nice-looking file selector dialog that doesn't prompt you whether you want to overwrite a file or not!) Make sender color customization be a list, not a dict Resend/forward puff Copy entire puff to clipboard Email puff via rightclick menu Researchy features ------------------ Predict category based on: text of puff previous categories, weighted over time and volume previously puffed to categories user interests Related work: Darragh & Witten's Reactive Keyboard Laird & Saul's TDAG Hidden Markov models text classification (not clustering) JTR's comments -------------- 5 - Having the thumbnails wrap with the text is a little unattractive. Maybe the puff box could have a vertical line near the left side dividing it in two? I think this would make the full size thumbnails more appealing, too. 6 - Having detached the send box, I'd like for things like C-R to still affect it. I detached the send box, moved it over to the side, and expanded the puff pane to fill the whole thing, and found that it wasn't nearly as handy when I lost my favorite key binding. Puff flow control ----------------- Sources, sinks, filters. Don't forget about color-coding puffs based on some attribute. Either the puff renderer ought to be paramaterizable, or there should be a filter that sets the "color" of a puff. Windows stack traces -------------------- pygale:250:verify callback(self) pygale:693:lambda p=p.verify(lambda p, s=self: s.verify_done(p)) pygale:712:verify_done self._puff_callback(puff) TypeError: call of non-function (type None) Stack trace from molly (perhaps related to new keys?) ---------------------- File "fugu.py", line 26, in ? main() File "fugu.py", line 23, in main ui.main() File "ui.py", line 2494, in main m.mainloop() File "/usr/lib/python1.5/lib-tk/Tkinter.py", line 492, in mainloop self.tk.mainloop(n) File "engine.py", line 125, in File "engine.py", line 143, in read_ready File "pygale/pygale.py", line 676, in handle_read self._puffs.append(self.process_puff(puffdata)) File "pygale/pygale.py", line 715, in process_puff p = p.verify(lambda p, s=self: s._puff_callback(p)) File "pygale/pygale.py", line 262, in verify p.__fragments.update(gale_pack.group_to_dict(frag)) File "pygale/gale_pack.py", line 100, in group_to_dict (frag_name_len, frag) = pop_int(frag) File "pygale/gale_pack.py", line 132, in pop_int num = unpack32bit(p[:4]) File "pygale/gale_pack.py", line 33, in unpack32bit return (ord(s[0])<<24) | (ord(s[1])<<16) \ IndexError: string index out of range Dan's stack trace on quit: -------------------------- Error: 1 IOError Exception in Tk callback Function: (type: ) Args: () Traceback (innermost last): File "/usr/lib/python1.5/site-packages/Pmw/Pmw_0_8/lib/PmwBase.py", line 1349, in __call__ return apply(self.func, args) File "/usr/local/fugu/lib/fugu-0.9.8a/ui.py", line 899, in quit Config.persist_pufflog(FUGUDIR) File "/usr/local/fugu/lib/fugu-0.9.8a/config.py", line 644, in persist_pufflog traceback.print_exc() File "/tmp/py-root/usr/lib/python1.5/traceback.py", line 135, in print_exc print_exception(etype, value, tb, limit, file) File "/tmp/py-root/usr/lib/python1.5/traceback.py", line 79, in print_exception _print(file, 'Traceback (innermost last):') File "/tmp/py-root/usr/lib/python1.5/traceback.py", line 9, in _print file.write(str+terminator) IOError: [Errno 5] Input/output error Heath's iconify trace: Error: 1 AttributeError Exception in Tk callback Function: at 8315f18> (type: ) Args: (,) Event type: KeyPress (type num: 2) Traceback (innermost last): File "/xyz/apps/fugu/lib/fugu-0.9.7f/Pmw/Pmw_0_8_1/lib/PmwBase.py", line 1480, in __call__ return apply(self.func, args) File "/xyz/apps/fugu/lib/fugu-0.9.9/ui.py", line 959, in self._puffwidget.bind('', lambda e, s=self: AttributeError: iconify Thumbnail trace: File "fugu.py", line 39, in ? main() File "fugu.py", line 36, in main ui.main() File "ui.py", line 2066, in main m.mainloop() File "/usr/lib/python1.5/lib-tk/Tkinter.py", line 492, in mainloop self.tk.mainloop(n) File "engine.py", line 125, in File "engine.py", line 143, in read_ready File "pygale/asyncurl.py", line 79, in fetch_url3 self.callback(self.data) File "thumbcache.py", line 100, in asyncurl.fetch_url(thumburl, lambda data, f=thumbimg, ef=errorfunc, File "thumbcache.py", line 126, in getthumb2 getthumb3(id, thumbimg, errorfunc) File "thumbcache.py", line 135, in getthumb3 os.unlink(thumbimg) OSError: [Errno 2] No such file or directory: '/home/tlau/.gale/thumbs/mikef@praxis.etla.net' Dan, after restarting his galed: Traceback (innermost last): File "/xyz/bin/fugu", line 39, in ? main() File "/xyz/bin/fugu", line 36, in main ui.main() File "/xyz/apps/fugu/lib/fugu-0.9.9/ui.py", line 2054, in main m.mainloop() File "/usr/lib/python1.5/lib-tk/Tkinter.py", line 492, in mainloop self.tk.mainloop(n) File "/xyz/apps/fugu/lib/fugu-0.9.9/pygale/engine.py", line 125, in Tkinter.READABLE, lambda f, m, s=self, h=handle: File "/xyz/apps/fugu/lib/fugu-0.9.9/pygale/engine.py", line 143, in read_ready self._callback_map[handle]() File "/xyz/apps/fugu/lib/fugu-0.9.9/pygale/pygale.py", line 549, in self._engine.add_callback(sock, lambda self=self, s=sock, h=host, File "/xyz/apps/fugu/lib/fugu-0.9.9/pygale/pygale.py", line 569, in connect3 self._on_connect(host) File "/xyz/apps/fugu/lib/fugu-0.9.9/ui.py", line 357, in self._galeconn.set_onconnect(lambda h, r=reconnect, s=self: File "/xyz/apps/fugu/lib/fugu-0.9.9/ui.py", line 371, in on_sendconn self._galeconn.sub_to(akd_cat) File "/xyz/apps/fugu/lib/fugu-0.9.9/pygale/pygale.py", line 617, in sub_to self._sock.send(req) socket.error: (32, 'Broken pipe') If you try to use someone else's fuguconfig.xml you get this nasty trace: [...] File "/usr/local/lib/fugu-0.9.9b/pygale/sign.py", line 214, in decode_sig(signature, msg_data, lambda k, u=keyobj, c=callback: File "/usr/local/lib/fugu-0.9.9b/pygale/sign.py", line 232, in decode_done callback(upper_key) File "/usr/local/lib/fugu-0.9.9b/pygale/authcache.py", line 293, in self.find_pubkey_fromdisk(keyobj, lambda k, s=self, cb=callback: File "/usr/local/lib/fugu-0.9.9b/pygale/authcache.py", line 298, in find_pubkey2 callback(keyobj) File "/usr/local/lib/fugu-0.9.9b/pygale/pygale.py", line 830, in authcache.find_pubkey(keyobj, lambda k, c=cb: lookup_id_done(k, c)) File "/usr/local/lib/fugu-0.9.9b/pygale/pygale.py", line 846, in lookup_id_done callback((key.name(), key.comment())) File "/usr/local/lib/fugu-0.9.9b/pygale/pygale.py", line 898, in lookup_id(userid, lambda n, id=userid, p=p, c=conn: File "/usr/local/lib/fugu-0.9.9b/pygale/pygale.py", line 907, in finish_notify_in p = p.sign_message(namepair[0]) File "/usr/local/lib/fugu-0.9.9b/pygale/pygale.py", line 149, in sign_message privkey = authcache.private(signer) File "/usr/local/lib/fugu-0.9.9b/pygale/authcache.py", line 327, in private return apply(KeyCache.private, args) File "/usr/local/lib/fugu-0.9.9b/pygale/authcache.py", line 52, in private return self.__private[name] KeyError: corin@cs.washington.edu File "fugu.py", line 39, in ? main() File "fugu.py", line 36, in main ui.main() File "ui.py", line 2198, in main m.mainloop() File "/usr/lib/python1.5/lib-tk/Tkinter.py", line 492, in mainloop self.tk.mainloop(n) File "engine.py", line 125, in File "engine.py", line 143, in read_ready File "pygale/asyncurl.py", line 79, in fetch_url3 self.callback(self.data) File "thumbcache.py", line 127, in asyncurl.fetch_url(thumburl, lambda data, f=thumbimg, ef=errorfunc, File "thumbcache.py", line 153, in getthumb2 getthumb3(id, thumbimg, errorfunc) File "thumbcache.py", line 168, in getthumb3 for (t, cb) in THUMB_CBS[id]: KeyError: lbedford@dublin.wbtsystems.com While I was away for AAAI: Error: 1 TypeError Exception in Tk callback Function: (type: ) Args: () Traceback (innermost last): File "/usr/lib/python1.5/site-packages/Pmw-0.8.4/Pmw_0_8_4/lib/PmwBase.py", line 1690, in __call__ return apply(self.func, args) File "/usr/lib/python1.5/lib-tk/Tkinter.py", line 227, in callit apply(func, args) File "connection.py", line 201, in timer_callback engine.timeout.process() File "pygale/engine.py", line 32, in process callback() File "pygale/authcache.py", line 181, in timeout_callback = lambda k=keyobj, s=self, l=client:\ File "pygale/authcache.py", line 214, in handle_AKD_timeout self.cleanup_akd(key) File "pygale/authcache.py", line 204, in cleanup_akd cb(keyobj) File "pygale/sign.py", line 313, in authcache.find_pubkey_akd(key, lambda key, d=sigdata, m=msg_data, File "pygale/sign.py", line 326, in decode_sig4 authcache.save_pubkey_todisk(key) File "pygale/authcache.py", line 339, in save_pubkey_todisk return apply(KeyCache.save_pubkey_todisk, args) File "pygale/authcache.py", line 278, in save_pubkey_todisk f.write(keyobj.data()) TypeError: read-only buffer, None Dan's reconnect bug: Restart galed Reconnect in Fugu the automatic connect is already in progress a single screen connects twice; once to ofb.net immediately (because of the manual reconnect), and once to ofb.net.gale.org (perhaps because of the automatic connect) John Meacham's feature requests: random feature requests: -searchable puffs, -multiple views of all puffs in buffer, with the ability to restrict the puffs in a given window arbitrarily, like only those from author foo, or only those that contain regex bar. -ability to snarf puffs from some sort of log upon startup. or populate puff buffer from log when changing subscriptions... -ability to call external editor for the sending of puffs... , reply to (author/cat) would spawn an editor on a temporary file, fugu would send the puff once it gets the SIGCHLD (notification of death of editor) if the tempory file was modified. (similar to how mutt spawns vi externally). -ability to pipe selected puff or puffs through arbitrary commands. -ability to select an arbitrary set of puffs to perform these complicated operations on. if just one were to be implemented i would like to see the external editor, this also gives the ability to compose multiple puffs at once for free :) JTR: I think I might dig it if metadata like "Subscribed to gib:ber:ish" was set off in a different font and with horizontal rules above. if os.path.exists(conffile): confvars = gale_env.parse_sys_conf(conffile) result = 'Reading Gale settings from ' + conffile conffile can be, say, "conf" with no path attached at this point, which means that if there's a file or directory called "conf" in the current directory it will try to open it. This should, perhaps, be in a try block, and it should handle the case where conffile has a directory path better. Shayde/Liam's traceback on public unsigned key: File "./roe.py", line 24, in evhandler for i in c.next_puffs(): File "./pygale/pygale.py", line 716, in next_puffs self._engine.process(0) File "./pygale/engine.py", line 83, in process callback() File "./pygale/pygale.py", line 701, in handle_read self._puffs.append(self.process_puff(puffdata)) File "./pygale/pygale.py", line 740, in process_puff p = p.verify(lambda p, s=self: s._puff_callback(p)) File "./pygale/pygale.py", line 256, in verify callback(self) File "./pygale/pygale.py", line 740, in p = p.verify(lambda p, s=self: s._puff_callback(p)) File "./pygale/authcache.py", line 183, in client.set_puff_callback(lambda p, s=self, k=keyobj, File "./pygale/authcache.py", line 240, in handle_key_puff sign.import_pubkey(keyobj, self.finish_key_puff, 0) File "./pygale/sign.py", line 230, in import_pubkey decode_sig(signature, msg_data, lambda k, u=keyobj, c=callback: NameError: msg_data When my home directory (with the thumbcache) goes down: 10-19 17:31:39 Caused a crash out of the blue on apathy: Traceback (most recent call last): File "fugu.py", line 42, in ? main() File "fugu.py", line 39, in main ui.main() File "ui.py", line 2259, in main else: File "/uns/share/lib/python1.6/lib-tk/Tkinter.py", line 494, in mainloop self.tk.mainloop(n) File "/scratch/tlau/fugu/pygale/engine.py", line 151, in Tkinter.WRITABLE, lambda f, m, s=self, h=handle: File "/scratch/tlau/fugu/pygale/engine.py", line 168, in write_ready self._callback_write_map[handle]() File "pygale/asyncurl.py", line 65, in fetch_url2 self.callback(None) File "thumbcache.py", line 229, in if DEBUG: print 'going to getthumb3 after saving to disk' File "thumbcache.py", line 247, in getthumb2 img = bigimg.subsample(2) KeyError: hozer@altus.drgw.net Crash out of the blue on molly, probably having to do with wtanaka's puff: Traceback (most recent call last): File "fugu.py", line 42, in ? main() File "fugu.py", line 39, in main ui.main() File "ui.py", line 2367, in main m.mainloop() File "/usr/local/lib/python2.0/lib-tk/Tkinter.py", line 931, in mainloop self.tk.mainloop(n) File "/home/tlau/src/fugu/pygale/engine.py", line 145, in Tkinter.READABLE, lambda f, m, s=self, h=handle: File "/home/tlau/src/fugu/pygale/engine.py", line 165, in read_ready self._callback_map[handle]() File "/home/tlau/src/fugu/pygale/pygale.py", line 715, in handle_read self._puffs.append(self.process_puff(puffdata)) File "/home/tlau/src/fugu/pygale/pygale.py", line 752, in process_puff p = p.verify(lambda p, s=self: s._verify_callback(p)) File "/home/tlau/src/fugu/pygale/pygale.py", line 276, in verify sign.decode_sig(sig, frag, lambda key, s=self, p=p, c=callback: File "/home/tlau/src/fugu/pygale/sign.py", line 99, in decode_sig import_pubkey(keyobj, lambda k, d=sigdata, File "/home/tlau/src/fugu/pygale/sign.py", line 128, in import_pubkey callback(keyobj) File "/home/tlau/src/fugu/pygale/sign.py", line 99, in import_pubkey(keyobj, lambda k, d=sigdata, File "/home/tlau/src/fugu/pygale/sign.py", line 316, in decode_sig2 authcache.find_pubkey_fromdisk(key, lambda key, d=sigdata, m=msg_data, File "/home/tlau/src/fugu/pygale/authcache.py", line 344, in find_pubkey_fromdisk return apply(KeyCache.find_pubkey_fromdisk, args) File "/home/tlau/src/fugu/pygale/authcache.py", line 104, in find_pubkey_fromdisk path = os.path.join(prefix, suffix, name) File "/usr/local/lib/python2.0/posixpath.py", line 43, in join if b[:1] == '/': TypeError: unsliceable object TypeError Exception in Tk callback Function: (type: ) Args: () Traceback (innermost last): File "/usr/local/lib/python2.0/site-packages/Pmw/Pmw_0_8_1/lib/PmwBase.py", line 1480, in __call__ return apply(self.func, args) File "/usr/local/lib/python2.0/lib-tk/Tkinter.py", line 437, in callit apply(func, args) File "ui.py", line 645, in text.after_idle(lambda pl=conf.pufflist(), s=self, c=conf: File "ui.py", line 688, in idle_pufflist conf.addpuff(p, 0) File "connection.py", line 369, in addpuff time.localtime(msg.get('id/time'))) TypeError: illegal argument type for built-in operation Crash on startup when no gale server can be found: Traceback (innermost last): File "/usr/local/bin/fugu", line 42, in ? main() File "/usr/local/bin/fugu", line 39, in main ui.main() File "/usr/local/fugu/lib/fugu-0.9.9f/ui.py", line 2349, in main m.mainloop() File "/usr/lib/python1.5/lib-tk/Tkinter.py", line 492, in mainloop self.tk.mainloop(n) File "/usr/local/fugu/lib/fugu-0.9.9f/pygale/engine.py", line 145, in Tkinter.READABLE, lambda f, m, s=self, h=handle: File "/usr/local/fugu/lib/fugu-0.9.9f/pygale/engine.py", line 165, in read_re a dy self._callback_map[handle]() File "/usr/local/fugu/lib/fugu-0.9.9f/pygale/pygale.py", line 562, in self._engine.add_callback(sock, lambda self=self, s=sock, h=host, File "/usr/local/fugu/lib/fugu-0.9.9f/pygale/pygale.py", line 589, in connect 3 self._on_connect(host) File "/usr/local/fugu/lib/fugu-0.9.9f/connection.py", line 155, in self._galec.set_onconnect(lambda h, s=self, sub=sub: File "/usr/local/fugu/lib/fugu-0.9.9f/connection.py", line 169, in make_subs self.register_filehandler() File "/usr/local/fugu/lib/fugu-0.9.9f/connection.py", line 184, in register_f i lehandler self._galec.set_puff_callback(lambda p, s=self: s._master.recvpuff(s, p)) File "/usr/local/fugu/lib/fugu-0.9.9f/pygale/pygale.py", line 665, in set_puf f _callback self._engine.add_callback(self._sock, self.handle_read) File "/usr/local/fugu/lib/fugu-0.9.9f/pygale/engine.py", line 144, in add_cal l back Tkinter.tkinter.createfilehandler(handle.fileno(), AttributeError: 'None' object has no attribute 'fileno' ---------------------------------------------------------------------- Setup wizard does not name private key file properly ---------------------------------------------------------------------- Related to network issues via firewall/RoadRunner: Traceback (most recent call last): File "fugu.py", line 46, in ? main() File "fugu.py", line 43, in main ui.main() File "ui.py", line 2266, in main m.mainloop() File "/usr/local/lib/python2.1/lib-tk/Tkinter.py", line 930, in mainloop self.tk.mainloop(n) File "pygale/engine.py", line 154, in Tkinter.READABLE, lambda f, m, s=self, h=handle: File "pygale/engine.py", line 174, in read_ready self._callback_map[handle]() File "pygale/pygale.py", line 644, in self._engine.add_callback(sock, lambda self=self, s=sock, h=host, File "pygale/pygale.py", line 651, in connect3 self.__sockets_in_progress.remove(sock) ValueError: list.remove(x): x not in list OFB going up/down causes Fugu crash: Setting debugging GC Traceback (most recent call last): File "fugu.py", line 46, in ? main() File "fugu.py", line 43, in main ui.main() File "ui.py", line 2266, in main m.mainloop() File "/usr/local/lib/python2.1/lib-tk/Tkinter.py", line 930, in mainloop self.tk.mainloop(n) File "pygale/engine.py", line 154, in Tkinter.READABLE, lambda f, m, s=self, h=handle: File "pygale/engine.py", line 174, in read_ready self._callback_map[handle]() File "pygale/pygale.py", line 644, in self._engine.add_callback(sock, lambda self=self, s=sock, h=host, File "pygale/pygale.py", line 672, in connect3 self._on_connect(host) File "ui.py", line 370, in self._galeconn.set_onconnect(lambda h, r=reconnect, s=self: File "ui.py", line 383, in on_sendconn self.create_akd_server(reconnect) File "ui.py", line 388, in create_akd_server self._galeconn.sub_to([akd_cat], cb) File "pygale/pygale.py", line 719, in sub_to lookup_all_locations(loc_list, lambda l, s=self, cb=cb: File "pygale/pygale.py", line 1350, in lookup_all_locations lookup_location(loc, loc_cb, do_akd=0) File "pygale/pygale.py", line 1244, in lookup_location cb(loc, ['']) File "pygale/pygale.py", line 1348, in loc_cb = lambda new_loc, keylist, l=loc, c=collector, cb=new_cb:\ File "pygale/pygale.py", line 1365, in _loc_coll_callback cb(collector.getvalues()) File "pygale/pygale.py", line 1343, in new_cb = lambda keys, cb=cb:\ File "pygale/pygale.py", line 1372, in lookup_all_locations_akd callback(loc_key_list) File "pygale/pygale.py", line 719, in lookup_all_locations(loc_list, lambda l, s=self, cb=cb: File "pygale/pygale.py", line 761, in sub_to2 self._sock.send(req) socket.error: (32, 'Broken pipe') On Joshua's machine: Fixed. Caused by a '&' in the GECOS field not being escaped in the XML. Traceback (most recent call last): File "/usr/X11R6/lib/fugu-1.1.6/config.py", line 604, in load_default_config self.load_vars(parser.tree) File "/usr/X11R6/lib/fugu-1.1.6/config.py", line 524, in load_vars 'presence') File "/usr/X11R6/lib/fugu-1.1.6/config.py", line 347, in getlist return node.getlist(nodename) AttributeError: 'None' object has no attribute 'getlist' Unable to load default configuration No configuration file found. Using defaults. Traceback (most recent call last): File "/usr/X11R6/bin/fugu", line 46, in ? main() File "/usr/X11R6/bin/fugu", line 43, in main ui.main() File "/usr/X11R6/lib/fugu-1.1.6/ui.py", line 2228, in main m = GaleWin(root) File "/usr/X11R6/lib/fugu-1.1.6/ui.py", line 257, in __init__ self._create_widgets() File "/usr/X11R6/lib/fugu-1.1.6/ui.py", line 477, in _create_widgets self.regen_screenmenu() File "/usr/X11R6/lib/fugu-1.1.6/ui.py", line 1161, in regen_screenmenu for conf in Config.SUBLIST: AttributeError: ConfigClass instance has no attribute 'SUBLIST' Create a bunch of new screens, select one of them while the Config window is still up, delete the one currently displayed: Error: 1 IndexError Exception in Tk callback Function: (type: ) Args: () Traceback (innermost last): File "/usr/local/lib/python2.1/site-packages/Pmw/Pmw_0_8_5/lib/PmwBase.py", line 1690, in __call__ return apply(self.func, args) File "ui.py", line 2093, in delete if self._master.is_curscreen(delwindow): File "ui.py", line 834, in is_curscreen return Config.SUBLIST[self._curscreen] == screen IndexError: list index out of range