def handle_messaging_command(self, user, request): """ Handles requests from fetcher. Really just a multiplexer function, real handling is done in separate classes. """ t = request.get('t') if t == 'msg': validator = {'msg': [ONE_OR_MORE, str], OPTIONAL_KEY('context'): [ZERO_OR_MORE, {}] } if not validate(validator, request): warning('Invalid messaging request: %s\n' %(str(request))) return None l = request['msg'] if len(l) < 6: warning('Invalid messaging request: %s\n' % str(request)) return None msg = Message() msg.from_list(l) # check internal validity if not msg.validate(): return None if self.chatcontext: context = request.get('context') else: context = None return self.handle_message(user, msg, context) elif t == 'request': if not validate({'msgid': str}, request): warning('Invalid messaging request: %s\n' % str(request)) return None return self.handle_request(user, request['msgid']) elif t == 'request_heads': if not validate({'addrs' : [ZERO_OR_MORE, str]}, request): warning('Invalid heads request: %s\n' % str(request)) return None return self.handle_request_heads(user, request['addrs']) return {}
def got_message_reply(self, user, result, ctx): (c, m, ctxmsgs) = ctx if result == None: return if not validate(self.msgreplyspec, result): return for m in ctxmsgs: if m.error: m.error = False for cb in self.change_message_cb: cb(c, m) parentid = result['parentid'] children = result['children'] # find conversation with parentid c = None for ci in self.conversations.values(): if ci.has_msgid(parentid): c = ci break if c == None: return # request children if we don't have them already for childid in children: if not c.has_msgid(childid): self.request_message(user, childid, max_depth=10)
def got_data(self, user, d): validator = { 'packet': int, 'frag': lambda i: is_unsigned_int('frag', i), 'fragcount': lambda i: is_unsigned_int('fragcount', i), 'payload': str, 'ack': bool, } if not validate(validator, d): warning('fetcher: Invalid data packet %s\n' % str(d)) return if d['fragcount'] > TP_MAX_RECORD_SIZE // MTU + 1: warning('fetcher: Too large packet %d\n' % d['fragcount']) return if len(d['payload']) > MTU * 2: warning('fetcher: Received too large fragment %d\n' % len(d['payload'])) return key = (user, d['packet']) o = pending_receives.get(key) if o == None: # We have not yet received any fragments from this packet o = UDP_Receiver(user, d['packet']) o.handle_data(d)
def got_community_profiles(self, user, reply, ctx): if reply == None: return validator = { 'cname': [ZERO_OR_MORE, str], 'profile': [ZERO_OR_MORE, {}] } if not validate(validator, reply): warning('Invalid community profiles reply\n' % str(reply)) return communities = self.get_user_communities(user) for (cname, profile) in zip(reply['cname'], reply['profile']): if cname == DEFAULT_COMMUNITY_NAME: continue com = self.get_ordinary_community(cname) if com in communities: self.update_community_profile(com, user, profile) communities.remove(com) # Do icon requests for the rest of communities for com in communities: if com.get('name') != DEFAULT_COMMUNITY_NAME: self.request_com_icon(user, com)
def fmt_bdecode(fmt, data): try: x = bdecode(data) except ValueError: return None if not validate(fmt, x): return None return x
def got_ack(self, user, d): validator = { 'packet': int, 'ack': [ONE_OR_MORE, lambda i: is_unsigned_int('ack', i)], } if not validate(validator, d): warning('fetcher: Invalid ack packet %s\n' % str(d)) return key = (user, d['packet']) o = pending_sends.get(key) if o != None: o.handle_ack(d)
def got_bye(self, d, address): """ User quit, denounce """ validator = { 'uid': lambda s: valid_uid(s) and s != self.myuid, } if not validate(validator, d): return user = self.safe_get_user(d.get('uid'), address[0]) if user == None: info('Rejecting quit message from uid %s\n' % d.get('uid')) else: self.denounce_user(user)
def handle_icon_push(self, user, request): """ This is called when is received. Save the icon image. """ validator = {'icon': str, 'iconid': str, 'version': lambda i: is_unsigned_int('version', i) } if not validate(validator, request): return None icon = request['icon'] iconid = request['iconid'] if iconid == 'user': if user.get('faceversion') != request['version']: # This is an outdated version of the icon.. return None if icon == '': # if we got an empty string, user removed the icon # giving None to save_face removes the picture delete_face(user) elif not save_face(user, icon): warning('Could not save face for %s: %d bytes\n' % (user.tag(), len(icon))) return None user.set('myfaceversion', request['version']) self.announce_user_change(user, what=(PROFILE_ICON_CHANGED, None)) elif iconid.startswith('c:'): cname = iconid[2:] if cname == DEFAULT_COMMUNITY_NAME: return None com = self.get_ordinary_community(cname) if com == None: return None if com.get('iconversion') != request['version']: # This is an outdated version of the icon.. return None if com.get('iconlocked'): return None if icon == '': delete_community_icon(com) elif not save_community_icon(com, icon): warning('Failed to update community icon: %s\n' % cname) return None com.set('myiconversion', request['version']) self.announce_community_change(com) return None
def process_results(self, reply): metas = [] if reply == None: return metas if not validate(self.queryresultspec, reply): warning("msgboard: Invalid results: %s\n" % str(reply)) return metas for metadict in reply["msgs"]: meta = Share_Meta() if not meta.unserialize(metadict): warning("msgboard: Can not unserialize: %s\n" % str(metadict)) continue if not self.validate_message(meta): warning("msgboard: Invalid meta: %s\n" % str(meta)) continue metas.append(meta) return metas
def handle_request(self, user, request): """ Handle incoming queries. Search through Share_Metas. """ if request.get("t") == "msgpush": return self.handle_msgpush(user, request) if not validate(self.queryspec, request): warning("Invalid msgboard query: %s\n" % str(request)) return None keywords = request.get("keywords") criteria = request.get("criteria") if criteria == None: criteria = {} criteria.setdefault("src", self.community.myuid) metas = search_metas(self.all_metas(), criteria, keywords) if not normal_traffic_mode(): t = int(time()) metas = filter(lambda meta: self.test_send_time(meta, t), metas) for meta in metas: self.set_send_time(meta, t) serializedmetas = [] for meta in metas: serializedmetas.append(meta.serialize()) if len(serializedmetas) == 0: if normal_traffic_mode(): return {"msgs": []} else: return self.fetcher.POSTPONE_REPLY if self.fetcher.is_fetch_community_efficient(): com = self.community.get_default_community() # Broadcast messages in bundles of three messages push = {"t": "msgpush"} for metabundle in n_lists(serializedmetas, 3): push["msgs"] = metabundle self.fetcher.fetch_community(com, self.name, push, None, ack=False) return self.fetcher.POSTPONE_REPLY return {"msgs": serializedmetas}
def got_request_heads_reply(self, user, result, addrs): if not validate(self.requestheadsreplyspec, result): return heads = result['heads'] for head in heads: addr = head[0] # check that we requested a head for this address, if not # then skip this addr if addr not in addrs: continue # request all heads received for this address for msgid in head[1:]: c = self.get_conversation(addr) if not c.has_msgid(msgid): self.request_message(user, msgid, max_depth=10)
def handle_community_profiles_fetch(self, user, request): validator = { 'cname': [ZERO_OR_MORE, str], 'version': [ZERO_OR_MORE, lambda i: is_unsigned_int('version', i)] } if not validate(validator, request): warning('Invalid community profiles fetch\n' % str(request)) return None cnames = [] profiles = [] for (cname, version) in zip(request['cname'], request['version']): com = self.get_ordinary_community(cname) if com == None: continue if version < com.get('v'): cnames.append(cname) profiles.append(com.serialize()) debug('Sending %s community profile to %s\n' % (com.get('name'), user.get('nick'))) return {'cname': cnames, 'profile': profiles}
def got_hello(self, d, address): """ Check validity of Proximate hello, and register the other party. """ validator = { 'v': valid_protocol_version, 'pv': lambda x: type(x) == int and x >= 0, 'port': valid_port, 'nick': valid_nick, 'uid': lambda s: valid_uid(s) and s != self.myuid, } if not validate(validator, d): if type(d) != dict or d.get('uid') != self.myuid: info('Rejecting signature: %s\n' % str(d)) return updatelist = [('nick', d['nick']), ('protocolversion', d['v'])] if address != None: ip = address[0] else: ip = None self.add_or_update_user(d['uid'], updatelist, d['pv'], ip, d['port'])
# type validator documentation as a commented source code :-) from bencode.py import fmt_bdecode from typevalidator import validate, ANY, ZERO_OR_MORE, ONE_OR_MORE, OPTIONAL_KEY # Syntax of validate: validate(specification, object) # Syntax of fmt_bdecode: fmt_bdecode(specification, blob) # # Blob is first transformed to an object with bdecode, then validated with # type validator. This can be done with one call with fmt_bdecode() # Asserted examples with type validator: # Demand a dictionary whose keys and values are strings: assert validate({str: str}, {'name': 'Cheradenine'}) assert validate({str: str}, {1: 'Cheradenine'}) == False assert validate({str: str}, {'name': 1}) == False # Demand a dictionary whose keys are integers but values may have any # (supported) type. Furthermore, key with value 123 must exist. assert validate({int: ANY, 123: ANY}, {123: 'x'}) assert validate({int: ANY, 123: ANY}, {123: 456}) assert validate({int: ANY, 123: ANY}, {4: 'x'}) == False # 123 does not exist # List may begin with ZERO_OR_MORE or ONE_OR_MORE to specify that minimum # length of the list is either zero or one, respectively. If either is # used, then also a type must be specified after this. assert validate([ZERO_OR_MORE, ANY], []) assert validate([ONE_OR_MORE, ANY], ['x']) assert validate([ONE_OR_MORE, ANY], []) == False assert validate([ONE_OR_MORE, str], ['x', 1]) == False
def validate_message(self, sm): return validate(self.msgspec, sm.d)
def got_request_reply(self, user, result, max_depth): if not validate(self.requestreplyspec, result): return msg_list = result['msg'] children = result['children'] m = Message() m.from_list(msg_list) if not m.validate(): warning('Invalid message received from request\n') return debug('Received requested message %s (%i)\n' % (m.get_msg(), max_depth)) ctime = int(m.get_ctime()[0]) if time.time() - ctime >= MESSAGE_EXPIRATION: debug('Rejecting expired message: %s\n' % m.get_msg()) return # when we receive a message, the corresponding conversation # is identified: # user-to-user : from sender_addr or target_addr (!= my_addr) # community : from target_addr target_addr = m.get_target_addr() sender_addr = m.get_sender_addr() if is_addr_community(target_addr): ckey = target_addr else: if sender_addr == self.my_addr: ckey = target_addr else: ckey = sender_addr c = self.get_conversation(ckey) # discard duplicates msgid = m.get_msgid() if c.has_msgid(msgid): return # add message and set it as new head if it doesn't have children if len(c.get_children(msgid)) > 0: c.add_msg(m) else: c.add_msg(m, set_head=True) self.cleanup_conversation(c) self.announce_new_message(c, m) # if maximum depth is reached, do not continue to request # parent and children if max_depth <= 0: return # request parent if we don't have it already parentid = m.get_parentid() if parentid != '': if not c.has_msgid(parentid): self.request_message(user, parentid, max_depth=max_depth-1) # request children if we don't have them already for childid in children: if not c.has_msgid(childid): self.request_message(user, childid, max_depth=max_depth-1)
def handle_requests(self, from_user, request): """ Handles requests from fetcher """ if request == None: return if not validate({'t': str, 'uid': lambda s: type(s) == str and valid_uid(s) and s != self.my_uid, 'param': str}, request): debug('Key management: Broken request: %s\n' %(request)) return {'r': self.KM_PROTOCOL_VIOLATION, uid: self.my_uid} cmd = request['t'] uid = request['uid'] param = request['param'] debug('Key management: handling request %s from %s\n' %(cmd, uid)) user = self.community.get_user(uid) if user != from_user: warning("keymanagement: Invalid uid from fetcher: %s\n" %(uid)) return {'r': self.KM_PROTOCOL_VIOLATION, 'uid': self.my_uid} nick = user.get('nick') if self.current['user'] and user != self.current['user']: warning('keymanagement: Paraller request from %s: Current uid is %s\n' %(nick, self.current['user'].get('uid'))) return {'r': self.KM_REQUEST_NACK, 'uid': self.my_uid} if not self.check_request(cmd): warning('keymanagement: Protocol violation from %s: Current state is %s but received request %s\n' %(nick, self.current['state'], cmd)) return {'r': self.KM_PROTOCOL_VIOLATION, 'uid': self.my_uid} self.current['state'] = cmd self.key_exchange_gui.plugin_to_gui(user, cmd, True) payload = '' if cmd == self.KM_REQUEST_KEY: self.current['user'] = user result = self.KM_REQUEST_ACK elif cmd == self.KM_REQUEST_DENIED: debug('keymanagement: %s denied request for key exchange\n' %(nick)) self.current = {'user': None, 'state': None} result = self.KM_CANCEL elif cmd == self.KM_REQUEST_OK: debug('keymanagement: started key exchange with %s\n' %(nick)) result = self.KM_REQUEST_ANSWER_ACK elif cmd == self.KM_TEMP_KEY_ACK: # Other user has typed in the passphrase. We can now send the # temporary key encrypted with it. result = self.KM_TEMP_KEY1 payload = self.sym_enc(self.load_pub_key(self.myself, temp=True), self.temp_passphrase) if not payload: result = self.KM_ERROR payload = '' elif cmd == self.KM_TEMP_KEY2: # Received other party's temporary key. Let's send our # permanent key encrypted with this temporary key. temp_key = self.sym_dec(param, self.temp_passphrase) if temp_key and self.save_key(user, pub=temp_key, temp=True): result = self.KM_PERM_KEY1 payload = self.asym_enc(self.load_pub_key(self.myself), self.key_path(user, temp=True)) if not payload: result = self.KM_ERROR payload = '' else: result = self.KM_ERROR payload = '' elif cmd == self.KM_PERM_KEY2: # Received permanent key. Save it and send "finished". perm_key = self.asym_dec(param, self.key_path(self.myself, temp=True)) if perm_key and self.save_key(user, pub=perm_key): result = self.KM_PERM_KEY_ACK else: result = self.KM_ERROR elif cmd == self.KM_CANCEL: self.current = {'user': None, 'state': None} # Key exchange canceled result = self.KM_CANCEL elif cmd == self.KM_FINISHED: self.community.announce_user_change(user) # update user state self.current = {'user': None, 'state': None} # Successful key exchange result = self.KM_FINISHED elif cmd == self.KM_ERROR: self.current = {'user': None, 'state': None} result = self.KM_ERROR elif cmd == self.KM_PROTOCOL_VIOLATION: self.current = {'user': None, 'state': None} result = self.KM_PROTOCOL_VIOLATION debug('Key management: sending answer %s to %s\n' %(result, nick)) return {'r': result, 'uid': self.my_uid, 'param': payload}
def request_cb(self, target_user, request, ctx): """ Handles answers from fetcher """ if request == None: return if not validate({'r': str, 'uid': lambda s: type(s) == str and valid_uid(s) and s != self.my_uid, 'param': str}, request): debug('Key management: Broken payload: %s\n' %(' '.join(payload))) return cmd = request['r'] uid = request['uid'] param = request['param'] if uid == self.my_uid: return debug('Key management: got answer %s from %s\n' %(cmd, uid)) user = self.community.get_user(uid) if self.current['user'] and user != self.current['user']: warning('keymanagement: Protocol violation from %s: Current uid is %s\n' %(nick, self.current['uid'].get('uid'))) return {'r': self.KM_PROTOCOL_VIOLATION, 'uid': self.my_uid} if not self.check_answer(cmd): warning('keymanagement: Protocol violation from %s: request was %s but answer was %s' %(uid, self.current['state'], cmd)) self.send_request(user, self.KM_PROTOCOL_VIOLATION, '') return self.key_exchange_gui.plugin_to_gui(user, cmd, False) payload = '' if cmd == self.KM_REQUEST_ACK: self.temp_key_watcher = self.gen_temp_key() self.temp_passphrase = self.gen_passphrase() debug('Key management: passphrase is %s\n' %(self.temp_passphrase)) return if cmd == self.KM_REQUEST_ANSWER_ACK: self.gen_temp_key() return elif cmd == self.KM_TEMP_KEY1: # Received temporery key: save it and send our temporary key # encrypted with the symmetric cipher temp_key = self.sym_dec(param, self.temp_passphrase) if temp_key and self.save_key(user, pub=temp_key, temp=True): send_cmd = self.KM_TEMP_KEY2 payload = self.sym_enc(self.load_pub_key(self.myself, temp=True), self.temp_passphrase) if not payload: send_cmd = self.KM_ERROR payload = '' else: send_cmd = self.KM_ERROR payload = '' elif cmd == self.KM_PERM_KEY1: # Received counterpartys permanent key, so let's save it and send ours perm_key = self.asym_dec(param, self.key_path(self.myself, temp=True)) if perm_key and self.save_key(user, pub=perm_key): send_cmd = self.KM_PERM_KEY2 payload = self.asym_enc(self.load_pub_key(self.myself), self.key_path(user, temp=True)) if not payload: send_cmd = KM.ERROR payload = '' else: send_cmd = KM_ERROR payload = '' elif cmd == self.KM_PERM_KEY_ACK: send_cmd = self.KM_FINISHED elif cmd == self.KM_FINISHED: # Successful key exchange self.current = {'user': None, 'state': None} self.community.announce_user_change(user) # update user state return elif cmd == self.KM_CANCEL: self.current = {'user': None, 'state': None} return elif cmd == self.KM_ERROR: self.current = {'user': None, 'state': None} return elif cmd == self.KM_PROTOCOL_VIOLATION: self.current = {'user': None, 'state': None} return elif cmd == self.KM_REQUEST_NACK: self.current = {'user': None, 'state': None} return self.current['state'] = send_cmd self.send_request(user, send_cmd, payload)