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
Exemple #2
0
 def __load_records(self) -> Dict[str, ID]:
     path = self.__path()
     self.info('Loading ANS records from: %s' % path)
     dictionary = {}
     text = self.read_text(path=path)
     if text is not None:
         lines = text.splitlines()
         for record in lines:
             pair = record.split('\t')
             if len(pair) != 2:
                 self.error('invalid record: %s' % record)
                 continue
             k = pair[0]
             v = pair[1]
             dictionary[k] = ID.parse(identifier=v)
     #
     #  Reserved names
     #
     dictionary['all'] = EVERYONE
     dictionary[EVERYONE.name] = EVERYONE
     dictionary[ANYONE.name] = ANYONE
     dictionary['owner'] = ANYONE
     dictionary['founder'] = ID.parse(
         identifier='moky@4DnqXWdTV8wuZgfqSCX9GjE2kNq7HJrUgQ'
     )  # 'Albert Moky'
     return dictionary
Exemple #3
0
 def __init__(self):
     super().__init__()
     # caches
     self.__ids = {}
     self.__private_keys = {}
     self.__metas = {}
     self.__profiles = {}
     self.__users = {}
     # load built-in users
     self.__load_user(identifier=ID.parse(self.HULK))
     self.__load_user(identifier=ID.parse(self.MOKI))
Exemple #4
0
 def execute(self, cmd: Command, msg: ReliableMessage) -> Optional[Content]:
     assert isinstance(cmd, InviteCommand) or isinstance(cmd, ResetCommand), 'group command error: %s' % cmd
     facebook = self.facebook
     from ..facebook import Facebook
     assert isinstance(facebook, Facebook), 'entity delegate error: %s' % facebook
     # 0. check group
     group = cmd.group
     owner = facebook.owner(identifier=group)
     members = facebook.members(identifier=group)
     if owner is None or members is None or len(members) == 0:
         # FIXME: group profile lost?
         # FIXME: how to avoid strangers impersonating group members?
         return self.__temporary_save(cmd=cmd, sender=msg.sender)
     # 1. check permission
     sender = msg.sender
     if sender != owner:
         # not the owner? check assistants
         assistants = facebook.assistants(identifier=group)
         if assistants is None or sender not in assistants:
             text = '%s is not the owner/assistant of group %s, cannot reset members' % (sender, group)
             raise AssertionError(text)
     # 2. resetting members
     new_members = self.members(cmd=cmd)
     if new_members is None or len(new_members) == 0:
         raise ValueError('group command error: %s' % cmd)
     # 2.1. check owner
     if owner not in new_members:
         raise AssertionError('cannot expel owner (%s) of group: %s' % (owner, group))
     # 2.2. build expelled-list
     remove_list = []
     for item in members:
         if item not in new_members:
             # removing member found
             remove_list.append(item)
     # 2.3. build invited-list
     add_list = []
     for item in new_members:
         if item not in members:
             # adding member found
             add_list.append(item)
     # 2.4. do reset
     if len(add_list) > 0 or len(remove_list) > 0:
         if facebook.save_members(members=new_members, identifier=group):
             if len(add_list) > 0:
                 cmd['added'] = ID.revert(add_list)
             if len(remove_list) > 0:
                 cmd['removed'] = ID.revert(remove_list)
     # 3. response (no need to response this group command)
     return None
Exemple #5
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 __init__(self, messenger: ClientMessenger):
     super().__init__()
     # delegate for send message
     self.messenger = messenger
     # group
     gid = ID.parse(identifier=group_naruto)
     self.__group = messenger.facebook.group(gid)
