Esempio n. 1
0
    def __init__(self, map):
        self.togglees = [ ] # Calls to make to update display if wodge is folded/unfolded
        self.wodge = None
        self.string = None
        self.signature = None
        self.initial_time = None
        self.initial_distance = None
        self.unit_decay_time = 60*60*24*7
        self.opinions = { }    # acq.name -> distance at initial_time
        self.collapsed = 0
        
        for (key, tmpl) in self.member_tmpls.items():
            if map.has_key(key):
                val = map[key]
                if check.matches(val, tmpl):
                    setattr(self, key, val)

        # .initial_time.
        if type(self.initial_time) in (types.FloatType, types.IntType, types.LongType):
            try:
                self.initial_time = float(self.initial_time)
            except:
                self.initial_time = None
        if type(self.initial_time) != types.FloatType:
            self.initial_time = time.time()

        # .opinions.
        for (key, dist) in self.opinions.items():
            if type(dist) not in (types.FloatType, types.IntType, types.LongType):
                del self.opinions[key]
Esempio n. 2
0
def get_checked_config(name, tmpl, default):
    """Ensures @E13: (ret is default) or check.matches(ret, tmpl)."""
    check.check_has_type(name, types.StringType)
    check.check_is_template(tmpl)
    
    try:
        ret = try_get_config(name)
    except:
        return default
    if not check.matches(ret, tmpl):
        print (_("Garbled config file `%s'; expecting something matching template `%s'.")
               % (name, str(tmpl)))
        ret = default
    
    if ret is not default: check.check_matches(ret, tmpl)
    return ret
Esempio n. 3
0
    def __init__(self,app):

        self.lock = threading.RLock()
        self.node = app.node
        self.app  = app 

        # Visible status
        self.quiet       = 0
        self.activity    = ''
        self.n_unseen_messages = 0

        # Results of /find and /search
        self.identity_list = [ ]
        self.file_list=[ ]

        # Python environment
        self.exec_vars = { 'app' : app, 'node' : app.node, 'daemon':app.daemon }

        # Detect repeated offline messages
        self.offline_message_buffer = [ ]

        # Store a copy of recent messages
        self.incoming_message_history = [ ]

        # used for /ls
        self.current_path_nickname = ''
        self.current_path = [ ]

        # While quiet, messages are buffered for later display.
        # Tuples of form (request, opt-address, time.time()).
        # address for offline-sent messages is None.
        # The time item must be wrt this client's epoch.
        self.unread_message_list = [ ]
        
        config = utility.get_checked_config("chat", types.DictionaryType, { })
        for name,tmpl in [('quiet', 'any'),
                          ('activity', 'any'),
                          ('offline_message_buffer', 'any'),
                          ('incoming_message_history', [('any', 'any timestamp')]),
                          ('unread_message_list', [((types.StringType,),
                                                    'opt-address',
                                                    'any timestamp')])]:
            if config.has_key(name):
                config_val = config[name]
                if check.matches(config_val, tmpl):
                    setattr(self, name, config_val)
                else:
                    print (_("Warning: ignoring chat config item %s, which doesn't match %s.")
                           % (name, tmpl))
        # The current version of Circle always writes these timestamps in
        # standardized form, but previous versions wrote a mixture of floats
        # and longs.
        for i in xrange(len(self.incoming_message_history)):
            request,tstamp = self.incoming_message_history[i]
            if type(tstamp) == types.LongType:
                self.incoming_message_history[i] = (request,
                                                    standard2host_timestamp(tstamp))
            check.check_matches(self.incoming_message_history[i],
                                incoming_message_history_item_tmpl)
            # Loose proof: definition of check.is_any_timestamp, @E17.
            # Relevance: @R.I16.
        for i in xrange(len(self.unread_message_list)):
            request,addr,tstamp = self.unread_message_list[i]
            if type(tstamp) == types.LongType:
                self.unread_message_list[i] = (request, addr,
                                               standard2host_timestamp(tstamp))
            check.check_matches(self.unread_message_list[i],
                                unread_message_list_item_tmpl)
            # Loose proof: as above.
            # Relevance: @R.I15.
        
        self.recall_list = [ ]

        # Detect repeated messages
        self.message_buffer = [ ]

        self.channel = [ ]
        self.channels = channels.Channels(self.app)
        self.chat_check_invar()
