Beispiel #1
0
 def __attach_key_digest(self, msg: ReliableMessage):
     # check message delegate
     if msg.delegate is None:
         msg.delegate = self.transceiver
     if msg.encrypted_key is not None:
         # 'key' exists
         return
     keys = msg.encrypted_keys
     if keys is None:
         keys = {}
     elif 'digest' in keys:
         # key digest already exists
         return
     # get key with direction
     sender = msg.sender
     group = msg.group
     if group is None:
         receiver = msg.receiver
         key = self.key_cache.cipher_key(sender=sender, receiver=receiver)
     else:
         key = self.key_cache.cipher_key(sender=sender, receiver=group)
     # get key data
     data = key.data
     if data is None or len(data) < 6:
         return
     # get digest
     pos = len(data) - 6
     digest = sha256(data[pos:])
     base64 = base64_encode(digest)
     # set digest
     pos = len(base64) - 8
     keys['digest'] = base64[pos:]
     msg['keys'] = keys
Beispiel #2
0
 def __receipt(message: str, msg: ReliableMessage) -> Content:
     receipt = ReceiptCommand.new(message=message)
     for key in ['sender', 'receiver', 'time', 'group', 'signature']:
         value = msg.get(key)
         if value is not None:
             receipt[key] = value
     return receipt
Beispiel #3
0
 def __broadcast_message(self, msg: ReliableMessage) -> Optional[Content]:
     """ Deliver message to everyone@everywhere, including all neighbours """
     self.debug('broadcasting message from: %s' % msg.sender)
     if self.__traced(msg=msg, station=self.station):
         self.error('ignore traced msg: %s in %s' % (self.station, msg.get('traces')))
         return None
     session_server = self.session_server
     # push to all neighbors connected th current station
     neighbors = self.__neighbors.copy()
     sent_neighbors = []
     success = 0
     for sid in neighbors:
         if sid == self.station:
             continue
         sessions = session_server.active_sessions(identifier=sid)
         if len(sessions) == 0:
             self.warning('remote station (%s) not connected, try later.' % sid)
             continue
         if self.__push_message(msg=msg, receiver=sid, sessions=sessions):
             sent_neighbors.append(sid)
             success += 1
     # push to the bridge (octopus) of current station
     sid = self.station
     sessions = session_server.active_sessions(identifier=sid)
     if len(sessions) > 0:
         # tell the bridge ignore this neighbor stations
         sent_neighbors.append(sid)
         msg['sent_neighbors'] = ID.revert(sent_neighbors)
         self.__push_message(msg=msg, receiver=sid, sessions=sessions)
     # FIXME: what about the failures
     # response
     text = 'Message broadcast to %d/%d stations' % (success, len(neighbors))
     res = TextContent(text=text)
     res.group = msg.group
     return res
 def __split_group_message(self, msg: ReliableMessage,
                           members: List[ID]) -> Optional[Content]:
     """ Split group message for each member """
     messages = msg.split(members=members)
     success_list = []
     failed_list = []
     for item in messages:
         if item.delegate is None:
             item.delegate = msg.delegate
         if self.__forward_group_message(msg=item):
             success_list.append(item.receiver)
         else:
             failed_list.append(item.receiver)
     response = ReceiptCommand(message='Group message delivering')
     if len(success_list) > 0:
         response['success'] = ID.revert(success_list)
     if len(failed_list) > 0:
         response['failed'] = ID.revert(failed_list)
         # failed to get keys for this members,
         # query from sender by invite members
         sender = msg.sender
         group = msg.receiver
         cmd = GroupCommand.invite(group=group, members=failed_list)
         self.send_content(sender=None, receiver=sender, content=cmd)
     return response
Beispiel #5
0
 def process_reliable_message(
         self, msg: ReliableMessage) -> Optional[ReliableMessage]:
     # check message delegate
     if msg.delegate is None:
         msg.delegate = self.transceiver
     receiver = msg.receiver
     if receiver.is_group:
         # deliver group message
         res = self.transmitter.deliver_message(msg=msg)
         if receiver.is_broadcast:
             # if this is a broadcast, deliver it, send back the response
             # and continue to process it with the station.
             # because this station is also a recipient too.
             if res is not None:
                 self.messenger.send_message(msg=res, callback=None)
         else:
             # or, this is is an ordinary group message,
             # just deliver it to the group assistant
             # and return the response to the sender.
             return res
     # try to decrypt and process message
     try:
         return super().process_reliable_message(msg=msg)
     except LookupError as error:
         if str(error).startswith('receiver error'):
             # not mine? deliver it
             return self.transmitter.deliver_message(msg=msg)
         else:
             raise error
Beispiel #6
0
 def __message_exists(self, msg: ReliableMessage, path: str) -> bool:
     if not self.exists(path=path):
         return False
     # check whether message duplicated
     messages = self.__load_messages(path=path)
     for item in messages:
         if item.get('signature') == msg.get('signature'):
             # only same messages will have same signature
             return True