Exemple #7
0
 def do_profile(self, name: str):
     if self.client is None:
         self.info('login first')
         return
     facebook = g_messenger.facebook
     user = facebook.current_user
     profile = None
     if name is None:
         identifier = user.identifier
     elif name.startswith('{') and name.endswith('}'):
         identifier = user.identifier
         profile = json_decode(data=utf8_encode(string=name))
     else:
         identifier = ID.parse(identifier=name)
         if identifier is None:
             self.info('I don\'t understand.')
             return
     if profile:
         private_key = facebook.private_key_for_signature(identifier=identifier)
         assert private_key is not None, 'failed to get private key for client: %s' % self.client
         # create new profile and set all properties
         tai = Document.create(doc_type=Document.VISA, identifier=identifier)
         for key in profile:
             tai.set_property(key, profile.get(key))
         tai.sign(private_key=private_key)
         cmd = DocumentCommand.response(identifier=identifier, document=tai)
     else:
         cmd = DocumentCommand.query(identifier=identifier)
     self.client.send_command(cmd=cmd)
Exemple #8
0
def create_server(identifier: str, host: str, port: int = 9394) -> Station:
    """ Create Local Server """
    identifier = ID.parse(identifier=identifier)
    server = Station(identifier=identifier, host=host, port=port)
    g_facebook.cache_user(user=server)
    Log.info('local station created: %s' % server)
    return server
Exemple #9
0
def upload_profile() -> Response:
    try:
        form = request.form.to_dict()
        identifier = form['ID']
        meta = form['meta']
        profile = form['profile']
        identifier = ID.parse(identifier=identifier)
        # save meta
        if meta is None:
            meta = g_facebook.meta(identifier=identifier)
            if meta is None:
                raise LookupError('meta not found: %s' % identifier)
        else:
            meta = Meta.parse(meta=json.loads(meta))
            if not g_facebook.save_meta(meta=meta, identifier=identifier):
                raise ValueError('meta not acceptable: %s' % identifier)
        # save profile
        if profile is None:
            raise ValueError('profile empty: %s' % identifier)
        else:
            profile = Document.parse(document=json.loads(profile))
            if not g_facebook.save_document(document=profile):
                raise ValueError('profile not acceptable: %s' % identifier)
        # OK
        return user(identifier)
    except Exception as error:
        res = {
            'code': 500,
            'name': 'Internal Server Error',
            'message': '%s' % error
        }
        xml = render_template('error.xml', result=res)
    return respond_xml(xml)
Exemple #10
0
 def test_identifier(self):
     print('\n---------------- %s' % self)
     str1 = 'gsp-s001@x77uVYBT1G48CLzW9iwe2dr5jhUNEM772G'
     id1 = ID(str1)
     self.assertEqual(id1.address.network, NetworkID.Station)
     arr1 = [str1]
     self.assertTrue(id1 in arr1)
Exemple #11
0
 def scan_ids(self) -> List[ID]:
     ids = []
     directory = os.path.join(self.root, 'public')
     # get all files in messages directory and sort by filename
     files = os.listdir(directory)
     for filename in files:
         path = os.path.join(directory, filename, 'meta.js')
         if not os.path.exists(path):
             self.warning('meta file not exists: %s' % path)
             continue
         address = ID.parse(identifier=filename)
         if address is None:
             self.error('ID/address error: %s' % filename)
             continue
         meta = self.meta(identifier=address)
         if meta is None:
             self.error('meta error: %s' % address)
         else:
             identifier = meta.generate_identifier(network=address.type)
             self.debug('loaded meta for %s from %s: %s' %
                        (identifier, path, meta))
             # the ID contains 'username' now
             if identifier != address:
                 # switch cache key
                 # self.__caches.pop(address)
                 self.__caches[identifier] = meta
             ids.append(identifier)
     self.debug('Scanned %d ID(s) from %s' % (len(ids), directory))
     return ids
Exemple #12
0
 def assistants(self, identifier: ID) -> Optional[List[ID]]:
     array = super().assistants(identifier=identifier)
     if array is not None and len(array) > 0:
         return array
     # get from ANS
     robot = ID.parse(identifier='assistant')
     if robot is not None:
         return [robot]
Exemple #13
0
 def do_login(self, name: str):
     sender = ID.parse(identifier=name)
     if sender is None:
         self.info('unknown user: %s' % name)
     else:
         self.info('login as %s' % sender)
         self.login(identifier=sender)
         facebook = g_messenger.facebook
         self.prompt = Console.prompt + facebook.name(identifier=sender) + '$ '
