def __put(self, cmd: Command, sender: ID) -> Content: # receive encrypted contacts, save it if self.database.save_contacts_command(cmd=cmd, sender=sender): self.info('contacts command saved for %s' % sender) return ReceiptCommand.new(message='Contacts of %s received!' % sender) else: self.error('failed to save contacts command: %s' % cmd) return TextContent.new(text='Contacts not stored %s!' % cmd)
def __put(self, cmd: Command, sender: ID) -> Content: # receive block command, save it if self.database.save_block_command(cmd=cmd, sender=sender): self.info('block command saved for %s' % sender) return ReceiptCommand.new(message='Block command of %s received!' % sender) else: self.error('failed to save block command: %s' % cmd) return TextContent.new(text='Block-list not stored %s!' % cmd)
def __get(self, identifier: ID) -> Content: # query meta for ID meta = self.facebook.meta(identifier=identifier) if meta is None: # meta not found text = 'Sorry, meta for %s not found.' % identifier return TextContent(text=text) # response return MetaCommand.response(identifier=identifier, meta=meta)
def __get_contacts(self, sender: ID) -> Content: # query encrypted contacts, load it stored: Command = self.database.contacts_command(identifier=sender) # response if stored is not None: # response the stored contacts command directly return stored else: return TextContent.new(text='Sorry, contacts of %s not found.' % sender)
def execute(self, cmd: Command, msg: ReliableMessage) -> Optional[Content]: assert isinstance(cmd, HandshakeCommand), 'command error: %s' % cmd message = cmd.message if message in ['DIM?', 'DIM!']: # S -> C return TextContent(text='Handshake command error: %s' % message) else: # C -> S: Hello world! assert 'Hello world!' == message, 'Handshake command error: %s' % cmd return self.__offer(session_key=cmd.session, sender=msg.sender)
def process(self, content: Content, sender: ID, msg: InstantMessage) -> Content: assert isinstance(content, HandshakeCommand), 'command error: %s' % content message = content.message if message in ['DIM?', 'DIM!']: # S -> C return TextContent.new(text='Handshake command error: %s' % message) else: # C -> S: Hello world! assert 'Hello world!' == message, 'Handshake command error: %s' % content return self.__offer(session_key=content.session, sender=sender)
def process(self, content: Content, sender: ID, msg: InstantMessage) -> Content: assert isinstance(content, StorageCommand), 'command error: %s' % content title = content.title if title == StorageCommand.CONTACTS: return self.__process_contacts(cmd=content, sender=sender) # error return TextContent.new( text='Storage command (title: %s) not support yet!' % title)
def broadcast_message(self, msg: ReliableMessage) -> Optional[Content]: """ Deliver message to everyone@everywhere, including all neighbours """ res = self.filter.check_broadcast(msg=msg) if res is not None: # broadcast is not allowed return res # TODO: broadcast this message text = 'Message broadcast to "%s" is not implemented' % msg.envelope.receiver res = TextContent.new(text=text) res.group = msg.envelope.group return res
def execute(self, cmd: Command, msg: ReliableMessage) -> Optional[Content]: assert isinstance(cmd, SearchCommand), 'command error: %s' % cmd # keywords keywords = cmd.get('keywords') if keywords is None: return TextContent(text='Search command error') keywords = keywords.split(' ') # search in database results = self.database.search(keywords=keywords) users = list(results.keys()) return SearchCommand(users=users, results=results)
def process(self, content: Content, sender: ID, msg: InstantMessage) -> Optional[Content]: assert isinstance(content, Command), 'command error: %s' % content # keywords keywords = content.get('keywords') if keywords is None: return TextContent.new(text='Search command error') keywords = keywords.split(' ') # search in database results = self.database.search(keywords=keywords) users = list(results.keys()) return SearchCommand.new(users=users, results=results)
def forward_message(self, msg: ReliableMessage) -> Optional[Content]: """ Re-pack and deliver (Top-Secret) message to the real receiver """ res = self.filter.check_forward(msg=msg) if res is not None: # forward is not allowed return res forward = ForwardContent.new(message=msg) receiver = self.facebook.identifier(string=msg.envelope.receiver) if self.send_content(content=forward, receiver=receiver): return ReceiptCommand.new(message='Message forwarded', envelope=msg.envelope) else: return TextContent.new(text='Failed to forward your message')
def __get(self, identifier: ID, doc_type: str = '*') -> Content: facebook = self.facebook # query entity document for ID doc = facebook.document(identifier=identifier, doc_type=doc_type) if facebook.is_empty_document(document=doc): # document not found text = 'Sorry, document not found for ID: %s' % identifier return TextContent(text=text) # response meta: Meta = facebook.meta(identifier=identifier) return DocumentCommand.response(document=doc, meta=meta, identifier=identifier)
def process(self, content: Content, sender: ID, msg: InstantMessage) -> Optional[Content]: assert isinstance(content, Command), 'command error: %s' % content # report title title = content.get('title') if 'report' == title: return self.__process_old_report(cmd=content, sender=sender) # get CPU by report title cpu = self.cpu(command=title) # check and run if cpu is None: return TextContent.new(text='Report command (title: %s) not support yet!' % title) assert cpu is not self, 'Dead cycle! report cmd: %s' % content return cpu.process(content=content, sender=sender, msg=msg)
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 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 __welcome(self, freshmen: list) -> TextContent: names = [g_facebook.nickname(item) for item in freshmen] count = len(names) if count == 1: string = names[0] msg = 'Welcome new member~ %s.' % string elif count > 1: string = ', '.join(names) msg = 'Welcome new members~ %s.' % string else: # should not be here msg = 'Welcome!' text = TextContent.new(text=msg) text.group = self.__group.identifier return text
def forward_message(self, msg: ReliableMessage) -> Optional[Content]: """ Re-pack and deliver (Top-Secret) message to the real receiver """ if self.filter.allow_forward(msg=msg): # call dispatcher to deliver this message # repack the top-secret message sender = self.messenger.current_user.identifier receiver = g_facebook.identifier(msg.envelope.receiver) content = ForwardContent.new(message=msg) i_msg = InstantMessage.new(content=content, sender=sender, receiver=receiver) # encrypt, sign & deliver it r_msg = self.messenger.encrypt_sign(msg=i_msg) return self.deliver_message(msg=r_msg) else: text = 'Message forward to "%s" is not allowed' % msg.envelope.receiver return TextContent.new(text=text)
def execute(self, cmd: Command, msg: ReliableMessage) -> Optional[Content]: assert isinstance(cmd, ReportCommand), 'report command error: %s' % cmd # report title title = cmd.title if title == ReportCommand.REPORT: return self.__process_old_report(cmd=cmd, sender=msg.sender) # get CPU by report title cpu = self.processor_for_name(command=title) # check and run if cpu is None: return TextContent( text='Report command (title: %s) not support yet!' % title) elif cpu is self: raise AssertionError('Dead cycle! report cmd: %s' % cmd) assert isinstance(cpu, CommandProcessor), 'CPU error: %s' % cpu cpu.messenger = self.messenger return cpu.execute(cmd=cmd, msg=msg)
def send_report(self, text: str, receiver: ID) -> bool: if self.sender is None: self.error('sender not set yet') return False sender = self.facebook.identifier(self.sender) receiver = self.facebook.identifier(receiver) timestamp = int(time.time()) content = TextContent.new(text=text) i_msg = InstantMessage.new(content=content, sender=sender, receiver=receiver, time=timestamp) s_msg = self.messenger.encrypt_message(msg=i_msg) r_msg = self.messenger.sign_message(msg=s_msg) # try for online user sessions = self.session_server.all(identifier=receiver) if sessions and len(sessions) > 0: self.info('%s is online(%d), try to push report: %s' % (receiver, len(sessions), text)) success = 0 for sess in sessions: if sess.valid is False or sess.active is False: # self.info('session invalid %s' % sess) continue request_handler = self.session_server.get_handler( client_address=sess.client_address) if request_handler is None: self.error('handler lost: %s' % sess) continue if request_handler.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.info( 'report pushed to activated session(%d) of user: %s' % (success, receiver)) return True # store in local cache file self.info('%s is offline, store report: %s' % (receiver, text)) self.database.store_message(r_msg) # push notification return self.apns.push(identifier=receiver, message=text)
def __check_blocked(self, envelope: Envelope) -> Optional[Content]: sender = self.facebook.identifier(envelope.sender) receiver = self.facebook.identifier(envelope.receiver) group = self.facebook.identifier(envelope.group) # check block-list if self.database.is_blocked(sender=sender, receiver=receiver, group=group): nickname = self.__name(identifier=receiver) if group is None: text = 'Message is blocked by %s' % nickname else: grp_name = self.__name(identifier=group) text = 'Message is blocked by %s in group %s' % (nickname, grp_name) # response res = TextContent.new(text=text) res.group = group return res
def process_message(self, msg: Message) -> Optional[Content]: if isinstance(msg, ReliableMessage): s_msg = self.verify_message(msg=msg) if s_msg is None: # waiting for sender's meta if not exists return None receiver = self.facebook.identifier(string=msg.envelope.receiver) if receiver.type.is_group() and receiver.is_broadcast: # if it's a grouped broadcast id, then # split and deliver to everyone return self.broadcast_message(msg=msg) try: return self.process_message(msg=s_msg) except LookupError as error: if str(error).startswith('receiver error'): return self.deliver_message(msg=msg) else: return TextContent.new(text='failed to process message: %s' % s_msg) else: return super().process_message(msg=msg)
def process(self, content: Content, sender: ID, msg: InstantMessage) -> Optional[Content]: assert isinstance(content, QueryCommand), 'group command error: %s' % content facebook: Facebook = self.facebook group: ID = facebook.identifier(content.group) # 1. check permission if not facebook.exists_member(member=sender, group=group): if not facebook.exists_assistant(member=sender, group=group): raise AssertionError('only member/assistant can query: %s' % msg) # 2. get group members members = facebook.members(identifier=group) if members is None or len(members) == 0: text = 'Group members not found: %s' % group return TextContent.new(text=text) # 3. response group members for sender user = facebook.current_user assert user is not None, 'current user not set' if facebook.is_owner(member=user.identifier, group=group): return GroupCommand.reset(group=group, members=members) else: return GroupCommand.invite(group=group, members=members)
def execute(self, cmd: Command, msg: ReliableMessage) -> Optional[Content]: assert isinstance(cmd, QueryCommand), 'group command error: %s' % cmd facebook = self.facebook group: ID = cmd.group # 1. check permission if not facebook.exists_member(member=msg.sender, group=group): if not facebook.exists_assistant(member=msg.sender, group=group): raise AssertionError('only member/assistant can query: %s, %s' % (group, msg.sender)) # 2. get group members members = facebook.members(identifier=group) if members is None or len(members) == 0: text = 'Group members not found: %s' % group return TextContent(text=text) # 3. response group members for sender user = facebook.current_user assert user is not None, 'current user not set' if facebook.is_owner(member=user.identifier, group=group): return GroupCommand.reset(group=group, members=members) else: return GroupCommand.invite(group=group, members=members)
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 __put_contacts(self, cmd: StorageCommand, sender: ID) -> Content: # receive encrypted contacts, save it if self.database.save_contacts_command(cmd=cmd, sender=sender): return ReceiptCommand(message='Contacts of %s received!' % sender) else: return TextContent(text='Contacts not stored %s!' % cmd)
def __put(self, cmd: BlockCommand, sender: ID) -> Content: # receive block command, save it if self.database.save_block_command(cmd=cmd, sender=sender): return ReceiptCommand.new(message='Block command of %s received!' % sender) else: return TextContent.new(text='Sorry, block-list not stored: %s!' % cmd)