Example #1
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
Example #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
Example #3
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
Example #4
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)
 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)
Example #6
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
Example #7
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_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)