Exemple #14
0
 def save_contacts(self, contacts: List[ID], user: ID) -> bool:
     assert contacts is not None, 'contacts cannot be empty'
     # store into memory cache
     self.__contacts[user] = contacts
     # store into local storage
     path = self.__contacts_path(identifier=user)
     self.info('Saving contacts into: %s' % path)
     contacts = ID.revert(members=contacts)
     text = '\n'.join(contacts)
     return self.write_text(text=text, path=path)
 def save_members(self, members: List[ID], group: ID) -> bool:
     assert len(
         members) > 0, 'group members should not be empty: %s' % group
     # 1. store into memory cache
     self.__members[group] = members
     # 2. store into local storage
     path = self.__members_path(identifier=group)
     self.info('Saving members into: %s' % path)
     members = ID.revert(members=members)
     text = '\n'.join(members)
     return self.write_text(text=text, path=path)
Exemple #16
0
 def __roaming(self, cmd: LoginCommand, sender: ID) -> Optional[ID]:
     # check time expires
     old = self.database.login_command(identifier=sender)
     if old is not None:
         if cmd.time < old.time:
             return None
     # get station ID
     assert cmd.station is not None, 'login command error: %s' % cmd
     sid = ID.parse(identifier=cmd.station.get('ID'))
     if sid == g_station.identifier:
         return None
     return sid
Exemple #17
0
def load_station(identifier: Union[ID, str], facebook: CommonFacebook) -> Station:
    """ Load station info from 'etc' directory

        :param identifier - station ID
        :param facebook - social network data source
        :return station with info from 'dims/etc/{address}/*'
    """
    identifier = ID.parse(identifier=identifier)
    # check meta
    meta = facebook.meta(identifier=identifier)
    if meta is None:
        # load from 'etc' directory
        meta = Meta.parse(meta=load_station_info(identifier=identifier, filename='meta.js'))
        if meta is None:
            raise LookupError('failed to get meta for station: %s' % identifier)
        elif not facebook.save_meta(meta=meta, identifier=identifier):
            raise ValueError('meta error: %s' % meta)
    # check private key
    private_key = facebook.private_key_for_signature(identifier=identifier)
    if private_key is None:
        # load from 'etc' directory
        private_key = PrivateKey.parse(key=load_station_info(identifier=identifier, filename='secret.js'))
        if private_key is None:
            pass
        elif not facebook.save_private_key(key=private_key, identifier=identifier):
            raise AssertionError('failed to save private key for ID: %s, %s' % (identifier, private_key))
    # check profile
    profile = load_station_info(identifier=identifier, filename='profile.js')
    if profile is None:
        raise LookupError('failed to get profile for station: %s' % identifier)
    Log.info('station profile: %s' % profile)
    name = profile.get('name')
    host = profile.get('host')
    port = profile.get('port')
    # create station
    if private_key is None:
        # remote station
        station = Station(identifier=identifier, host=host, port=port)
    else:
        # create profile
        profile = Document.create(doc_type=Document.PROFILE, identifier=identifier)
        profile.set_property('name', name)
        profile.set_property('host', host)
        profile.set_property('port', port)
        profile.sign(private_key=private_key)
        if not facebook.save_document(document=profile):
            raise AssertionError('failed to save profile: %s' % profile)
        # local station
        station = Station(identifier=identifier, host=host, port=port)
    facebook.cache_user(user=station)
    Log.info('station loaded: %s' % station)
    return station
Exemple #18
0
 def do_call(self, name: str):
     if self.client is None:
         self.info('login first')
         return
     receiver = ID.parse(identifier=name)
     if receiver is None:
         self.info('unknown user: %s' % name)
     else:
         facebook = g_messenger.facebook
         meta = facebook.meta(identifier=receiver)
         self.info('talking with %s now, meta=%s' % (receiver, meta))
         # switch receiver
         self.receiver = receiver