Beispiel #7
0
 def __traced(self, msg: ReliableMessage, station: Station) -> bool:
     sid = station.identifier
     traces = msg.get('traces')
     if traces is not None:
         for node in traces:
             if isinstance(node, str):
                 return sid == node
             elif isinstance(node, dict):
                 return sid == node.get('ID')
             else:
                 self.error('traces node error: %s' % node)
Beispiel #8
0
 def __load_login(
         self, identifier: ID
 ) -> (Optional[LoginCommand], Optional[ReliableMessage]):
     path = self.__path(identifier=identifier)
     self.info('Loading login from: %s' % path)
     dictionary = self.read_json(path=path)
     if dictionary is None:
         return None, None
     cmd = dictionary.get('cmd')
     msg = dictionary.get('msg')
     if cmd is not None:
         cmd = LoginCommand(cmd)
     if msg is not None:
         msg = ReliableMessage.parse(msg=msg)
     return cmd, msg
Beispiel #9
0
 def departure(self, msg: ReliableMessage) -> Optional[ReliableMessage]:
     receiver = msg.receiver
     if receiver == g_station.identifier:
         self.debug('msg for %s will be stopped here' % receiver)
         return None
     sent_neighbors = msg.get('sent_neighbors')
     if sent_neighbors is None:
         sent_neighbors = []
     else:
         msg.pop('sent_neighbors')
     neighbors = self.__neighbors.copy()
     success = 0
     for sid in neighbors:
         if sid in sent_neighbors:
             self.debug('station %s in sent list, ignore this neighbor' %
                        sid)
             continue
         if self.__deliver_message(msg=msg, neighbor=sid):
             success += 1
     # FIXME: what about the failures
     if g_released:
         # FIXME: how to let the client knows where the message reached
         return None
     # response
     sender = msg.sender
     meta = g_messenger.facebook.meta(identifier=sender)
     if meta is None:
         # waiting for meta
         return None
     text = 'Message broadcast to %d/%d stations' % (success,
                                                     len(neighbors))
     self.debug('outgo: %s, %s | %s | %s' %
                (text, msg['signature'][:8], sender.name, msg.receiver))
     res = TextContent(text=text)
     res.group = msg.group
     return self.__pack_message(content=res, receiver=sender)
 def process_reliable_message(
         self, msg: ReliableMessage) -> Optional[ReliableMessage]:
     # check message delegate
     if msg.delegate is None:
         msg.delegate = self
     receiver = msg.receiver
     if receiver.is_group:
         # FIXME: check group meta/profile
         meta = self.facebook.meta(identifier=receiver)
         if meta is None:
             self.suspend_message(msg=msg)
             self.info('waiting for meta of group: %s' % receiver)
             return None
         # process group message
         return self.__process_group_message(msg=msg)
     # try to decrypt and process message
     return super().process_reliable_message(msg=msg)
Beispiel #11
0
 def __load_messages(self, path: str) -> List[ReliableMessage]:
     text = self.read_text(path=path)
     lines = text.splitlines()
     self.debug('read %d line(s) from %s' % (len(lines), path))
     messages = []
     for line in lines:
         msg = line.strip()
         if len(msg) == 0:
             self.warning('skip empty line')
             continue
         try:
             msg = json_decode(data=utf8_encode(string=msg))
             msg = ReliableMessage.parse(msg=msg)
             messages.append(msg)
         except Exception as error:
             self.error('message package error %s, %s' % (error, line))
     return messages
Beispiel #12
0
 def __load_messages(self, path: str) -> list:
     data = self.read_text(path=path)
     lines = data.splitlines()
     self.info('read %d line(s) from %s' % (len(lines), path))
     # messages = [ReliableMessage(json.loads(line)) for line in lines]
     messages = []
     for line in lines:
         msg = line.strip()
         if len(msg) == 0:
             self.info('skip empty line')
             continue
         try:
             msg = json.loads(msg)
             msg = ReliableMessage(msg)
             messages.append(msg)
         except Exception as error:
             self.info('message package error %s, %s' % (error, line))
     return messages
 def __forward_group_message(self, msg: ReliableMessage) -> bool:
     receiver = msg.receiver
     key = msg.get('key')
     if key is None:
         # get key from cache
         sender = msg.sender
         group = msg.group
         key = self.__key_cache.get_key(sender=sender,
                                        member=receiver,
                                        group=group)
         if key is None:
             # cannot forward group message without key
             return False
         msg['key'] = key
     forward = ForwardContent(message=msg)
     return self.send_content(sender=None,
                              receiver=receiver,
                              content=forward)
Beispiel #14
0
 def __traced(self, msg: ReliableMessage, station: ID) -> bool:
     traces = msg.get('traces')
     if traces is None:
         # broadcast message starts from here
         traces = [station]
     else:
         for node in traces:
             if isinstance(node, str):
                 if station == node:
                     return True
             elif isinstance(node, dict):
                 if station == node.get('ID'):
                     return True
             else:
                 self.error('traces node error: %s' % node)
         # broadcast message go through here
         traces.append(station)
     msg['traces'] = ID.revert(members=traces)
     return False
