def request_meeting(chat, params): """ Request a meeting with the user "nickname". This function is attached to the chat command "/meet" Send a request to the receiver. If he/she accept, then a local meeting call will be emited. node.call receiver query="start meeting" if return yes: start a call to the receiver else: show the error message (rejected, or technical failure) """ check.check_matches(params, ['text']) check.check_assertion(1 <= len(params) <= 1) nickname = params[0] if not app.name_server.nicknames.has_key(nickname): app.chat.show("Unknown user '%s' at the local names server.\n"% nickname) return server_acq = app.name_server.nicknames[nickname] # we are the "client", the receiver is the "server". if server_acq.address == node.address: app.chat.show("You can not meet yourself, sorry.\n") return if not server_acq.online: app.chat.show("The user '%s' is not online.\n"% nickname) return Task(request_meeting_task, server_acq).start() return
def __init__(self, name_server, map, remember): check.check_isinstance(name_server, Name_server) check.check_matches(map, self.map_tmpl) check.check_has_type(remember, types.IntType) check.check_assertion(remember in (0, 1)) utility.Task_manager.__init__(self) self.editor_open = 0 self.watch = 0 # used to tell if the person logs in or out in chat window # nothing to do with 'watchers' self.distance = None self.drm = 0 self.address = None self.watching = 0 self.watched = 0 self.online = 0 self.status = { } # .name and .nickname will be filled in from map. We initialize # them here just to placate pychecker. # Note: Don't confuse self.name (check.is_name) with self.info['name'] # (person's preferred nickname). self.name = '(error)' self.nickname = '(error)' self.name_server = name_server for key,value in map.items(): setattr(self, key, value) self.remember = remember self.check_invar()
def send_message(self, dests, message, attachment=None, augmentation=None): check.check_matches(dests, ['text']) check.check_is_text(message) if self.quiet: self.show_status() utility.start_thread(chat_tell_thread( self,dests,message,attachment,augmentation))
def remove_channel(self, dest): check.check_matches(dest, 'text') #=@R51 # Relevance: @R.I31. self.lock.acquire() try: if dest not in self.channel: return self.channel.remove(dest) self.app.refresh_userlist() self.set_prompt() finally: self.lock.release()
def set_channel(self, dests): check.check_matches(dests, ['text']) #=@R49 # Relevance: @R.I31. self.lock.acquire() try: self.channel = dests for dest in self.channel: if dest[0]=='#': self.channel_sub(dest) self.channel_mute(dest,0) self.app.refresh_userlist() self.set_prompt() finally: self.lock.release()
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
def handle(self, request, address,call_id): check.check_matches(request, (types.StringType,)) #=@R34 check.check_is_af_inet_address(address) #=@R31 self.lock.acquire() try: if request[0] == 'chat look': return (not self.quiet, self.activity) if request[0] in ['chat message 2']: unpacked = safe_pickle.loads(request[1]) # TODO check unpacked against template # TODO don't unpack twice (here and in receive task) sender = unpacked['from'] recipients = unpacked['to'] for recipient in recipients: if recipient[0] == self.app.name_server.public_key_name: break if recipient[2][:1] == '#' and \ self.channels.is_listening_to(recipient[2]) and \ sender[0] != self.app.name_server.public_key_name: break else: return None if not self.repeated(address,call_id): if self.quiet: new_item = (request, address, time.time()) check.check_matches(new_item, unread_message_list_item_tmpl) # Proof: @R34,@R31, and not having modified request or # address, and the relevant types being immutable # (partly by @E11). time.time() returns float. # Relevance: @R.I15. self.unread_message_list.append(new_item) self.set_prompt() del new_item else: self.receive(request,address,self.quiet,time.time()) # Proof of @R33: @R31; deep immutability by @E11. # Proof of @R35: time.time() returns float. return (not self.quiet, self.activity) finally: self.lock.release()
def handle(self, request,address,call_id): check.check_matches(request, (types.StringType,)) check.check_is_af_inet_address(address) # Idempotence is a bugger if request[0] == 'data cache store' or \ request[0] == 'data cache store multi': self.lock.acquire() try: if self.cached_items.has_key(request[1]): if self.cached_items[request[1]].call == (address,call_id): return None return Error('already storing') if request[0] == 'data cache store': names = [ request[2] ] else: names = request[2] if len(names) > settings.max_cache_names: return Error('too many names') item = Cache_item(names,request[3],request[4],(address,call_id)) self.cached_items[request[1]] = item # here is the actual publish: redundancy is settings.cache_redundancy for name in names: self.node.publish(name, request[3], settings.cache_redundancy) finally: self.lock.release() return None if request[0] == 'data cache remove': self.lock.acquire() try: lock = hash.hash_of(request[1]) if self.cached_items.has_key(lock): self.node.unpublish(self.cached_items[lock].data) del self.cached_items[lock] finally: self.lock.release() return None
def random_inst(tmpl): # todo: Return an error.Error instance in more cases. # todo: This is currently specific to request results, in that # the results are always dumpable and sometimes contain error.Error # instances. check.check_is_template(tmpl) if type(tmpl) == types.TupleType: ret = tuple(map(random_inst, tmpl) + random_dumpable_list()) # `+ random_dumpable_list()' is because check_matches currently # allows so, and some requests do take a variable number of arguments. elif type(tmpl) == types.DictionaryType: ret = {} # Some extra items in case these keys are checked for. for i in range(random_nat(3)): ret[random.choice(extra_keys)] = random_dumpable() for (k,v) in tmpl.items(): ret[k] = random_inst(v) elif type(tmpl) == types.ListType: assert(tmpl != []) if len(tmpl) == 1: ret = [] for i in range(random_nat(2)): ret.append(random_inst(tmpl[0])) else: ret = random_inst(random.choice(tmpl)) elif type(tmpl) == types.TypeType: f = type_randers.get(tmpl) if f is not None: ret = f() else: print "Unhandled type template " + `tmpl` + "\n" assert(0) elif type(tmpl) == types.StringType: f = randers.get(tmpl) if f is not None: ret = f() else: print "Unhandled template string " + `tmpl` + "\n" assert(0) else: print "Unrecognized template " + `tmpl` + "\n" assert(0) check.check_matches(ret, tmpl) return ret
def handle(self, request, ignored__address, call_id): check.check_matches(request, (types.StringType,)) check.check_is_af_inet_address(ignored__address) if request[0] == 'gossip list': #TODO: Type checking, locking, oh, and this is really inefficient self.any_gossip_gets = 1 list = [ ] for item in self.gossip[:]: list.append((to_fixed(item.distance(self.app.name_server)), item.unit_decay_time, item.signature)) list.sort() return list[request[1]:request[2]] elif request[0] == 'gossip get': for item in self.gossip: if item.signature == request[1]: return item.string return error.Error(_("No such wodge."))
def handle(self, request, address, call_id): check.check_matches(request, (types.StringType,)) check.check_is_af_inet_address(address) if request[0] == 'auction bid': name = request[1] bid = request[2] if self.auctions.has_key(name): print "auction found" if self.auctions_prices[name] < bid: self.auctions_prices[name] = bid return 'ok' else: print "bid too low" return 'bid too low' else: return 'error: no such auction' elif request[0] == 'auction list': list = self.auctions return list
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)
def handle(self, request, address,call_id): check.check_matches(request, (types.StringType,)) check.check_is_af_inet_address(address) #=@R30 if request[0] == 'identity test': check.check_matches(request[1:], ('name',)) return self.sign('identity test ' + request[1]) elif request[0] == 'identity query': return self.get_info() elif request[0] == 'identity watch': self.lock.acquire() try: if address not in self.watchers: self.watchers.append(address) status = self.status finally: self.lock.release() return status elif request[0] == 'identity abort': def abort_thread(self, address=address): id_test_result = [] yield 'wait', identity_test_thread(address,self.public_key,self.node,id_test_result) if not id_test_result[0]: print "error testing identity" return self.aborted = 1 self.app.shutdown(_('Reconnecting from different machine')) utility.start_thread(abort_thread(self)) return None elif request[0] == 'identity connecting': if not (len(request) >= 2): return error.Error('Name_server.handle: identity connecting: expecting tuple of at least 2.') self.acquire_lock('get acq') try: acq = self.acquaintances.get(request[1]) finally: self.release_lock('get acq') if acq is None: return error.Error(_("I don't know you.")) else: #if len(request) > 2: # status = request[2] #else: # status = { } def acq_change_thread(ns, acq, address, call_id, node): check.check_is_af_inet_address(address) #=@R29 try: result, subthread = try_address(acq,address,node) yield 'wait',subthread if not result[0]: return except error.Error: return #acq.lock.acquire() #acq.status = status #acq.lock.release() ns.acquaintance_status_changed(acq, 'connect') utility.start_thread(acq_change_thread(self, acq, address, call_id, self.node)) # Proof of @R29: @R30; deeply immutable by @E11. return None elif request[0] == 'identity status changed': if not (len(request) >= 3): return error.Error('Name_server.handle: identity status changed: expecting tuple of at least 3.') self.lock.acquire() try: acq = self.acquaintances.get(request[1],None) if acq: acq.lock.acquire() try: acq.status = request[2] finally: acq.lock.release() self.acquaintance_status_changed(acq, 'status changed') finally: self.lock.release() elif request[0] == 'identity disconnecting': if not (len(request) >= 2): return error.Error('Name_server.handle: identity disconnecting: expecting tuple of at least 2.') self.lock.acquire() if address in self.watchers: self.watchers.remove(address) if self.acquaintances.has_key(request[1]): acq = self.acquaintances[request[1]] self.lock.release() if acq.disconnection(address): self.acquaintance_status_changed(acq, 'disconnect', message=request[2]) return None else: self.lock.release() return error.Error(_("I don't know you."))
def chat_tell_thread(chat, dests, message, attachment=None, augmentation=None): """Messages are sent in parallel, one thread per recipient. The information displayed in field ends with '...' until the last message has been sent, then with '..' until all recipients have answered, and finally with '.' The field is closed by the last thread to finish """ check.check_matches(dests, ['text']) check.check_is_text(message) std_timestamp = host2standard_timestamp(time.time()) if augmentation and len(augmentation) != len(message): sys.stderr.write("Augmentation %d bytes, should be %d bytes.\n" % (len(augmentation), len(message))) augmentation = None message_id = chat.create_message_id() try: result = ['','','','','...\n'] dest_list = [ ] dest_address = [ ] checked_address = [ ] dest_names = [ ] received_names = [ ] offline_list = [ ] offline_names = [ ] recipient_list = [ ] channel_pipes = { } chan_cnt=[0] for dest in dests: if dest[0]=='#': channel_pipes[dest] = chat.channels.sub_list_pipe(dest) recipient_list.append((None,None,dest)) continue acq = chat.app.name_server.locate(dest) acq.start_watching(chat.node) while not acq.watched: yield 'sleep',0.1 acq.lock.acquire() online = acq.online address = acq.address name = acq.name username = acq.info['name'] acq.lock.release() if not online: offline_list.append(acq) offline_names.append(dest) recipient_list.append((name,None,username)) else: dest_list.append(acq) dest_address.append(address) dest_names.append(dest) recipient_list.append((name,address,username)) my_details = ( chat.app.name_server.public_key_name, chat.app.name_server.name ) package = { 'from': my_details, 'to': recipient_list, 'text': message } if augmentation: package['aug'] = augmentation if attachment: package['attach'] = attachment package_pickle = safe_pickle.dumps(package) signature = chat.app.name_server.sign(package_pickle) message = ('chat message 2',package_pickle,signature) def recipient_thread(chat,address,name,channel,received_names,dest_address, checked_address,chan_cnt,message_id,result, message=message): succ = 1 try: ticket, template, wait = chat.node.call(address,message) if wait: yield ('call',(chat.node,ticket)) ret_value = chat.node.get_reply(ticket,template) #Type checking if type(ret_value) != types.TupleType: succ = 0 if not channel: result[2] = result[2] + name + _(' sent bad reply.\n') chat.update_message_status(message_id, result[0]+result[4]+result[2]) except error.Error,err: succ = 0 if not channel: result[2] = result[2] + name + _(' could not be contacted: ')+err.message+'\n' chat.update_message_status(message_id, result[0]+result[4]+result[2]) chat.app.name_server.bad_address(address) if succ: if channel: chan_cnt[0] += 1 else: received_names.append(name) if ret_value[0]: pass #if ret_value[1] != '': # result[2] = result[2] + name + ' ' + ret_value[1] + '\n' else: if ret_value[1] == '': result[2] = result[2] + name + _(' is quiet.\n') else: result[2] = result[2] + name + _(' is quiet: ') + ret_value[1] + '\n' if chan_cnt[0] == 0: result[0] = _('Message received by ') + utility.english_list(received_names) elif chan_cnt[0] == 1: result[0] = _('Message received by ')\ +utility.english_list(received_names+[ _('1 person')])+result[1] else: result[0] = _('Message received by ')\ +utility.english_list(received_names+[ _('%d people') % chan_cnt[0] ])+result[1] chat.update_message_status(message_id,result[0]+result[4]+result[2]) checked_address.append(address) checked_address.sort() if result[4]=='..\n' and checked_address == dest_address: if chan_cnt[0]==0 and received_names == []: result[0] = _('Nobody received your message') result[4] = '.\n' chat.update_message_status(message_id, result[0]+result[4]+result[2]) for i in range(len(dest_list)): utility.start_thread(recipient_thread(chat,dest_address[i],dest_names[i],0,\ received_names,dest_address,checked_address,chan_cnt,message_id,result)) if channel_pipes: if channel_pipes.__len__()>1: result[1] = (_(' on channels %s') % utility.english_list(channel_pipes.keys())) else: result[1] = (_(' on channel %s') % channel_pipes.keys()[0]) if channel_pipes: for chan_name in channel_pipes.keys(): if not chat.channels.cache.has_key(chan_name): chat.channels.cache[chan_name]=[] else: for address in chat.channels.cache[chan_name]: if address in dest_address: continue dest_address.append(address) utility.start_thread( recipient_thread(chat,address,'',1,received_names, dest_address,checked_address,chan_cnt,message_id,result)) #reset the cache: chat.channels.cache[chan_name]=[] while channel_pipes: for chan_name,chan_pipe in channel_pipes.items(): if chan_pipe.finished(): chan_pipe.stop() del channel_pipes[chan_name] continue for address in chan_pipe.read_all(): #update the cache if address not in chat.channels.cache[chan_name]: chat.channels.cache[chan_name].append(address) if address in dest_address: continue dest_address.append(address) utility.start_thread(recipient_thread(chat,address,'',1,received_names, dest_address,checked_address,chan_cnt,message_id,result)) yield 'sleep',0.1 #we now have launched all the tasks dest_address.sort() result[4]='..\n' if checked_address == dest_address: if chan_cnt[0]==0 and received_names == []: result[0] = _('Nobody received your message') result[4] = '.\n' chat.update_message_status(message_id, result[0]+result[4]+result[2]) recall_list = [ ] if offline_list: chat.update_message_status(message_id,'Caching message...\n',1) for i in range(len(offline_list)): key = utility.random_bytes(settings.name_bytes) lock = hash.hash_of(key) crypt = offline_list[i].encrypt((key, message, std_timestamp)) data = { 'type' : 'offline message', 'crypt': crypt } name = hash.hash_of('offline message '+offline_list[i].name) # Loose proof of @R50: @I21, @E22. recall_list.append((offline_list[i].nickname,name,data,key)) publish_result, subthread = chat.app.cache.publish([name],data,lock) yield 'wait',subthread if publish_result: redundancy = publish_result[0] else: redundancy = 0 if redundancy == settings.cache_redundancy: str = offline_names[i] + _(' will get your message when next logged on.\n') elif redundancy == 0: str = _('Could not store message for ') + offline_names[i] + '!\n' else: str = (offline_names[i] + _(' will probably get your message when next logged on:\n' +\ ' Message only stored with redundancy %d.\n') % redundancy) result[3] = result[3] + str chat.update_message_status(message_id,result[3]+'\n',1) chat.recall_list = recall_list
def publish_thread(self, result, names, data, lock, redundancy, expiry): if not (self.cache_pool_last_refresh+10*60 > time.time() or \ len(self.cache_pool) > self.cache_pool_original_size/2): cache_pool = [ ] pipe = self.node.retrieve(self.name, settings.cache_redundancy) while len(cache_pool) < 50 and not pipe.finished(): list = pipe.read_all() for item in list: check.check_matches(item, ('af_inet_address', 'any')) # Proof: @E6. Combine with @E10 and the fact that we don't # pass pipe to anything else (nor write to it ourselves # other than through pipe.read_all()) to show that @E6 # isn't broken by some other write. if type(item[1]) != types.DictionaryType: # todo: consider printing debug info continue if item[1].get('type','') == 'service data cache': #cache_pool.append((-item[1]['up time'],item[0],item[1])) up_time = item[1].get('up time') if type(up_time) != types.IntType: # todo: consider printing debug info continue # Treat all up for over a day as equal to share load if up_time > 24*60*60: up_time = 24*60*60 + random.random() cache_pool.append((-up_time,item[0],item[1])) yield 'sleep',1 pipe.stop() self.cache_pool = cache_pool self.cache_pool.sort() self.cache_pool_original_size = len(self.cache_pool) if not expiry: expiry = self.data['max expiry time'] if expiry < 0: result.append(0) return pool = self.cache_pool[:] bad_items = [ ] pos = 0 n = 0 while n < redundancy: if len(pool) <= pos: result.append(n) return try: if len(names) == 1: ticket, template, wait = self.node.call( pool[pos][1],('data cache store',lock,names[0],data,expiry)) if wait: yield 'call',(self.node,ticket) dummy_result = self.node.get_reply(ticket, template) else: ticket, template, wait = self.node.call( pool[pos][1],('data cache store multi',lock,names,data,expiry)) if wait: yield 'call',(self.node,ticket) dummy_result = self.node.get_reply(ticket, template) except Error, error: if error.message != 'already storing': bad_items.append(pool[pos]) else: # thomasV: we need to limit amplification # print 'already storing', pool[pos] n = n + 1 # end thomasV pos = pos + 1 continue pos = pos + 1 n = n + 1
def handle(self, request, address, call_id): check.check_matches(request, (types.StringType,)) check.check_is_af_inet_address(address) if request[0] == 'download chunk': path, mtime = self.paths.get(request[1],(None,None)) if not path: path, mtime = self.private_paths.get(request[1],(None,None)) try: if not path or \ os.path.getmtime(path) != mtime: return Error("no such file") file = open(path,'rb') except IOError: return Error("no such file") if address[0] !='127.0.0.1'\ and address not in self.node.trusted_addresses \ and self.private_directory != ''\ and utility.is_subdir(path,self.private_directory): return Error("access denied") try: file.seek(request[2]) return file.read(request[3]) finally: file.close() elif request[0] == 'files available': if len(request) == 3: # this returns the keys of # all published files from all directories # regardless of the directory structure list = self.paths.keys() else: list = [ ] access_denied = 0 directory_found = 0 string_request = [] for str in request[3]: string_request.append(utility.force_string(str)) if not self.roots: return Error('No public directory') request_dir = apply( os.path.join, [os.path.abspath(self.roots[0][0])] + string_request) if address[0]=='127.0.0.1': flags = flags_local else: flags = flags_fast if os.path.exists(request_dir): directory_found = 1 if address[0]!='127.0.0.1' \ and address not in self.node.trusted_addresses\ and self.private_directory != ''\ and utility.is_subdir(request_dir,self.private_directory): access_denied = 1 if not directory_found: return Error("no such directory: %s"%request_dir) elif access_denied: return Error("access denied") entry = build_entry(request_dir,None,flags,self) if entry: if not entry.is_dir: return Error("not a directory: "+request_dir) for file in entry.files: entry_2 = build_entry(file,None,flags,self) if entry_2: info = entry_2.info.copy() info['path'] = request[3] + [ info['filename'] ] list.append(info) return list[request[1]:request[2]]
def chat_check_invar(self): check.check_matches(self.unread_message_list, [unread_message_list_item_tmpl]) #=@I15 check.check_matches(self.incoming_message_history, [incoming_message_history_item_tmpl]) #=@I16 check.check_matches(self.channel, ['text']) #=@I31
def receive(self, request, address, quiet, send_time, add_to_history=1): """send_time should be as if from time.time() (i.e. a float expressing seconds since the local machine's epoch) rather than the standardized timestamps sent over the network (i.e. a long expressing seconds since the Unix epoch of 1970).""" # TODO: more checking of message format # TODO: search for people not in acq list check.check_is_opt_address(address) #=@R33 fixme callers check.check_has_type(send_time, types.FloatType) #=@R35 fixme callers # fixme: should probably require that request be a tuple with 5+ elems. if 1: if not self.is_visible(): self.n_unseen_messages = self.n_unseen_messages + 1 if self.n_unseen_messages == 1: title = _('1 unseen message') else: title = _('%d unseen messages') % self.n_unseen_messages self.app.set_title(title) if request[0] == 'chat message 2': package = safe_pickle.loads(request[1]) text = package['text'] augmentation = package.get('aug') has_attachment = package.has_key('attach') attachment = package.get('attach') sender_key_name, sender_name = package['from'] recipients = package['to'] verify_text = request[1] verify_sig = request[2] else: # Legacy message format text = request[3] augmentation = None has_attachment = (len(request) == 6) if has_attachment: attachment = safe_pickle.loads(request[4]) recipients = request[2] if type(request[1]) == type(()): sender_key_name = request[1][0] sender_name = request[1][1] else: # Used not to send username with key name sender_key_name = request[1] sender_name = 'unknown' if has_attachment: verify_text = request[3].encode('utf8')+request[4] verify_sig = request[5] else: verify_text = request[3] verify_sig = request[4] if not augmentation: augmentation = chr(128) * len(text) self.show_received_message(sender_name,sender_key_name,address,recipients, text,verify_text,verify_sig,quiet, send_time,augmentation,has_attachment,attachment) #except error.Error: # self.show(_('Bad message received from ') + repr(address) + '\n') if add_to_history: self.lock.acquire() try: new_item = (request, send_time) check.check_matches(new_item, incoming_message_history_item_tmpl) # Proof: @R35, immutability of float, send_time not reassigned # in this function. # Relevance: @I16. self.incoming_message_history.append(new_item) while len(self.incoming_message_history) > settings.incoming_message_history_size: del self.incoming_message_history[0] finally: self.lock.release()
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()