#!/usr/bin/env python import gtk, GDK import sys sys.path.insert(0, '/home/tlau/src/fugu') from pygale import * import time, re, string 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() # Gale protocol client class class App: def __init__(self): # Use the Gtk event loop pygale.engine.engine = pygale.engine.GtkEngine() # 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', None) pubsubs = pygale.gale_env.get('GALE_GSUB', None) if mysub is not None: sub = mysub else: sub = pygale.id_category(pygale.gale_user(), 'user', '') if pubsubs is not None: sub = sub + ':' + pubsubs 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) # Pop up a new puff class PuffPop: def __init__(self, puff, galeconn): self.puff = puff self.galeconn = galeconn self._create_widgets() def _create_widgets(self): self.window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL) box = gtk.GtkVBox() box.show() self.window.add(box) self.text = gtk.GtkText() self.text.set_editable(gtk.FALSE) self.text.set_word_wrap(gtk.TRUE) self.text.show() box.pack_start(self.text) header = '%s on %s\n\n' %\ (self.puff.get('message/sender', 'Anonymous'), self.puff.get_cat()) self.window.set_title(header) self.text.freeze() self.text.insert_defaults('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_defaults(timestr) bodytext = re.sub('\r\n', '\n', self.puff.get('message/body', '')) self.text.insert_defaults(bodytext) self.text.thaw() hbox = gtk.GtkHBox() hbox.show() box.pack_start(hbox) replybut = gtk.GtkButton('Reply') replybut.connect('clicked', self.reply) replybut.show() hbox.pack_start(replybut) closebut = gtk.GtkButton('Close') closebut.connect('clicked', self.window.destroy) closebut.show() hbox.pack_start(closebut) self.window.show() def reply(self, *args): ComposePop(self.galeconn, recp=self.puff.get_signer()) class ComposePop: def __init__(self, galeconn, recp=None): self.galeconn = galeconn self.puff = pygale.Puff() self.bad_ids = [] self.recp_ids = [] self.cat = [] self.recp_names = [] self._create_widgets(recp) def _create_widgets(self, recp=None): self.window = gtk.GtkWindow(gtk.WINDOW_TOPLEVEL) box1 = gtk.GtkVBox() box1.show() self.window.add(box1) box2 = gtk.GtkHBox() box2.show() box1.pack_start(box2) label = gtk.GtkLabel('To:') label.show() box2.pack_start(label) self.recipient = gtk.GtkEntry() self.recipient.show() box2.pack_start(self.recipient) self.text = gtk.GtkText() self.text.set_editable(gtk.TRUE) self.text.set_word_wrap(gtk.TRUE) self.text.show() box1.pack_start(self.text) box3 = gtk.GtkHBox() box3.show() box1.pack_start(box3) sendbut = gtk.GtkButton('Send') sendbut.connect('clicked', self.send) sendbut.show() box3.pack_start(sendbut) closebut = gtk.GtkButton('Cancel') closebut.connect('clicked', self.window.destroy) closebut.show() box3.pack_start(closebut) if recp: self.recipient.set_text(recp) self.window.show() def send(self, *args): recplist = string.split(self.recipient.get_text()) 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_chars(0, -1) + '\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.window.destroy() class MainWin: def __init__(self, galeconn): self._create_widgets() self.galeconn = galeconn def _create_widgets(self): self.window = gtk.GtkWindow() self.window.set_title('Toxin') self.window.connect('destroy', gtk.mainquit) self.window.connect('delete_event', gtk.mainquit) box = gtk.GtkVBox() self.window.add(box) box.show() new = gtk.GtkButton('Send puff') new.connect('clicked', self.newpuff) new.show() box.pack_start(new) close = gtk.GtkButton('Quit Toxin') close.connect('clicked', self.confirmquit) close.show() box.pack_start(close) self.window.show() def confirmquit(self, *args): gtk.mainquit() # m = tkMessageBox.askyesno(title='Really quit', # message='Really leave Toxin?') # if m: # self.quit() def newpuff(self, *args): ComposePop(self.galeconn) def main(): a = App() win = MainWin(a.galeconn) gtk.mainloop() if __name__ == '__main__': main()