Esempio n. 4
0
def retrieve_cached_messages_thread(self,on_complete):

    pipe = self.node.retrieve(
        hash.hash_of('offline message '+self.app.name_server.public_key_name),
        settings.cache_redundancy)

    # Loose proof of @R50: @I22, @E22.
    pipe_reads = [ ]
    if not pipe.finished():
        while 1:
            pipe_reads.extend(pipe.read_all())
            if pipe.finished():
                break
            yield 'sleep',1
    pipe.stop()
    # pjm 2002-08-05: I've changed the above to sleep only if
    # one read_all call isn't enough.  However, I don't know why
    # sleep is wanted in the first place, or whether a different
    # duration might be better.  (Python sleep allows its
    # argument to be fractional, implemented in terms of
    # select.)

    unique_messages = [ ]
    for item in pipe_reads:
        # I think we're guaranteed that item matches ('af_inet_address', 'any').
        if not check.matches(item[1], {'type' : types.StringType,
                                       'crypt' : ('any', 'any')}):
            # print bad peer
            pass
        elif item[1] not in unique_messages:
            unique_messages.append(item[1])

    message_list = [ ]
    for raw_msg in unique_messages:
            if type(raw_msg) == type({}) \
               and raw_msg.get('type','') == 'offline message' \
               and raw_msg.has_key('crypt'):
                try:
                    decrypt = self.app.name_server.decrypt(raw_msg['crypt'])
                    if not check.matches(decrypt, ('text',
                                                   (types.StringType,),
                                                   types.LongType)):
                        raise error.Error('bad decrypted reply')

                    # Remove from caches
                    for thing in pipe_reads:
                        if thing[1] == raw_msg:
                            try:
                                ticket, template, wait = self.node.call(\
                                    thing[0],('data cache remove',decrypt[0]))
                                if wait: yield ('call',(self.node,ticket))
                                self.node.get_reply(ticket,template)

                            except error.Error:
                                pass

                    message_list.append((decrypt[2],decrypt[1]))
                except error.Error:
                    pass

    message_list.sort()
    self.lock.acquire()
    try:
        any = 0
        for msg in message_list:
            if msg not in self.offline_message_buffer:
                self.offline_message_buffer = [msg] + self.offline_message_buffer[:50]

                new_item = (msg[1], None,
                            standard2host_timestamp(msg[0]))
                # Proof of @R36: msg is taken from message_list.
                # message_list is local to this method, and is not
                # passed to any other method (so is not shared with any
                # other thread).  message_list starts as empty and is
                # written to solely as (decrypt[2],decrypt[1]) pairs,
                # and only where decrypt has already been found to
                # match ('any', ('string',), 'long').  The relevant
                # types are immutable.
                check.check_matches(new_item,
                                    unread_message_list_item_tmpl)
                # Proof: the @R36 proof just above also shows that
                # msg[1] (i.e. decrypt[1]) matches ('string',) and
                # is immutable.  new_item[1] matches because
                # is_opt_address(None).  new_item[2] matches from @E17.
                # Relevance: @R.I15
                self.unread_message_list.append(new_item)
                any = 1
            else:
                print _("Duplicate offline message.")
    finally:
        self.lock.release()

    on_complete(self,any)