Exemple #19
0
 def send(self, identifier: str) -> int:
     identifier = ID.parse(identifier=identifier)
     device = Device(identifier)
     tokens = device.tokens
     if tokens is None:
         print('Device token not found, failed to push message: %s' % self.__payload.alert)
         return 0
     count = 0
     for token in tokens:
         self.__client.send_notification(token_hex=token, notification=self.__payload)
         count += 1
     print('Message has been sent to %d device(s)' % count)
     return count
Exemple #20
0
    def update_keys(self, key_map: dict) -> bool:
        """
        Update cipher key table into memory cache

        :param key_map: cipher keys(with direction) from local storage
        :return:        False on nothing changed
        """
        changed = False
        for _from in key_map:
            sender = ID.parse(identifier=_from)
            table = key_map.get(_from)
            assert isinstance(
                table, dict), 'sender table error: %s, %s' % (_from, table)
            for _to in table:
                receiver = ID.parse(identifier=_to)
                pw = table.get(_to)
                key = SymmetricKey.parse(key=pw)
                # TODO: check whether exists an old key
                changed = True
                # cache key with direction
                self.__cache_cipher_key(key, sender, receiver)
        return changed
Exemple #21
0
def user(address: str) -> Response:
    try:
        if address.find('@') < 0:
            address = Address.parse(address=address)
        identifier = ID.parse(identifier=address)
        info = g_worker.user_info(identifier=identifier)
        if info is None:
            messages = []
        else:
            identifier = ID.parse(identifier=info.get('ID'))
            messages = g_worker.messages(identifier=identifier,
                                         start=0,
                                         count=20)
        xml = render_template('user.xml', user=info, messages=messages)
    except Exception as error:
        res = {
            'code': 500,
            'name': 'Internal Server Error',
            'message': '%s' % error
        }
        xml = render_template('error.xml', result=res)
    return respond_xml(xml)
Exemple #22
0
 def __roaming(self, cmd: LoginCommand, sender: ID) -> Optional[ID]:
     # check time expires
     old = self.database.login_command(identifier=sender)
     if old is not None:
         if cmd.time < old.time:
             return None
     station = self.messenger.get_context(key='station')
     assert station is not None, 'current station not in the context'
     # get station ID
     assert cmd.station is not None, 'login command error: %s' % cmd
     sid = ID.parse(identifier=cmd.station.get('ID'))
     if sid == station.identifier:
         return None
     return sid
Exemple #23
0
 def contacts(self, user: ID) -> List[ID]:
     # try from memory cache
     array = self.__contacts.get(user)
     if array is None:
         # try from local storage
         path = self.__contacts_path(identifier=user)
         self.info('Loading contacts from: %s' % path)
         text = self.read_text(path=path)
         if text is None:
             array = []
         else:
             array = ID.convert(members=text.splitlines())
         # store into memory cache
         self.__contacts[user] = array
     return array
 def members(self, group: ID) -> List[ID]:
     # 1. try from memory cache
     array = self.__members.get(group)
     if array is None:
         # 2. try from local storage
         path = self.__members_path(identifier=group)
         self.info('Loading members from: %s' % path)
         text = self.read_text(path=path)
         if text is None:
             array = []
         else:
             array = ID.convert(members=text.splitlines())
         # 3. store into memory cache
         self.__members[group] = array
     return array
def load_freshmen() -> List[ID]:
    freshmen = []
    text = Storage.read_text(path=freshmen_file)
    if text is None:
        return freshmen
    array = text.splitlines()
    for item in array:
        identifier = ID.parse(identifier=item)
        if identifier is None:
            Log.error('ID error: %s' % item)
        elif identifier.type == NetworkType.MAIN:
            freshmen.append(identifier)
        else:
            # Log.error('Not a user ID: %s' % identifier)
            pass
    return freshmen
Exemple #26
0
 def users(self) -> list:
     array = []
     anyone = ANYONE
     id_list = self.__recommended_users(address=anyone.address)
     for item in id_list:
         identifier = item.strip()
         if len(identifier) == 0:
             # skip empty lines
             continue
         identifier = ID.parse(identifier=identifier)
         info = self.user_info(identifier=identifier)
         if info is None:
             # user info not found
             continue
         array.append(info)
     return array
