#!/usr/bin/env python from Tkinter import * import sys from pygale import * import time, tkMessageBox, re, string import Pmw TOXIN_VERSION = 'Toxin/1.0' def getinstance(): import socket, os dom = pygale.gale_env.get('GALE_DOMAIN', 'unknown domain') host = socket.gethostname() user = pygale.gale_env.get('GALE_USER', 'unknown user') if sys.platform == 'win32': display = 'Windows' else: display = pygale.gale_env.get('DISPLAY', 'unknown display') pid = str(os.getpid()) return '%(dom)s/%(host)s/%(user)s/%(display)s/%(pid)s' % locals() class App: def __init__(self): # Use the Tk event loop except on Windows if sys.platform != 'win32': pygale.engine.engine = pygale.engine.TkEngine() # Initialize PyGale pygale.init() # Register shutdown handler sys.exitfunc = pygale.shutdown # Create a gale client connection self.galeconn = pygale.GaleClient() self.galeconn.set_onconnect(self.makesub) self.galeconn.connect(self.connected) def makesub(self, host): "subscribe to my personal category" if host is None: print 'Unable to connect to Gale server' sys.exit(0) print 'Connected to', host # Construct my user category mysub = pygale.gale_env.get('GALE_SUBS') if mysub is not None: sub = mysub else: sub = pygale.id_category(pygale.gale_user(), 'user', '') self.galeconn.sub_to(sub) print 'Subscribed to', sub # Set up a callback on new puffs self.galeconn.set_puff_callback(self.recvpuff) def connected(self, host): if host is None: print 'Unable to connect to a gale server' sys.exit() print 'Connected to', host def recvpuff(self, puff): PuffPop(puff, self.galeconn) class PuffPop(Toplevel): def __init__(self, puff, galeconn): Toplevel.__init__(self) self.protocol('WM_DELETE_WINDOW', self.destroy) self.puff = puff self._create_widgets() self.galeconn = galeconn def _create_widgets(self): self.text = Pmw.ScrolledText(self, text_height=8) self.text.pack(expand=1, fill=BOTH) header = '%s on [%s]\n\n' %\ (self.puff.get('message/sender', 'Anonymous'), self.puff.get_cat()) self.title(header) self.text.insert('end', 'From: %s\n' % self.puff.get('message/sender', 'Anonymous')) if self.puff.get('id/time', None) is not None: timestr = 'Time: %s\n' % time.strftime("%l:%M %p %a %m/%d", time.localtime(self.puff.get('id/time'))) self.text.insert('end', timestr) bodytext = re.sub('\r\n', '\n', self.puff.get('message/body', '')) self.text.insert('end', bodytext) replybut = Button(self, command=self.reply, text='Reply') replybut.pack(side=TOP, expand=1, fill=X) closebut = Button(self, command=self.destroy, text='Close') closebut.pack(side=TOP, expand=1, fill=X) def reply(self): ComposePop(self.galeconn, recp=self.puff.get_signer()) class ComposePop(Toplevel): def __init__(self, galeconn, recp=None): Toplevel.__init__(self) self.galeconn = galeconn self.puff = pygale.Puff() self.protocol('WM_DELETE_WINDOW', self.destroy) self.bad_ids = [] self.recp_ids = [] self.cat = [] self.recp_names = [] self._create_widgets(recp) def _create_widgets(self, recp=None): f = Frame(self) f.pack(side=TOP, expand=1, fill=BOTH) label = Label(f, text='To:') label.pack(side=LEFT, expand=0) self.recipient = Entry(f) self.recipient.pack(side=RIGHT, expand=1, fill=X) self.text = Pmw.ScrolledText(self, text_height=8) self.text.pack(expand=1, fill=BOTH) self.text.component('text').bind('', self.send) self.text.component('text').bind('', lambda e, s=self: s.destroy()) self.recipient.bind('', self.send) self.recipient.bind('', lambda e, s=self: s.destroy()) sendbut = Button(self, command=self.send, text='Send (Ctrl-Enter)') sendbut.pack(side=TOP, expand=1, fill=X) closebut = Button(self, command=self.destroy, text='Cancel (Ctrl-c)') closebut.pack(side=TOP, expand=1, fill=X) if recp: self.recipient.insert('end', recp) self.text.component('text').focus_set() else: self.recipient.focus_set() def send(self, *args): recplist = string.split(self.recipient.get()) recplist = map(pygale.expand_id, recplist) if not recplist: m = tkMessageBox.showerror( title='No recipients', parent=self, message='At least one recipient must be specified.') return self.num_priv_recips = len(recplist) for p in recplist: pygale.lookup_id(p, lambda i, s=self, p=p: s.collect_ids(p, i)) def collect_ids(self, req_id, id_info): id, fullname = id_info if id is None or fullname is None: self.bad_ids.append(req_id) else: self.cat.append(pygale.id_category(id, 'user', '')) self.recp_names.append(fullname) self.recp_ids.append(id) if len(self.recp_ids) + len(self.bad_ids) == self.num_priv_recips: # All ids collected self.puff.set_text('message/recipient', string.join(self.recp_names, ',')) self.send_puff() def send_puff(self): if self.bad_ids: if len(self.bad_ids) == 1: msg = 'Cannot find user ' + self.bad_ids[0] else: msg='Cannot find users:\n' + string.join(self.bad_ids, '\n') m = tkMessageBox.showerror( title='Error: recipient list', parent=self.galewin, message=msg) return self.puff.set_time('id/time', int(time.time())) self.puff.set_text('id/instance', getinstance()) self.puff.set_text('message/sender', gale_env.get('GALE_FROM')) self.puff.set_cat(string.join(self.cat, ':')) self.puff.set_text('id/class', TOXIN_VERSION) self.puff.set_text('message/body', re.sub('\n', '\r\n', self.text.get('1.0', 'end-1c') + '\n')) out = self.puff.sign_message(pygale.gale_user()) if out is None: # can't sign print "Can't sign puff" return out.encrypt_message(self.recp_ids, lambda puff, s=self: s.finish_send(puff)) def finish_send(self, puff): # TODO: check for errors self.galeconn.transmit_puff(puff) self.destroy() class MainWin(Frame): def __init__(self, galeconn, master=None): Frame.__init__(self, master) self.master.title('Toxin') self.pack(expand=1, fill=BOTH) self._create_widgets() self.galeconn = galeconn def _create_widgets(self): new = Button(self, command=self.newpuff, text='Send puff') new.pack(side=TOP, expand=1, fill=X) close = Button(self, command=self.confirmquit, text='Quit Toxin') close.pack(side=TOP, expand=1, fill=X) def confirmquit(self): m = tkMessageBox.askyesno(title='Really quit', message='Really leave Toxin?') if m: self.quit() def newpuff(self): ComposePop(self.galeconn) def main(): a = App() win = MainWin(a.galeconn) win.mainloop() if __name__ == '__main__': main()