Esempio n. 5
0
    def start(self, status_monitor=None):
        utility.Task_manager.start(self)

        acq_list = utility.get_checked_config('acquaintances', types.ListType, [ ])
        for item in acq_list:
            if not check.matches(item, Acquaintance.map_tmpl):
                print _("Warning: corrupted acquaintances config file; ignoring item: "), item
                continue

            acq = Acquaintance(self, item, 1)
            self.acquaintances[acq.name] = acq
            self.nicknames[acq.nickname] = acq
            acq.start()


        def make_acquaintance_noaddr(self, info):

            name = key_name(info['key'])

            acq = Acquaintance(self, {'info': info,
                                          'name': name,
                                          'nickname': self.choose_nickname(info['name'])},0)
            self.acquaintances[name]     = acq
            self.nicknames[acq.nickname] = acq

            acq.start()
            acq.start_watching(self.node)
            self.acquaintance_status_changed(acq, "create")

            return acq

        self.me = make_acquaintance_noaddr(self,self.info)

        # Other me may want to test identity
        # May start chatting before test complete
        self.node.add_handler('identity test', self, ('name',), crypto.pubkey.signature_tmpl)
        self.node.add_handler('identity query', self, (), Acquaintance.info_template)
        self.node.add_handler('identity watch', self, (), types.DictionaryType)
        self.node.add_handler('identity connecting', self)
        self.node.add_handler('identity status changed', self, ('any', Acquaintance.status_template))
        self.node.add_handler('identity disconnecting', self,('string', 'opt-text'))
        self.node.add_handler('identity abort', self)

        self.node.publish(self.public_key_name,self.get_info_func, settings.identity_redundancy)
        self.node.publish(self.public_key_name_offline,self.get_info_func, settings.identity_redundancy)
        self.node.publish(self.service_name,self.get_info_func, settings.identity_redundancy)
        for item in self.info['keywords']:
            self.node.publish(hash.hash_of('identity-name '+item),
                              self.get_info_func, settings.identity_redundancy)

        def startup_thread(self, status_monitor=status_monitor):
            
            list = self.acquaintances.values()
            list.sort(lambda x,y: cmp(x.sort_value(),y.sort_value()))
            for item in list:
                item.start_watching(self.node)

                #start watching tends to breed, try to make sure we don't get
                #too many threads.
                #yes, this is hacky
                #print item.nickname, threading.activeCount()
                #time.sleep(0.25)

                while 1:
                    yield 'sleep',0.25
                    if threading.activeCount() < 40:
                        break
                 
            self.me.start_watching(self.node)
            while not self.me.watched:
                yield 'sleep',0.1
            
            online = self.me.online
            address = self.me.address
            if online:
                if status_monitor:
                    status_monitor(_('Shutting down your other peer.'))
                while 1:
                    ticket,template,wait = self.node.call(address, ('identity abort',))
                    if wait: yield 'call',(self.node,ticket)
                    try:
                        dummy_result = self.node.get_reply(ticket, template)
                    except error.Error:
                        break
                    yield 'sleep',4
            
            self.me.online  = 1
            self.me.address = self.node.address
            self.me.connect_time = time.time()
            # Task to retrieve existing watchers
            # Task to poll existing watchers
            utility.start_thread(name_server_watch_poller_thread(self))
            # now refresh my own offline presence
            pipe = self.node.retrieve(self.public_key_name_offline, settings.cache_redundancy)
            list = [ ]

            while not pipe.finished():
                for item in pipe.read_all():
                    if type(item[1]) == types.DictType and \
                           item[1].get('type') == 'identity offline' and \
                           item[1].get('salt'):
                        list.append(item)
                yield 'sleep',2
                if not self.running:
                    return
            pipe.stop()

            #if len(list) != 4:
            #    print _("%d peers holding your offline presence.") % len(list)

            for item in list:
                address, value = item
                key = hash.hash_of(safe_pickle.dumps(self.sign(value['salt'])))
                ticket, template, wait = self.node.call(address, ('data cache remove',key))
                if wait: yield 'call',(self.node,ticket)
                try:
                    dummy_result = self.node.get_reply(ticket,template)
                except error.Error:
                    pass

            self.lock.acquire()
            try:
                package = {
                    'name'       : self.info['name'],
                    'human-name' : self.info['human-name'],
                    'description': self.info['description'],
                    'timezone'   : self.info['timezone'],
                    'key'        : self.public_key,
                    'keywords'   : self.info['keywords'],
                }
            finally:
                self.lock.release()

            package_dumped = safe_pickle.dumps(package)
            signature = self.sign(package_dumped)


            # now publish and cache offline identity
            value = {
                'type' : 'identity offline',
                'package' : package_dumped,
                'signature' : signature,
                'salt' : utility.random_bytes(settings.name_bytes)
            }
            lock = hash.hash_of(hash.hash_of(safe_pickle.dumps(self.sign(value['salt']))))
            publications = [ self.public_key_name_offline, self.service_name ]
            for item in package['keywords']:
                publications.append(hash.hash_of('identity-name '+item))
            # thomasV
            # redundancy 4: this is the meta-publish
            result, publish_thread = self.app.cache.publish(publications,value,lock, 4)
            yield 'wait',publish_thread
            
        utility.start_thread(startup_thread(self))
