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()
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))