Beispiel #15
0
 def __split_group_message(self, msg: ReliableMessage) -> Optional[Content]:
     receiver = self.facebook.identifier(msg.envelope.receiver)
     assert receiver.type.is_group(), 'receiver not a group: %s' % receiver
     members = self.facebook.members(identifier=receiver)
     if members is not None:
         messages = msg.split(members=members)
         success_list = []
         failed_list = []
         for item in messages:
             if self.deliver(msg=item) is None:
                 failed_list.append(item.envelope.receiver)
             else:
                 success_list.append(item.envelope.receiver)
         response = ReceiptCommand.new(
             message='Message split and delivering')
         if len(success_list) > 0:
             response['success'] = success_list
         if len(failed_list) > 0:
             response['failed'] = failed_list
         return response
Beispiel #16
0
 def arrival(self, msg: ReliableMessage) -> Optional[ReliableMessage]:
     # check message delegate
     if msg.delegate is None:
         msg.delegate = self
     sid = g_station.identifier
     if self.__traced(msg=msg, station=g_station):
         self.debug(
             'current station %s in traces list, ignore this message: %s' %
             (sid, msg))
         return None
     if not self.__deliver_message(msg=msg, neighbor=sid):
         self.error('failed to send income message: %s' % msg)
         return None
     if g_released:
         # FIXME: how to let the client knows where the message reached
         return None
     # response
     sender = msg.sender
     text = 'Message reached station: %s' % g_station
     self.debug('income: %s, %s | %s | %s' %
                (text, msg['signature'][:8], sender.name, msg.receiver))
     res = TextContent(text=text)
     res.group = msg.group
     return self.__pack_message(content=res, receiver=sender)
    def __process_group_message(
            self, msg: ReliableMessage) -> Optional[ReliableMessage]:
        """
        Separate group message and forward them one by one

            if members not found,
                drop this message and query group info from sender;
            if 'keys' found in group message,
                update them to cache;
                remove non-exists member from 'keys
            split group message with members, forward them

        :param msg:
        :return: ReliableMessage as result
        """
        s_msg = self.verify_message(msg=msg)
        if s_msg is None:
            # signature error?
            return None
        sender = msg.sender
        receiver = msg.receiver
        if not self.facebook.exists_member(member=sender, group=receiver):
            if not self.facebook.is_owner(member=sender, group=receiver):
                # not allow, kick it out
                cmd = GroupCommand.expel(group=receiver, member=sender)
                sender = self.facebook.current_user.identifier
                receiver = msg.sender
                env = Envelope.create(sender=sender, receiver=receiver)
                i_msg = InstantMessage.create(head=env, body=cmd)
                s_msg = self.encrypt_message(msg=i_msg)
                if s_msg is None:
                    self.error('failed to encrypt message: %s' % i_msg)
                    self.suspend_message(msg=i_msg)
                    return None
                return self.sign_message(msg=s_msg)
        members = self.facebook.members(receiver)
        if members is None or len(members) == 0:
            # members not found for this group,
            # query it from sender
            res = GroupCommand.query(group=receiver)
        else:
            # check 'keys'
            keys = msg.get('keys')
            if keys is not None:
                # remove non-exists members from 'keys'
                expel_list = []
                for item in keys:
                    if item == 'digest':
                        self.info('key digest: %s' % keys[item])
                        continue
                    m = ID.parse(identifier=item)
                    if m not in members:
                        self.info('remove non-exists member: %s' % item)
                        expel_list.append(m)
                if len(expel_list) > 0:
                    # send 'expel' command to the sender
                    cmd = GroupCommand.expel(group=receiver,
                                             members=expel_list)
                    g_messenger.send_content(sender=None,
                                             receiver=sender,
                                             content=cmd)
                # update key map
                self.__key_cache.update_keys(keys=keys,
                                             sender=sender,
                                             group=receiver)
            # split and forward group message,
            # respond receipt with success or failed list
            res = self.__split_group_message(msg=msg, members=members)
        # pack response
        if res is not None:
            sender = self.facebook.current_user.identifier
            receiver = msg.sender
            env = Envelope.create(sender=sender, receiver=receiver)
            i_msg = InstantMessage.create(head=env, body=res)
            s_msg = self.encrypt_message(msg=i_msg)
            if s_msg is None:
                self.error('failed to encrypt message: %s' % i_msg)
                self.suspend_message(msg=i_msg)
                return None
            return self.sign_message(msg=s_msg)
Beispiel #18
0
 def unpack(self, msg: dimp.ReliableMessage) -> dimp.Content:
     msg = transceiver.verify(msg)
     msg = msg.trim(self.identifier)
     msg = transceiver.decrypt(msg)
     content = msg.content
     return content
Beispiel #19
0
 def __load_message(self, signature: str) -> Optional[ReliableMessage]:
     path = msg_path(signature=signature)
     msg = self.read_json(path=path)
     if msg is not None:
         return ReliableMessage.parse(msg=msg)