Exemple #27
0
 def execute(self, cmd: Command, msg: ReliableMessage) -> Optional[Content]:
     assert isinstance(cmd, InviteCommand), 'group command error: %s' % cmd
     facebook = self.facebook
     from ..facebook import Facebook
     assert isinstance(facebook,
                       Facebook), 'entity delegate error: %s' % facebook
     # 0. check group
     group = cmd.group
     owner = facebook.owner(identifier=group)
     members = facebook.members(identifier=group)
     if owner is None or members is None or len(members) == 0:
         # NOTICE: group membership lost?
         #         reset group members
         return self.__reset(cmd=cmd, msg=msg)
     # 1. check permission
     sender = msg.sender
     if sender not in members:
         # not a member? check assistants
         assistants = facebook.assistants(identifier=group)
         if assistants is None or sender not in assistants:
             raise AssertionError('only member/assistant can invite: %s' %
                                  msg)
     # 2. inviting members
     invite_list = self.members(cmd=cmd)
     if invite_list is None or len(invite_list) == 0:
         raise ValueError('invite command error: %s' % cmd)
     # 2.1. check for reset
     if sender == owner and owner in invite_list:
         # NOTICE: owner invites owner?
         #         it means this should be a 'reset' command
         return self.__reset(cmd=cmd, msg=msg)
     # 2.2. build invited-list
     add_list = []
     for item in invite_list:
         if item in members:
             continue
         # new member found
         add_list.append(item)
         members.append(item)
     # 2.3. do invite
     if len(add_list) > 0:
         if facebook.save_members(members=members, identifier=group):
             cmd['added'] = ID.revert(add_list)
     # 3. response (no need to response this group command)
     return None
Exemple #28
0
 def execute(self, cmd: Command, msg: ReliableMessage) -> Optional[Content]:
     assert isinstance(cmd, ExpelCommand), 'group command error: %s' % cmd
     facebook = self.facebook
     from ..facebook import Facebook
     assert isinstance(facebook,
                       Facebook), 'entity delegate error: %s' % facebook
     # 0. check group
     group = cmd.group
     owner = facebook.owner(identifier=group)
     members = facebook.members(identifier=group)
     if owner is None or members is None or len(members) == 0:
         raise LookupError('Group not ready: %s' % group)
     # 1. check permission
     sender = msg.sender
     if sender != owner:
         # not the owner? check assistants
         assistants = facebook.assistants(identifier=group)
         if assistants is None or sender not in assistants:
             text = '%s is not the owner/assistant of group %s, cannot expel member' % (
                 sender, group)
             raise AssertionError(text)
     # 2. expelling members
     expel_list = self.members(cmd=cmd)
     if expel_list is None or len(expel_list) == 0:
         raise ValueError('expel command error: %s' % cmd)
     # 2.1. check owner
     if owner in expel_list:
         raise AssertionError('cannot expel owner %s of group %s' %
                              (owner, group))
     # 2.2. build removed-list
     remove_list = []
     for item in expel_list:
         if item not in members:
             continue
         # expelled member found
         remove_list.append(item)
         members.remove(item)
     # 2.3. do expel
     if len(remove_list) > 0:
         if facebook.save_members(members=members, identifier=group):
             cmd['removed'] = ID.revert(remove_list)
     # 3. response (no need to response this group command)
     return None
Exemple #29
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
Exemple #30
0
 def __roaming(self, receiver: ID) -> Optional[ID]:
     login = self.database.login_command(identifier=receiver)
     if login is None:
         return None
     station = login.station
     if station is None:
         return None
     # check time expires
     now = time.time()
     login_time = login.time
     if login_time is None:
         self.error('%s login time not set: %s' % (receiver, login))
         return None
     if (now - login_time) > (3600 * 24 * 7):
         t_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(login_time))
         self.debug('%s login expired: [%s] %s' % (receiver, t_str, login))
         return None
     sid = ID.parse(identifier=station.get('ID'))
     if sid == self.station:
         return None
     return sid