Exemple #1
0
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
Exemple #2
0
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
Exemple #3
0
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
Exemple #4
0
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	
Exemple #5
0
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()
Exemple #6
0
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
Exemple #7
0
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
Exemple #8
0
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