def broadcast(self, message): """ Sends a broadcast message to all online followers. It will resolve each guid before sending the broadcast. Messages must be less than 140 characters. Returns the number of followers the broadcast reached. """ if len(message) > 140: return defer.succeed(0) def send(nodes): def how_many_reached(responses): count = 0 for resp in responses: if resp[1][0] and resp[1][1][0] == "True": count += 1 return count ds = [] signature = self.signing_key.sign(str(message))[:64] for n in nodes: if n[1] is not None: ds.append( self.protocol.callBroadcast(n[1], message, signature)) return defer.DeferredList(ds).addCallback(how_many_reached) dl = [] f = objects.Followers() f.ParseFromString(self.db.FollowData().get_followers()) for follower in f.followers: dl.append(self.kserver.resolve(follower.guid)) self.log.info("broadcasting %s to followers" % message) return defer.DeferredList(dl).addCallback(send)
def get_response(response): # Verify the signature on the response f = objects.Followers() try: pubkey = node_to_ask.signed_pubkey[64:] verify_key = nacl.signing.VerifyKey(pubkey) verify_key.verify(response[1][1] + response[1][0]) f.ParseFromString(response[1][0]) except Exception: return None # Verify the signature and guid of each follower. for follower in f.followers: try: v_key = nacl.signing.VerifyKey(follower.signed_pubkey[64:]) signature = follower.signature follower.ClearField("signature") v_key.verify(follower.SerializeToString(), signature) h = nacl.hash.sha512(follower.signed_pubkey) pow_hash = h[64:128] if int(pow_hash[:6], 16) >= 50 or follower.guid.encode("hex") != h[:40]: raise Exception('Invalid GUID') if follower.following != node_to_ask.id: raise Exception('Invalid follower') except Exception: f.followers.remove(follower) return f
def get_followers(self, request): def parse_followers(followers): if followers is not None: response = {"followers": []} for f in followers.followers: follower_json = { "guid": f.guid.encode("hex"), "handle": f.metadata.handle, "name": f.metadata.name, "avatar_hash": f.metadata.avatar_hash.encode("hex"), "nsfw": f.metadata.nsfw } response["followers"].append(follower_json) request.setHeader('content-type', "application/json") request.write(json.dumps(response, indent=4)) request.finish() else: request.write(json.dumps({})) request.finish() if "guid" in request.args: def get_node(node): if node is not None: self.mserver.get_followers(node).addCallback( parse_followers) else: request.write(json.dumps({})) request.finish() self.kserver.resolve(unhexlify( request.args["guid"][0])).addCallback(get_node) else: ser = self.db.FollowData().get_followers() if ser is not None: f = objects.Followers() f.ParseFromString(ser) parse_followers(f) else: parse_followers(None) return server.NOT_DONE_YET
def migrate(database_path): print "migrating to db version 3" conn = sqlite3.connect(database_path) conn.text_factory = str cursor = conn.cursor() # read followers from db cursor.execute('''SELECT serializedFollowers FROM followers WHERE id=1''') followers = cursor.fetchone() # delete follower table cursor.execute('''DROP TABLE followers''') # create new table cursor.execute( '''CREATE TABLE followers(guid TEXT UNIQUE, serializedFollower TEXT)''' ) cursor.execute( '''CREATE INDEX index_followers ON followers(serializedFollower);''') # write followers back into db if followers is not None: f = objects.Followers() f.ParseFromString(followers[0]) for follower in f.followers: cursor.execute( '''INSERT INTO followers(guid, serializedFollower) VALUES (?,?)''', ( follower.guid.encode("hex"), follower.SerializeToString().encode("hex"), )) # update version cursor.execute('''PRAGMA user_version = 3''') conn.commit() conn.close()