def process_content(self, content: Content, r_msg: ReliableMessage) -> Optional[Content]: res = super().process_content(content=content, r_msg=r_msg) if res is None: # respond nothing return None if isinstance(res, HandshakeCommand): # urgent command return res # if isinstance(i_msg.content, ReceiptCommand): # receiver = msg.receiver # if receiver.type == NetworkID.Station: # # no need to respond receipt to station # return None # check receiver receiver = r_msg.receiver user = self.facebook.select_user(receiver=receiver) assert user is not None, 'receiver error: %s' % receiver # pack message env = Envelope.create(sender=user.identifier, receiver=r_msg.sender) i_msg = InstantMessage.create(head=env, body=res) # normal response self.messenger.send_message(msg=i_msg) # DON'T respond to station directly return None
def send_content(self, sender: ID, receiver: ID, content: Content, callback: Optional[Callback] = None, priority: int = 0) -> bool: """ Send message content to receiver :param sender: sender ID :param receiver: receiver ID :param content: message content :param callback: if needs callback, set it here :param priority: task priority (smaller is faster) :return: True on success """ if sender is None: # Application Layer should make sure user is already login before it send message to server. # Application layer should put message into queue so that it will send automatically after user login user = self.facebook.current_user assert user is not None, 'failed to get current user' sender = user.identifier # pack and send env = Envelope.create(sender=sender, receiver=receiver) msg = InstantMessage.create(head=env, body=content) return self.messenger.send_message(msg=msg, callback=callback, priority=priority)
def __pack_message(self, content: Content, receiver: ID) -> Optional[ReliableMessage]: sender = g_station.identifier env = Envelope.create(sender=sender, receiver=receiver) i_msg = InstantMessage.create(head=env, body=content) s_msg = g_messenger.encrypt_message(msg=i_msg) if s_msg is None: self.error('failed to encrypt msg: %s' % i_msg) return None return g_messenger.sign_message(msg=s_msg)
def send_content(self, sender: ID, receiver: ID, content: Content, callback: Optional[Callback] = None, priority: int = 0) -> bool: """ Send message content to receiver :param sender: sender ID :param receiver: receiver ID :param content: message content :param callback: if needs callback, set it here :param priority: task priority (smaller is faster) :return: True on success """ env = Envelope.create(sender=sender, receiver=receiver) msg = InstantMessage.create(head=env, body=content) return self.send_message(msg=msg, callback=callback, priority=priority)
def __send_content(self, content: Content, receiver: ID) -> bool: station = self.get_context(key='station') if station is None: user = self.facebook.current_user if user is None: return False sender = user.identifier else: sender = station.identifier env = Envelope.create(sender=sender, receiver=receiver) i_msg = InstantMessage.create(head=env, body=content) s_msg = self.encrypt_message(msg=i_msg) if s_msg is None: # failed to encrypt message return False r_msg = self.sign_message(msg=s_msg) assert r_msg is not None, 'failed to sign message: %s' % s_msg r_msg.delegate = self self.dispatcher.deliver(msg=r_msg) return True
def send_report(self, text: str, receiver: ID) -> bool: if self.sender is None: self.error('sender not set yet') return False timestamp = int(time.time()) content = TextContent(text=text) env = Envelope.create(sender=self.sender, receiver=receiver, time=timestamp) i_msg = InstantMessage.create(head=env, body=content) s_msg = self.messenger.encrypt_message(msg=i_msg) assert s_msg is not None, 'failed to report: %s, %s' % (receiver, text) r_msg = self.messenger.sign_message(msg=s_msg) if r_msg.delegate is None: r_msg.delegate = self.messenger # try for online user sessions = self.session_server.active_sessions(identifier=receiver) if len(sessions) > 0: self.debug('%s is online(%d), try to push report: %s' % (receiver, len(sessions), text)) success = 0 for sess in sessions: if sess.push_message(r_msg): success = success + 1 else: self.error( 'failed to push report via connection (%s, %s)' % sess.client_address) if success > 0: self.debug( 'report pushed to activated session(%d) of user: %s' % (success, receiver)) return True # store in local cache file self.debug('%s is offline, store report: %s' % (receiver, text)) self.database.store_message(r_msg) # push notification dispatcher = self.messenger.dispatcher return dispatcher.push(sender=self.sender, receiver=receiver, message=text)
def deliver_message(self, msg: ReliableMessage) -> Optional[ReliableMessage]: """ Deliver message to the receiver, or broadcast to neighbours """ s_msg = self.messenger.verify_message(msg=msg) if s_msg is None: # signature error? return None # FIXME: check deliver permission res = None # self.filter.check_deliver(msg=msg) if res is None: # delivering is allowed, call dispatcher to deliver this message res = self.dispatcher.deliver(msg=msg) # pack response if res is not None: user = self.facebook.current_user env = Envelope.create(sender=user.identifier, receiver=msg.sender) i_msg = InstantMessage.create(head=env, body=res) s_msg = self.messenger.encrypt_message(msg=i_msg) if s_msg is None: raise AssertionError('failed to respond to: %s' % msg.sender) return self.messenger.sign_message(msg=s_msg)
def download(self, content: FileContent, password: SymmetricKey, msg: SecureMessage): url = content.url if url is None or url.find('://') < 0: # download URL not found return False i_msg = InstantMessage.create(head=msg.envelope, body=content) # download from CDN encrypted = self.messenger.download_data(url=url, msg=i_msg) if encrypted is None or len(encrypted) == 0: # save symmetric key for decrypting file data after download from CDN content.password = password return False else: # decrypt file data data = password.decrypt(data=encrypted) if data is None or len(data) == 0: raise ValueError('failed to decrypt file data with key: %s' % password) content.data = data content.url = None return True
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)