Ejemplo n.º 1
0
    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 {}
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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)
Ejemplo n.º 5
0
def fmt_bdecode(fmt, data):
    try:
        x = bdecode(data)
    except ValueError:
        return None
    if not validate(fmt, x):
        return None
    return x
Ejemplo n.º 6
0
def fmt_bdecode(fmt, data):
    try:
        x = bdecode(data)
    except ValueError:
        return None
    if not validate(fmt, x):
        return None
    return x
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
 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
Ejemplo n.º 11
0
    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}
Ejemplo n.º 12
0
    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)
Ejemplo n.º 13
0
    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}
Ejemplo n.º 14
0
    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'])
Ejemplo n.º 15
0
# 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
Ejemplo n.º 16
0
 def validate_message(self, sm):
     return validate(self.msgspec, sm.d)
Ejemplo n.º 17
0
# 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
Ejemplo n.º 18
0
    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)
Ejemplo n.º 19
0
    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}
Ejemplo n.º 20
0
    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)