Esempio n. 6
0
    def try_address_thread(self, address, the_node, result):

        if self.online and self.address == address:
            result.append(0)
            return 

        key = self.info['key']
        id_test_result = []
        yield 'wait', identity_test_thread(address, key, the_node, id_test_result)
        if not id_test_result[0]:
            print "identity did not pass test"
            #result.append(0)
            #return

        ticket,template, wait = the_node.call(address,('identity query',))
        if wait: yield 'call',(the_node,ticket)
        try:
            info = the_node.get_reply(ticket,template)
        except:
            result.append(0)
            return

        if not check.matches(info, Acquaintance.info_template):
            node.bad_peer(address,
                     _("bad response to 'identity query': ") + `info`)
            result.append(0)
            return 
        self.info = info

        if info.get('peer-name') == the_node.name:
            result.append(0)
            return 

        ticket,template, wait = the_node.call(address,('identity watch',))
        if wait: yield 'call',(the_node,ticket)
        try:
            status = the_node.get_reply(ticket,template)
        except:
            result.append(0)
            return

        if type(status) != types.DictionaryType:
            status = { }

        was_online = self.online
        self.address  = address
        self.online   = 1
        self.watched  = 1
        self.status = status

        # up_time is an interval. connect_time is time in my zone.
        up_time = self.info.get('up time')
        if type(up_time) == types.IntType:
            self.connect_time = time.time() - up_time
        else:
            self.connect_time = None

        if self.drm:
            the_node.trusted_addresses.append(address)
        else:
            while address in the_node.trusted_addresses:
                the_node.trusted_addresses.remove(address)

        self.start_watching(the_node)

        self.name_server.acquaintance_status_changed(self, 'discover')

        result.append(not was_online)
        return
Esempio n. 7
0
        def gossip_fetch_thread(self,acq):
            self.fetch_threads+=1
            
            if not self.running:
                self.fetch_threads-=1
                return

            if acq.distance != None:
                #acq.start_watching(self.node, 1)
                
                acq.lock.acquire()
                online = acq.online
                address = acq.address
                distance = acq.distance
                acq.lock.release()

                if distance == None or not online:
                    self.fetch_threads-=1
                    return

                pos = 0
                fetch_timeout = node.make_timeout(settings.gossip_fetch_time) 
                while not node.is_timed_out(fetch_timeout):
                    try:
                        ticket, template, wait = self.node.call(address,('gossip list',pos,pos+20))
                        if wait: yield 'call',(self.node,ticket)
                        result = self.node.get_reply(ticket,template)
                        
                        if not check.matches(result,
                                             [(types.IntType, 'any', 'any')]):
                            node.bad_peer(address,
                                     _("Bad reply to 'gossip list': ") + `result`)
                            break
                        if len(result) == 0:
                            break

                        all_ok = 1
                        # effic: sort self.gossip, sort the returned results,
                        # to speed up searches for signatures.  However, I
                        # haven't yet seen gossip_fetch_task come up in
                        # profiles.
                        for item in result:
                            already_there = 0

                            for wodge in self.gossip:
                                if wodge.signature == item[2]:
                                    #if wodge.distance(self.app.name_server) > from_fixed(item[0])+distance:
                                    #  # TODO: What to do with unit_decay_time?
                                    #  wodge.initial_distance = from_fixed(item[0])+distance
                                    #  wodge.initial_time     = time.time()
                                    wodge.opinions[acq.name] = from_fixed(item[0]) - wodge.decay()
                                    already_there = 1
                                    break

                            if already_there:
                                continue

                            try:
                                ticket, template, wait = self.node.call(
                                    address,('gossip get',item[2]))
                                if wait: yield 'call',(self.node,ticket)
                                string = self.node.get_reply(ticket, template)
                                wodgewodge = safe_pickle.loads(string)
                            except error.Error:
                                all_ok = 0
                                break

                            #TODO: Confirm signature of known people

                            wodge = Wodge({
                                'wodge': wodgewodge,
                                'string': string,
                                'signature': item[2],
                                'initial_time': time.time(),
                                'initial_distance': None,
                                #'initial_distance': from_fixed(item[0]) + distance
                                'unit_decay_time': item[1],
                                'opinions': { acq.name : from_fixed(item[0]) },
                                'collapsed': 0})

                            if not self.insert_wodge(wodge):
                                all_ok = 0
                                break

                        if not all_ok:
                            break
                        
                        pos = pos + 18
                    except error.Error:
                        break
            self.fetch_threads-=1