def msgPipeRead(pssName, fd): # the received message msg = "" # data to display in nick column displayFrom = "" # holds sender key fromKey = "" # on disconnect we have invalid fd(?) if fd < 0: return weechat.WEECHAT_RC_ERROR # get the data # \todo how to handle failure try: msg = os.read(fd, 1024) except OSError as e: return weechat.WEECHAT_RC_OK # parse the stream, and build whole stanzas # will also detect connection events, using the last one found as the current state # \todo remove json handling to websocket comms background process processed = stream.process(msg) # loop through all built stanzas for o in processed['results']: # holds content data structure r = "" # check if data is valid try: r = pss.rpc_parse(o) _ = r['params']['result'] except Exception as e: wOut(PSS_BUFPFX_DEBUG, [bufs[pssName]], "", "skipping invalid receive: " + repr(o)) return weechat.WEECHAT_RC_OK # decode contents and display in buffer msgSrc = r['params']['result']['Msg'][2:].decode("hex") # resolve the nick if it exists fromKey = r['params']['result']['Key'] if fromKey in nicks: displayFrom = nicks[fromKey].nick wOut(PSS_BUFPFX_IN, [buf_get(pssName, "chat", displayFrom, True)], displayFrom, msgSrc) else: displayFrom = pss.label(fromKey, 8) buf = buf_get(pssName, "chat", pss.label(fromKey, 16), False) wOut(PSS_BUFPFX_IN, [buf], displayFrom, msgSrc) return weechat.WEECHAT_RC_OK
def sock_connect(pssName, status, tlsrc, sock, error, ip): if status != weechat.WEECHAT_HOOK_CONNECT_OK: wOut(PSS_BUFPFX_ERROR, [], "???", "swarm gateway connect failed (" + str(status) + "): " + error) return weechat.WEECHAT_RC_ERROR wOut(PSS_BUFPFX_INFO, [], "!!!", "swarm gateway connected on " + pssName + ", sock " + repr(sock)) agent = pss.Agent(psses[pssName].host, 8500, sock) bzzs[pssName] = pss.Bzz(agent) # provided the connection went ok # add all nicks in the plugin's memory nick map # that match the pubkey of the node to the node's recipient address book # \todo use execption instead of if/else/error # \todo adding the nicks from a node should be separate proedure, and maybe even split up for feeds and pss for c in nicks: if nicks[c].src == psses[pssName].key: if psses[pssName].add(nicks[c].nick, nicks[c].key, nicks[c].address): wOut( PSS_BUFPFX_INFO, [bufs[pssName]], "+++", "added '" + nicks[c].nick + "' to node '" + pssName + "' (key: " + pss.label(psses[pssName].key) + ", addr: " + pss.label(psses[pssName].base) + ")") # \ todo make this call more legible (public key to bytes method in pss pkg) try: feeds[buf_generate_name( pssName, "chat", nicks[c].nick)] = pss.Feed( bzzs[pssName].agent, psses[pssName].get_account(), PSS_BUFTYPE_CHAT + pss.publickey_to_account( psses[pssName].key[2:].decode("hex"))) except: wOut(PSS_BUFPFX_DEBUG, [bufs[pssName]], "", "bzz gateway for not active") else: wOut( PSS_BUFPFX_DEBUG, [bufs[pssName]], "", "nick " + c + " not added: " + psses[pssName].error()['description']) return weechat.WEECHAT_RC_OK
def processFeedOutQueue(pssName, _): global _tmp_chat_queue_hash, _tmp_room_dirty # \todo change to lasthsh for publickey in cache.chats.keys(): try: coll = cache.chats[publickey][pssName] if _tmp_chat_queue_hash[pssName] != coll.senderfeed.lasthsh: wOut( PSS_BUFPFX_DEBUG, [], ">>>", "update feed " + pssName + ":" + pss.label(publickey.encode("hex")) + " addr: " + pss.label(cache.chats[publickey][pssName].senderfeed.obj.account.get_address().encode("hex")) ) coll.senderfeed.obj.update(coll.senderfeed.lasthsh) _tmp_chat_queue_hash[pssName] = coll.senderfeed.lasthsh except: pass for roomname in cache.rooms.keys(): try: room = cache.get_room(roomname) if _tmp_room_queue_hash[roomname] != room.feedcollection.senderfeed.lasthsh: wOut( PSS_BUFPFX_DEBUG, [], ">>>", "update room " + roomname + ": " + pss.label(room.feedcollection.senderfeed.obj.account.get_public_key().encode("hex")) + " addr: " + pss.label(room.feedcollection.senderfeed.obj.account.get_address().encode("hex")) ) room.feedcollection.senderfeed.obj.update(room.feedcollection.senderfeed.lasthsh) _tmp_room_queue_hash[roomname] = room.feedcollection.senderfeed.lasthsh _tmp_room_initial[roomname] = True except Exception as e: raise(e) return weechat.WEECHAT_RC_OK
def pss_handle(pssName, buf, args): # context is only used for acvie nodes ctx = EventContext() # parse cmd input # \todo remove consecutive whitespace argv = args.split(" ") argc = len(argv) # first we handle commands that are node independent # the connect command is the same for any context # \todo rollback on connect fail if argv[0] == "connect": host = "127.0.0.1" port = "8546" if argc < 2: wOut(PSS_BUFPFX_ERROR, [], "!!!", "invalid command <TODO insert help text>") ctx.set_node(argv[1]) if argc > 2: host = argv[2] if argc > 3: port = argv[3] if cache.have_node_name(ctx.get_node()): existingBuf = weechat.buffer_search("python", "pss.node." + ctx.get_node()) if existingBuf != "": wOut(PSS_BUFPFX_DEBUG, [], "", "pss " + ctx.get_node() + " already exists, changing to that buffer") weechat.buffer_set(bufs[ctx.get_node()], "display", "1") return weechat.WEECHAT_RC_OK if host == "": host = weechat.config_get_plugin(ctx.get_node() + "_url", host) if port == "": port = weechat.config_get_plugin(ctx.get_node() + "_port", port) wOut(PSS_BUFPFX_DEBUG, [], "", "pss " + ctx.get_node() + " already exists") # regardless of if we have the node already, store the connection parameters for later for this node name weechat.config_set_plugin(ctx.get_node() + "_url", host) weechat.config_set_plugin(ctx.get_node() + "_port", port) # if we made it here we don't have a buffer for this node already # so create it and merge the node buffer with core so we can do the neat ctrl-x trick bufs[ctx.get_node()] = weechat.buffer_new("pss.node." + ctx.get_node(), "buf_node_in", ctx.get_node(), "buf_close", ctx.get_node()) weechat.buffer_set(bufs[ctx.get_node()], "short_name", "pss."+ ctx.get_node()) weechat.buffer_set(bufs[ctx.get_node()], "title", "PSS '" + ctx.get_node() + "' | not connected") weechat.buffer_merge(bufs[ctx.get_node()], weechat.buffer_search_main()) weechat.buffer_set(bufs[ctx.get_node()], "display", "1") # now that we have the buffer up we have somewhere to write output relevant to this connection # we can proceed with connection in the pss instance wOut( PSS_BUFPFX_WARN, [bufs[ctx.get_node()]], "0-> 0", "connecting to '" + ctx.get_node() + "'" ) pssnode = pss.Pss(ctx.get_node(), host, port) if not pssnode.connect(): wOut(PSS_BUFPFX_ERROR, [bufs[ctx.get_node()]], "-1-x 0", "connect to '" + ctx.get_node() + "' failed: " + cache.get_pss(ctx.get_node()).error()['description']) return weechat.WEECHAT_RC_ERROR wOut(PSS_BUFPFX_OK, [bufs[ctx.get_node()]], "0---0", "connected to '" + ctx.get_node() + "'") _tmp_chat_queue_hash[ctx.get_node()] = "" cache.add_node(pssnode) wOut(PSS_BUFPFX_OK, [], "+++", "added pss " + ctx.get_node()) # save what we've accomplished so far in the context, to be passed to the hook ctx.parse_buffer(bufs[ctx.get_node()]) ctx.set_pss(cache.get_pss(ctx.get_node())) ctx.set_bzz(cache.get_active_bzz()) # \todo temporary solution, swarm gateway should be set explicitly or at least we need to be able to choose port ctxid = ctxstore.put(ctx) hookSocks.append(weechat.hook_connect("", host, 8500, 0, 0, "", "pss_connect", ctxid)) # start processing inputs on the websocket hookFds[ctx.get_node()] = weechat.hook_fd(cache.get_pss(ctx.get_node()).get_fd(), 1, 0, 0, "msgPipeRead", ctx.get_node()) hookTimers.append(weechat.hook_timer(PSS_FEEDBOX_PERIOD, 0, 0, "processFeedOutQueue", ctx.get_node())) # set own nick for this node # \todo use configurable global default nick # \todo clean up messy pubkey slicing (should be binary format in pss obj) cache.set_nodeself(ctx.get_node(), PSS_DEFAULT_NICK) return weechat.WEECHAT_RC_OK # get the context we're getting the command in # if we are not in pss context, # the we assume that the first argument is the name of the node # /pss oc add someone key addr # becomes # /pss add someone key addr # and "oc" is set to pssName # \todo consider exception for connect-command ctx.parse_buffer(buf) if ctx.is_root(): ctx.set_node(argv[0]) argv = argv[1:] argc -= 1 try: ctx.set_pss(cache.get_pss(ctx.get_node())) ctx.set_bzz(cache.get_active_bzz()) except: wOut( PSS_BUFPFX_ERROR, [], "!!!", "unknown pss connection '" + ctx.get_node() + "'" ) return weechat.WEECHAT_RC_ERROR # set configuation values if argv[0] == "set": if argc < 3: wOut(PSS_BUFPFX_ERROR, [], "!!!", "insufficient number of arguments <TODO help output") return weechat.WEECHAT_RC_ERROR k = argv[1] v = argv[2] # for now we handle privkeys directly # we will read keystore jsons in near future instead, though if k == "pk": try: privkey = pss.clean_privkey(v) except: wOut(PSS_BUFPFX_ERROR, [], "!!!", "invalid key format") return weechat.WEECHAT_RC_ERROR try: pssnode = cache.get_pss(ctx.get_node()) pssnode.set_account_write(privkey.decode("hex")) cache.update_node_contact_feed(pssnode) except KeyError as e: pass except ValueError as e: wOut(PSS_BUFPFX_ERROR, [], "!!!", "set account fail: " + str(e)) return weechat.WEECHAT_RC_ERROR else: wOut(PSS_BUFPFX_ERROR, [], "!!!", "unknown config key") return weechat.WEECHAT_RC_ERROR wOut(PSS_BUFPFX_DEBUG, [], "!!!", "set pk to " + v + " for " + ctx.get_node()) weechat.WEECHAT_RC_OK # add a recipient to the address books of plugin and node # \todo currently overwritten if same pubkey and different addr, should be possible to have both, maybe even one-shots special command with dark address where entry is deleted after message sent!!! elif argv[0] == "add": nick = "" pubkeyhx = "" overlayhx = "" # input sanity check if argc < 3: wOut( PSS_BUFPFX_ERROR, [ctx.get_buffer()], "!!!", "not enough arguments for add <TODO: help output>" ) return weechat.WEECHAT_RC_ERROR # puny human varnames nick = argv[1] pubkeyhx = argv[2] if argc == 4: overlayhx = argv[3] else: overlayhx = "0x" # backend add recipient call pubkey = pss.clean_pubkey(pubkeyhx).decode("hex") overlay = pss.clean_overlay(overlayhx).decode("hex") try: newcontact = pss.PssContact(nick, ctx.get_pss().get_public_key()) newcontact.set_public_key(pubkey) newcontact.set_overlay(overlay) ctx.get_pss().add(newcontact) cache.add_contact(newcontact, True) except Exception as e: wOut( PSS_BUFPFX_ERROR, [ctx.get_buffer()], "!!!", "add contact error: " + repr(e) ) return weechat.WEECHAT_RC_ERROR ctx.reset(PSS_BUFTYPE_CHAT, ctx.get_node(), nick) # refresh the plugin memory map version of the recipient wOut( PSS_BUFPFX_DEBUG, [], "!!!", "added key " + pubkeyhx + " to nick " + nick + " node " + ctx.get_node() ) # retrieve the buffer (create if it doesn't exist) buf_get(ctx, True) wOut( PSS_BUFPFX_INFO, [ctx.get_buffer()], "!!!", "added contact '" + ctx.get_name() + "' to '" + ctx.get_node() + "' (key: " + pss.label(pubkeyhx) + ", addr: " + pss.label(overlayhx) + ")" ) # send a message to a recipient elif argv[0] == "send" or argv[0] == "msg": nick = "" msg = "" # verify input if argc < 2: wOut( PSS_BUFPFX_ERROR, [ctx.get_buffer()], "!!!", "not enough arguments for send" ) return weechat.WEECHAT_RC_ERROR nick = pss.clean_nick(argv[1]) if argc > 2: msg = " ".join(argv[2:]) # check that the contact is known in the cache contact = None try: contact = cache.get_contact_by_nick(nick) except: wOut( PSS_BUFPFX_ERROR, [ctx.get_buffer()], "!!!", "invalid nick " + nick ) return weechat.WEECHAT_RC_ERROR ctx.reset(PSS_BUFTYPE_CHAT, ctx.get_node(), nick) buf = buf_get(ctx, True) # if no message body we've just opened the chat window if msg != "": if not pss.is_message(msg): wOut( PSS_BUFPFX_DEBUG, [ctx.get_buffer()], "", "invalid message " + msg ) return weechat.WEECHAT_RC_ERROR return buf_in(ctx.get_node(), buf, msg) # create/join existing chat room # \todo broken elif argv[0] == "join": room = "" if argc < 2: wOut( PSS_BUFPFX_ERROR, [ctx.get_buffer()], "!!!", "not enough arguments for join" ) return weechat.WEECHAT_RC_ERROR room = argv[1] ctx.reset(PSS_BUFTYPE_ROOM, ctx.get_node(), room) if not ctx.get_name() in _tmp_room_queue_hash: _tmp_room_queue_hash[ctx.get_name()] = pss.zerohsh # start buffer for room buf_get(ctx, True) # invite works in context of chat rooms, and translates in swarm terms to # adding one separate feed encoded with the invited peer's key # room argument can be omitted if command is issued om channel to invite to # note feeds are currently unencrypted # \todo broken elif argv[0] == "invite": nick = "" roomname = "" if argc < 2: wOut( PSS_BUFPFX_ERROR, [ctx.get_buffer()], "!!!", "not enough arguments for invite" ) # if missing channel argument get bufname command was issued in # and derive channel name from it if we can (fail if not) elif argc < 3: if not ctx.is_room(): wOut( PSS_BUFPFX_ERROR, [ctx.get_buffer()], "!!!", "unknown channel '" + ctx.get_name() + "'" ) return weechat.WEECHAT_RC_ERROR else: ctx.set_name(argv[2]) nick = pss.clean_nick(argv[1]) # check if room exists # if it does, perform invitation try: #roombufname = buf_generate_name(pssName, "room", roomname) roombufname = ctx.to_buffer_name() room = cache.get_room(ctx.get_name()) #roombufname) pss_invite(pssName, nick, room) wOut( PSS_BUFPFX_DEBUG, [], "!!!", "added " + nick + " to " + ctx.get_name() ) # if neither the previous fail, add the nick to the buffer roombuf = weechat.buffer_search("python", roombufname) buf_room_add(roombuf, nick) except KeyError as e: # keyerror catches both try statements wOut( PSS_BUFPFX_ERROR, [ctx.get_buffer()], "!!!", "Unknown room or nick: " + str(e) ) # output node key elif argv[0] == "key" or argv[0] == "pubkey": wOut( PSS_BUFPFX_INFO, [ctx.get_buffer()], ctx.get_node() + ".key", ctx.get_pss().get_public_key().encode("hex") ) # output node base address elif argv[0] == "addr" or argv[0] == "address": wOut( PSS_BUFPFX_INFO, [ctx.get_buffer()], ctx.get_node() + ".addr", ctx.get_pss().get_overlay().encode("hex") ) # set nick for pss node elif argv[0] == "nick": try: if len(argv) > 1: nick = pss.clean_nick(argv[1]) cache.set_nodeself(ctx.get_node(), nick) wOut( PSS_BUFPFX_INFO, [ctx.get_buffer()], ctx.get_node(), "nick is '" + cache.get_nodeself(ctx.get_node()) + "'" ) except ValueError as e: wOut( PSS_BUFPFX_ERROR, [ctx.get_buffer()], "!!!", "Invalid nick: " + argv[1] ) # stop connection # \todo also kill the subprocess # \todo ensure clean shutdown so conncet can be called over elif argv[0] == "stop": weechat.unhook(hookFds[ctx.get_node()]) wOut( PSS_BUFPFX_INFO, [ctx.get_buffer()], "!!!", "disconnected from " + ctx.get_node() ) cache.close_node(ctx.get_node()) # invalid input else: return weechat.WEECHAT_RC_ERROR # all good return weechat.WEECHAT_RC_OK
def buf_get(ctx, known): global _tmp_room_queue_hash, _tmp_room_initial haveBzz = False # \todo integrity check of input data bufname = ctx.to_buffer_name() wOut( PSS_BUFPFX_DEBUG, [], "!!!", "generated bufname " + bufname ) buf = weechat.buffer_search("python", bufname) if ctx.is_room() and ctx.get_bzz() == None: raise RuntimeError("gateway needed for multiuser chats over swarm") if buf == "": # for debug only pss_publickey_hex = pss.rpchex(ctx.get_pss().get_public_key()) pss_overlay_hex = pss.rpchex(ctx.get_pss().get_overlay()) # chat is DM between two parties if ctx.is_chat(): shortname = "pss:" + ctx.get_name() # set up the buffer ctx.set_buffer(weechat.buffer_new(bufname, "buf_in", ctx.get_node(), "buf_close", ctx.get_node()), bufname) weechat.buffer_set(ctx.get_buffer(), "short_name", shortname) weechat.buffer_set(ctx.get_buffer(), "title", ctx.get_name() + " @ PSS '" + ctx.get_node() + "' | node: " + weechat.config_get_plugin(ctx.get_pss().get_host() + "_url") + ":" + weechat.config_get_plugin(ctx.get_pss().get_port() + "_port") + " | key " + pss.label(pss_publickey_hex) + " | address " + pss.label(pss_overlay_hex)) weechat.buffer_set(ctx.get_buffer(), "hotlist", weechat.WEECHAT_HOTLIST_PRIVATE) weechat.buffer_set(ctx.get_buffer(), "display", "1") plugin = weechat.buffer_get_pointer(ctx.get_buffer(), "plugin") bufs[bufname] = buf # room is multiuser conversation elif ctx.is_room(): shortname = "pss#" + ctx.get_name() buf = weechat.buffer_new(bufname, "buf_in", ctx.get_node(), "buf_close", ctx.get_node()) weechat.buffer_set(buf, "short_name", shortname) weechat.buffer_set(buf, "title", "#" + ctx.get_node() + " @ PSS '" + ctx.get_node() + "' | node: " + weechat.config_get_plugin(ctx.get_pss().get_host() + "_url") + ":" + weechat.config_get_plugin(ctx.get_pss().get_port() + "_port") + " | key " + pss.label(ctx.get_pss().get_public_key().encode("hex")) + " | address " + pss.label(ctx.get_pss().get_overlay().encode("hex"))) weechat.buffer_set(buf, "hotlist", weechat.WEECHAT_HOTLIST_PRIVATE) weechat.buffer_set(buf, "nicklist", "1") weechat.buffer_set(buf, "display", "1") weechat.nicklist_add_group(buf, "", "me", "weechat.color.nicklist_group", 1) weechat.nicklist_add_nick(buf, "me", ctx.get_node(), "bar_fg", "", "bar_fg", 1) plugin = weechat.buffer_get_pointer(buf, "plugin") bufs[bufname] = buf if cache.get_room_count() == 0: hookTimers.append(weechat.hook_timer(PSS_ROOM_PERIOD, 0, 0, "roomRead", ctx.get_node())) # create new room (room, loaded) = cache.add_room(ctx.get_name(), ctx.get_node()) _tmp_room_initial[ctx.get_name()] = False if loaded: _tmp_room_queue_hash[ctx.get_name()] = room.feedcollection.senderfeed.lasthsh try: cache.have_room_initial(ctx.get_name()) _tmp_room_initial[ctx.get_name()] = True except Exception as e: sys.stderr.write("no initial update, not pulling: " + repr(e) + "\n") wOut( PSS_BUFPFX_DEBUG, [], "", "loaded room " + repr(cache.get_room(ctx.get_name())) ) for p in room.get_participants(): pubkey = p.get_public_key() if pubkey == ctx.get_pss().get_public_key(): continue nick = "" try: c = cache.get_contact_by_public_key(pubkey) nick = c.get_nick() buf_room_add(buf, c.get_nick()) except: nick = p.get_nick() buf_room_add(buf, nick) else: raise RuntimeError("invalid buffer type") else: ctx.set_buffer(buf, bufname) return ctx.get_buffer()
def msgPipeRead(pssName, fd): # the received message msg = "" # data to display in nick column displayFrom = "" # holds sender key fromKey = "" fromKeyHex = "" # whether or not this is a nick already registered in cache # \todo possibly redundant, should be able to tell from cache directly known = False # on disconnect we have invalid fd(?) if fd < 0: return weechat.WEECHAT_RC_ERROR # get the data # \todo how to handle failure try: msg = os.read(fd, 1024) except OSError as e: return weechat.WEECHAT_RC_OK # parse the stream, and build whole stanzas # will also detect connection events, using the last one found as the current state # \todo remove json handling to websocket comms background process processed = stream.process(msg) # loop through all built stanzas for o in processed['results']: # holds content data structure r = "" # check if data is valid try: r = pss.rpc_parse(o) _ = r['params']['result'] except Exception as e: wOut( PSS_BUFPFX_DEBUG, [bufs[pssName]], "", "skipping invalid receive: " + repr(o) ) return weechat.WEECHAT_RC_OK # decode contents and display in buffer msgSrc = r['params']['result']['Msg'][2:].decode("hex") # resolve the nick if it exists fromKeyHex = pss.clean_pubkey(r['params']['result']['Key']) fromKey = fromKeyHex.decode("hex") # \todo add pss name and sender nick name to ctx ctx = EventContext() ctx.set_pss(cache.get_pss(pssName)) try: contact = cache.get_contact_by_public_key(fromKey) displayFrom = contact.get_nick() known = True except Exception as e: sys.stderr.write("exception in get contact: " + repr(e) + "\n") displayFrom = pss.label(fromKeyHex, 8) # \todo without metadata we have no way of knowing the overlay, so it has to be empty contact = pss.PssContact(displayFrom, ctx.get_pss().get_public_key()) contact.set_public_key(fromKey) contact.set_overlay("") ctx.get_pss().add(contact) cache.add_contact(contact) ctx.reset(PSS_BUFTYPE_CHAT, pssName, displayFrom) # write the message to the buffer wOut( PSS_BUFPFX_IN, [buf_get(ctx, known)], displayFrom, msgSrc ) return weechat.WEECHAT_RC_OK
def buf_node_in(pssName, buf, args): global psses ctx = {} currentPss = None argv = "" argc = 0 bufname = "" # parse cmd input # \todo remove consecutive whitespace argv = args.split(" ") argc = len(argv) # first we handle commands that are node independent # the connect command is the same for any context if argv[0] == "connect": host = "127.0.0.1" port = "8546" pssName = "" if argc < 2: wOut(PSS_BUFPFX_ERROR, [], "!!!", "invalid command <TODO insert help text>") pssName = argv[1] if argc > 2: host = argv[2] if argc > 3: port = argv[3] if pssName in psses: existingBuf = weechat.buffer_search("python", "pss.node." + pssName) if existingBuf != "": wOut( PSS_BUFPFX_DEBUG, [], "", "pss " + pssName + " already exists, changing to that buffer") weechat.buffer_set(bufs[pssName], "display", "1") return weechat.WEECHAT_RC_OK if host == "": host = weechat.config_get_plugin(pssName + "_url", host) if port == "": port = weechat.config_get_plugin(pssName + "_port", port) wOut(PSS_BUFPFX_DEBUG, [], "", "pss " + pssName + " already exists") else: psses[pssName] = pss.Pss(pssName, host, port) wOut(PSS_BUFPFX_OK, [], "+++", "added pss " + pssName) # regardless of if we have the node already, store the connection parameters for later for this node name weechat.config_set_plugin(pssName + "_url", host) weechat.config_set_plugin(pssName + "_port", port) # if we made it here we don't have a buffer for this node already # so create it and merge the node buffer with core so we can do the neat ctrl-x trick bufs[pssName] = weechat.buffer_new("pss.node." + pssName, "buf_node_in", pssName, "buf_node_close", pssName) weechat.buffer_set(bufs[pssName], "short_name", "pss." + pssName) weechat.buffer_set(bufs[pssName], "title", "PSS '" + pssName + "' | not connected") weechat.buffer_merge(bufs[pssName], weechat.buffer_search_main()) weechat.buffer_set(bufs[pssName], "display", "1") # now that we have the buffer up we have somewhere to write output relevant to this connection # we can proceed with connection in the pss instance wOut(PSS_BUFPFX_WARN, [bufs[pssName]], "0-> 0", "connecting to '" + pssName + "'") if not psses[pssName].connect(): wOut( PSS_BUFPFX_ERROR, [bufs[pssName]], "0-x 0", "connect to '" + pssName + "' failed: " + psses[pssName].error()['description']) return weechat.WEECHAT_RC_ERROR wOut(PSS_BUFPFX_OK, [bufs[pssName]], "0---0", "connected to '" + pssName + "'") # \todo temporary solution, swarm gateway should be set explicitly or at least we need to be able to choose port hookSocks.append( weechat.hook_connect("", host, 8500, 0, 0, "", "sock_connect", pssName)) # start processing inputs on the websocket hookFds[pssName] = weechat.hook_fd(psses[pssName].get_fd(), 1, 0, 0, "msgPipeRead", pssName) hookTimers.append( weechat.hook_timer(feedBoxPeriod, 0, 0, "processFeedBox", pssName)) # set own nick for this node # \todo use configurable global default nick # \todo clean up messy pubkey slicing (should be binary format in pss obj) pubkey = psses[pssName].key[2:] selfs[pssName] = pss.PssContact( PSS_DEFAULT_NICK, psses[pssName].key, pss.publickey_to_account(pubkey.decode("hex")).encode("hex"), psses[pssName].key) wOut(PSS_BUFPFX_OK, [bufs[pssName]], pssName, "nick is " + selfs[pssName].nick) return weechat.WEECHAT_RC_OK # get the context we're getting the command in # if we are not in pss context, # the we assume that the first argument is the name of the node # /pss oc add someone key addr # becomes # /pss add someone key addr # and "oc" is set to pssName # \todo consider exception for connect-command ctx = buf_get_context(buf) wOut(PSS_BUFPFX_DEBUG, [], "", "ctx: " + repr(ctx['t']) + " n " + ctx['n'] + " h " + ctx['h']) shiftArg = False # t 0 means any non-pss buffer if ctx['t'] == 0 and argv[0] != "connect": pssName = argv[0] argv = argv[1:] argc -= 1 else: pssName = ctx['n'] wOut(PSS_BUFPFX_DEBUG, [], "!!!", "after ctx " + pssName) # see if we already have this node registered if not pssName in psses: wOut(PSS_BUFPFX_ERROR, [], "!!!", "unknown pss connection '" + pssName + "'") return weechat.WEECHAT_RC_ERROR currentPss = psses[pssName] # set configuation values if argv[0] == "set": if argc < 3: wOut(PSS_BUFPFX_ERROR, [], "!!!", "insufficient number of arguments <TODO help output") return weechat.WEECHAT_RC_ERROR k = argv[1] v = argv[2] # for now we handle privkeys directly # we will read keystore jsons in near future instead, though if k == "pk": try: privkey = pss.clean_privkey(v) except: wOut(PSS_BUFPFX_ERROR, [], "!!!", "invalid key format") return weechat.WEECHAT_RC_ERROR try: currentPss.set_account_write(privkey.decode("hex")) except ValueError as e: wOut(PSS_BUFPFX_ERROR, [], "!!!", "set account fail: " + str(e)) return weechat.WEECHAT_RC_ERROR else: wOut(PSS_BUFPFX_ERROR, [], "!!!", "unknown config key") return weechat.WEECHAT_RC_ERROR wOut(PSS_BUFPFX_DEBUG, [], "!!!", "set pk to " + v + " for " + pssName) weechat.WEECHAT_RC_OK # add a recipient to the address books of plugin and node # \todo currently overwritten if same pubkey and different addr, should be possible to have both, maybe even one-shots special command with dark address where entry is deleted after message sent!!! elif argv[0] == "add": nick = "" key = "" addr = "" # input sanity check if argc < 3: wOut(PSS_BUFPFX_ERROR, [bufs[currentPssName]], "!!!", "not enough arguments for add <TODO: help output>") return weechat.WEECHAT_RC_ERROR # puny human varnames nick = argv[1] key = argv[2] if argc == 4: addr = argv[3] else: addr = "0x" # backend add recipient call if not currentPss.add(nick, key, addr): wOut(PSS_BUFPFX_ERROR, [bufs[pssName]], "!!!", "add contact error: " + currentPss.error()['description']) return weechat.WEECHAT_RC_ERROR # refresh the plugin memory map version of the recipient wOut(PSS_BUFPFX_DEBUG, [], "!!!", "added key " + key + " to nick " + nick) nicks[key] = currentPss.get_contact(nick) remotekeys[nick] = key # append recipient to file for reinstating across sessions storeFile.write(nick + "\t" + key + "\t" + addr + "\t" + currentPss.key + "\n") # open the buffer if it doesn't exist buf_get(pssName, "chat", nick, True) wOut( PSS_BUFPFX_INFO, [bufs[pssName]], "!!!", "added contact '" + nicks[key].nick + "' to '" + pssName + "' (key: " + pss.label(key) + ", addr: " + pss.label(addr) + ")") # send a message to a recipient elif argv[0] == "send" or argv[0] == "msg": nick = "" msg = "" if argc < 2: wOut(PSS_BUFPFX_ERROR, [bufs[pssName]], "!!!", "not enough arguments for send") return weechat.WEECHAT_RC_ERROR nick = argv[1] if argc > 2: msg = " ".join(argv[2:]) # \todo handle hex address only if not currentPss.have_nick(nick): wOut(PSS_BUFPFX_ERROR, [bufs[pssName]], "!!!", "invalid nick " + nick) return weechat.WEECHAT_RC_ERROR buf = buf_get(pssName, "chat", nick, True) # \todo remove the bufs dict, since we can use weechat method for getting it bufs[weechat.buffer_get_string(buf, "name")] = buf # if no message body we've just opened the chat window if msg != "": if not pss.is_message(msg): wOut(PSS_BUFPFX_DEBUG, [bufs[pssName]], "", "invalid message " + msg) return weechat.WEECHAT_RC_ERROR return buf_in(pssName, buf, msg) # create/join existing chat room elif argv[0] == "join": room = "" if argc < 2: wOut(PSS_BUFPFX_ERROR, [bufs[pssName]], "!!!", "not enough arguments for join") return weechat.WEECHAT_RC_ERROR room = argv[1] wOut(PSS_BUFPFX_DEBUG, [], "!!!", "in join " + pssName) buf = buf_get(pssName, "room", room, True) # invite works in context of chat rooms, and translates in swarm terms to # adding one separate feed encoded with the invited peer's key # room argument can be omitted if command is issued om channel to invite to # note feeds are currently unencrypted elif argv[0] == "invite": nick = "" roomname = "" if argc < 2: wOut(PSS_BUFPFX_ERROR, [bufs[pssName]], "!!!", "not enough arguments for invite") # if missing channel argument get bufname command was issued in # and derive channel name from it if we can (fail if not) elif argc < 3: if ctx['t'] != PSS_BUFTYPE_ROOM: wOut(PSS_BUFPFX_ERROR, [bufs[pssName]], "!!!", "unknown channel '" + ctx['t'] + "'") return weechat.WEECHAT_RC_ERROR roomname = ctx['h'] else: roomname = argv[2] nick = argv[1] # check if room exists # if it does, perform invitation try: roombufname = buf_generate_name(pssName, "room", roomname) room = rooms[roombufname] pss_invite(pssName, nick, room) wOut(PSS_BUFPFX_DEBUG, [], "!!!", "added " + nick + " to " + roomname) # if neither the previous fail, add the nick to the buffer roombuf = weechat.buffer_search("python", roombufname) buf_room_add(roombuf, nick) except KeyError as e: # keyerror catches both try statements wOut(PSS_BUFPFX_ERROR, [buf], "!!!", "Unknown room or nick: " + str(e)) # output node key elif argv[0] == "key" or argv[0] == "pubkey": wOut(PSS_BUFPFX_INFO, [bufs[pssName]], pssName + ".key", currentPss.key) # output node base address elif argv[0] == "addr" or argv[0] == "address": wOut(PSS_BUFPFX_INFO, [bufs[pssName]], pssName + ".addr", currentPss.base) # set nick for pss node elif argv[0] == "nick": try: if len(argv) > 1: nick = pss.clean_nick(argv[1]) selfs[pssName].nick = nick wOut(PSS_BUFPFX_INFO, [bufs[pssName]], pssName, "nick is '" + selfs[pssName].nick + "'") except ValueError as e: wOut(PSS_BUFPFX_ERROR, [bufs[pssName]], "!!!", "Invalid nick: " + argv[1]) # stop connection # \todo also kill the subprocess # \todo ensure clean shutdown so conncet can be called over elif argv[0] == "stop": weechat.unhook(hookFds[pssName]) wOut(PSS_BUFPFX_INFO, [bufs[pssName]], "!!!", "disconnected from " + pssName) currentPss.close() #del psses[currentPssName] # invalid input else: return weechat.WEECHAT_RC_ERROR # all good return weechat.WEECHAT_RC_OK
def buf_get(pssName, typ, name, known): haveBzz = False # \todo integrity check of input data bufname = buf_generate_name(pssName, typ, name) wOut( PSS_BUFPFX_DEBUG, [], "!!!", "generated bufname " + bufname + " for " + pssName + " / " + typ + " / " + name) try: buf = weechat.buffer_search("python", bufname) # \todo re-evaluate why exception can occur here, and which one specifically except Exception as e: return "" if pssName in bzzs: haveBzz = True elif typ == "room": raise RuntimeException("gateway needed for multiuser chats over swarm") if buf == "": shortname = "" # chat is DM between two parties if typ == "chat": ispubkey = False if known: shortname = "pss:" + name else: shortname = "pss:" + name[:8] ispubkey = True buf = weechat.buffer_new(bufname, "buf_in", pssName, "buf_close", pssName) weechat.buffer_set(buf, "short_name", shortname) weechat.buffer_set( buf, "title", name + " @ PSS '" + pssName + "' | node: " + weechat.config_get_plugin(psses[pssName].host + "_url") + ":" + weechat.config_get_plugin(psses[pssName].port + "_port") + " | key " + pss.label(psses[pssName].key) + " | address " + pss.label(psses[pssName].base)) weechat.buffer_set(buf, "hotlist", weechat.WEECHAT_HOTLIST_PRIVATE) weechat.buffer_set(buf, "display", "1") plugin = weechat.buffer_get_pointer(buf, "plugin") bufs[bufname] = buf debugstr = "have " + repr( psses[pssName].have_account()) + " + " + repr(haveBzz) wOut(PSS_BUFPFX_DEBUG, [], "have", debugstr) if psses[pssName].have_account() and haveBzz: pubkey = "" if ispubkey: pubkey = name.decode("hex") else: pubkey = remotekeys[name].decode("hex") pubkey = "\x04" + pubkey try: feeds[bufname] = pss.Feed( bzzs[pssName].agent, psses[pssName].get_account(), PSS_BUFTYPE_CHAT + pss.publickey_to_account(pubkey)) wOut( PSS_BUFPFX_DEBUG, [], "", "added feed with topic " + feeds[bufname].topic.encode("hex")) except ValueError as e: wOut(PSS_BUFPFX_ERROR, [], "???", "could not set up feed: " + str(e)) # room is multiuser conversation elif typ == "room": shortname = "pss#" + name buf = weechat.buffer_new(bufname, "buf_in", pssName, "buf_close", pssName) weechat.buffer_set(buf, "short_name", shortname) weechat.buffer_set( buf, "title", "#" + name + " @ PSS '" + pssName + "' | node: " + weechat.config_get_plugin(psses[pssName].host + "_url") + ":" + weechat.config_get_plugin(psses[pssName].port + "_port") + " | key " + pss.label(psses[pssName].key) + " | address " + pss.label(psses[pssName].base)) weechat.buffer_set(buf, "hotlist", weechat.WEECHAT_HOTLIST_PRIVATE) weechat.buffer_set(buf, "nicklist", "1") weechat.buffer_set(buf, "display", "1") #weechat.nicklist_add_group(buf, "", "me", "weechat.color.nicklist_group", 1) #weechat.nicklist_add_nick(buf, "me", psses[pssName].name, "bar_fg", "", "bar_fg", 1) plugin = weechat.buffer_get_pointer(buf, "plugin") bufs[bufname] = buf if len(rooms) == 0: hookTimers.append( weechat.hook_timer(roomPeriod, 0, 0, "roomRead", psses[pssName].name)) rooms[bufname] = pss.Room(bzzs[pssName], name, psses[pssName].get_account()) # \todo test load first, only init if not found try: roomhsh = rooms[bufname].get_state_hash() rooms[bufname].load(roomhsh, psses[pssName].get_account()) wOut( PSS_BUFPFX_DEBUG, [], "", "loaded room with topic " + rooms[bufname].feed_out.topic.encode("hex") + " account " + rooms[bufname].feed_out.account.address.encode("hex") + " roomfeed " + rooms[bufname].feed_room.topic.encode("hex")) for k, p in rooms[bufname].participants.iteritems(): nick = "" if p.key == selfs[pssName].key: nick = selfs[pssName].nick else: if not p.key in nicks: nicks[p.nick] = pss.PssContact( p.nick, p.key, p.addr, p.src) remotekeys[p.key] = p.nick nick = nicks[p.key].nick buf_room_add(buf, nick) # \todo correct expect to disambiguate unexpected fails except Exception as e: wOut(PSS_BUFPFX_DEBUG, [], "", "fail room load: " + str(e)) rooms[bufname].start(PSS_DEFAULT_NICK) wOut( PSS_BUFPFX_DEBUG, [], "", "added room with topic " + rooms[bufname].feed_out.topic.encode("hex") + " account " + rooms[bufname].feed_out.account.address.encode("hex") + " roomfeed " + rooms[bufname].feed_room.topic.encode("hex")) else: raise RuntimeError("invalid buffer type") return buf