def handle_participant_message(self, wavelet, pconn, message): """ Handle a participant's operation. If True is returned, go on with processing the next message. If False is returned, discard any following messages. """ participant = pconn.participant pconn.last_contact = datetime.datetime.now() pconn.save() if message.has_key(u"type"): if message["type"] == "PING": self.emit(pconn, "PONG", message["property"]) # Traditionally elif message["type"] == "WAVELET_OPEN": self.logger.info("[%s/%d@%s] Opening wavelet" % (participant.name, pconn.id, wavelet.wave.id)) pconn.wavelets.add(wavelet) # I know this is neat :) self.emit(pconn, "WAVELET_OPEN", { "wavelet": wavelet.serialize(), "blips": wavelet.serialize_blips(), }) elif message["type"] == "PARTICIPANT_INFO": self.logger.info("[%s/%d@%s] Sending participant information" % (participant.name, pconn.id, wavelet.wave.id)) p_info = {} for p_id in message["property"]: try: p_info[p_id] = Participant.objects.get(id=p_id).serialize() except ObjectDoesNotExist: p_info[p_id] = None self.emit(pconn, "PARTICIPANT_INFO", p_info) elif message["type"] == "PARTICIPANT_SEARCH": if len(message["property"]) < getattr(settings, "PARTICIPANT_SEARCH_LENGTH", 0): self.emit(pconn, "PARTICIPANT_SEARCH", {"result": "TOO_SHORT", "data": getattr(settings, "PARTICIPANT_SEARCH_LENGTH", 0)}) self.logger.debug("[%s/%d@%s] Participant search query too short" % (participant.name, pconn.id, wavelet.wave.id)) else: self.logger.info("[%s/%d@%s] Performing participant search" % (participant.name, pconn.id, wavelet.wave.id)) lst = [] for p in Participant.objects.filter(name__icontains=message["property"]).exclude(id=participant.id): lst.append(p.id) self.emit(pconn, "PARTICIPANT_SEARCH", {"result": "OK", "data": lst}) elif message["type"] == "GADGET_LIST": all_gadgets = map(lambda g: {"id": g.id, "uploaded_by": g.by_user.participants.all()[0].name, "name": g.title, "descr": g.description, "url": g.url}, Gadget.objects.all()) self.logger.info("[%s/%d@%s] Sending Gadget list" % (participant.name, pconn.id, wavelet.wave.id)) self.emit(pconn, "GADGET_LIST", all_gadgets) elif message["type"] == "WAVELET_ADD_PARTICIPANT": # Find participant try: p = Participant.objects.get(id=message["property"]) except ObjectDoesNotExist: self.logger.error("[%s/%d@%s] Target participant '%s' not found" % (participant.name, pconn.id, wavelet.wave.id, message["property"])) return True # Fail silently (TODO: report error to user) # Check if already participating if wavelet.participants.filter(id=message["property"]).count() > 0: self.logger.error("[%s/%d@%s] Target participant '%s' already there" % (participant.name, pconn.id, wavelet.wave.id, message["property"])) return True # Fail silently (TODO: report error to user) wavelet.participants.add(p) self.logger.info("[%s/%d@%s] Added new participant '%s'" % (participant.name, pconn.id, wavelet.wave.id, message["property"])) self.broadcast(wavelet, "WAVELET_ADD_PARTICIPANT", message["property"]) elif message["type"] == "WAVELET_REMOVE_SELF": self.broadcast(wavelet, "WAVELET_REMOVE_PARTICIPANT", participant.id) wavelet.participants.remove(participant) # Bye bye pconn.wavelets.remove(wavelet) # Also for your connection self.logger.info("[%s/%d@%s] Participant removed himself" % (participant.name, pconn.id, wavelet.wave.id)) if wavelet.participants.count() == 0: # Oh my god, you killed the Wave! You bastard! self.logger.info("[%s/%d@%s] Wave got killed!" % (participant.name, pconn.id, wavelet.wave.id)) wavelet.wave.delete() return False elif message["type"] == "OPERATION_MESSAGE_BUNDLE": # Build OpManager newdelta = OpManager(wavelet.wave.id, wavelet.id) newdelta.unserialize(message["property"]["operations"]) version = message["property"]["version"] # Transform for delta in wavelet.deltas.filter(version__gt=version): for op in delta.getOpManager().operations: newdelta.transform(op) # Trash results (an existing delta cannot be changed) # Apply wavelet.applyOperations(newdelta.operations) # Raise version and store wavelet.version += 1 wavelet.save() Delta.createByOpManager(newdelta, wavelet.version).save() # Create tentative checksums blipsums = wavelet.blipsums() # Respond self.emit(pconn, "OPERATION_MESSAGE_BUNDLE_ACK", {"version": wavelet.version, "blipsums": blipsums}) self.broadcast(wavelet, "OPERATION_MESSAGE_BUNDLE", {"version": wavelet.version, "operations": newdelta.serialize(), "blipsums": blipsums}, [pconn]) self.logger.debug("[%s/%d@%s] Processed delta #%d -> v%d" % (participant.name, pconn.id, wavelet.wave.id, version, wavelet.version)) else: self.logger.error("[%s/%d@%s] Unknown message: %s" % (participant.name, pconn.id, wavelet.wave.id, message)) else: self.logger.error("[%s/%d@%s] Unknown message: %s" % (participant.name, pconn.id, wavelet.wave.id, message)) return True
def handle_participant_message(self, wavelet, pconn, message): """ Handle a participant's operation. If True is returned, go on with processing the next message. If False is returned, discard any following messages. """ participant = pconn.participant pconn.last_contact = datetime.datetime.now() pconn.save() if message.has_key(u"type"): if message["type"] == "PING": # DEPRECATED: Use the manager! self.emit(pconn, "PONG", message["property"]) # Traditionally elif message["type"] == "WAVELET_OPEN": self.logger.info("[%s/%s@%s] Opening wavelet" % (participant.name, pconn.id, wavelet.wave.id)) pconn.wavelets.add(wavelet) # I know this is neat :) self.emit(pconn, "WAVELET_OPEN", {"wavelet": wavelet.serialize(), "blips": wavelet.serialize_blips()}) elif message["type"] == "WAVELET_CLOSE": # No reply pconn.wavelets.remove(wavelet) self.logger.info( "[%s/%s@%s] Connection to wavelet closed (by client)" % (participant.name, pconn.id, wavelet.wave.id) ) return False elif message["type"] == "PARTICIPANT_INFO": # DEPRECATED: Moved to manager! self.logger.info( "[%s/%s@%s] Sending participant information" % (participant.name, pconn.id, wavelet.wave.id) ) p_info = {} for p_id in message["property"]: try: p_info[p_id] = Participant.objects.get(id=p_id).serialize() except ObjectDoesNotExist: p_info[p_id] = None self.emit(pconn, "PARTICIPANT_INFO", p_info) elif message["type"] == "PARTICIPANT_SEARCH": # DEPRECATED: Moved to manager! if len(message["property"]) < getattr(settings, "PARTICIPANT_SEARCH_LENGTH", 0): self.emit( pconn, "PARTICIPANT_SEARCH", {"result": "TOO_SHORT", "data": getattr(settings, "PARTICIPANT_SEARCH_LENGTH", 0)}, ) self.logger.debug( "[%s/%s@%s] Participant search query too short" % (participant.name, pconn.id, wavelet.wave.id) ) else: self.logger.info( "[%s/%s@%s] Performing participant search" % (participant.name, pconn.id, wavelet.wave.id) ) lst = [] for p in Participant.objects.filter(name__icontains=message["property"]).exclude(id=participant.id): lst.append(p.id) self.emit(pconn, "PARTICIPANT_SEARCH", {"result": "OK", "data": lst}) elif message["type"] == "GADGET_LIST": # DEPRECATED: Moved to manager! all_gadgets = map( lambda g: { "id": g.id, "uploaded_by": g.by_user.participants.all()[0].name, "name": g.title, "descr": g.description, "url": g.url, }, Gadget.objects.all(), ) self.logger.info("[%s/%s@%s] Sending Gadget list" % (participant.name, pconn.id, wavelet.wave.id)) self.emit(pconn, "GADGET_LIST", all_gadgets) elif message["type"] == "WAVELET_ADD_PARTICIPANT": # DEPRECATED: Moved to OPERATION_MESSAGE_BUNDLE # Find participant try: p = Participant.objects.get(id=message["property"]) except ObjectDoesNotExist: self.emit( pconn, "ERROR", { "tag": "PARTICIPANT_NOT_FOUND", "desc": "A participant with id '%s' does not exist" % message["property"], }, ) self.logger.error( "[%s/%s@%s] Target participant '%s' not found" % (participant.name, pconn.id, wavelet.wave.id, message["property"]) ) return True # Check if already participating if wavelet.participants.filter(id=message["property"]).count() > 0: self.emit( pconn, "ERROR", { "tag": "PARTICIPANT_ALREADY_IN", "desc": "The participant with id '%s' already takes part" % message["property"], }, ) self.logger.error( "[%s/%s@%s] Target participant '%s' already there" % (participant.name, pconn.id, wavelet.wave.id, message["property"]) ) return True wavelet.participants.add(p) self.logger.info( "[%s/%s@%s] Added new participant '%s'" % (participant.name, pconn.id, wavelet.wave.id, message["property"]) ) self.broadcast(wavelet, "WAVELET_ADD_PARTICIPANT", message["property"], [], {"id": message["property"]}) elif message["type"] == "WAVELET_REMOVE_SELF": # DEPRECATED: Moved to OPERATION_MESSAGE_BUNDLE self.broadcast(wavelet, "WAVELET_REMOVE_PARTICIPANT", participant.id, [], {"id": participant.id}) wavelet.participants.remove(participant) # Bye bye if pconn.wavelets.filter(id=wavelet.id).count() > 0: pconn.wavelets.remove(wavelet) # Also for your connection self.logger.info( "[%s/%s@%s] Participant removed himself" % (participant.name, pconn.id, wavelet.wave.id) ) if wavelet.participants.count() == 0: # Oh my god, you killed the Wave! You bastard! self.logger.info("[%s/%s@%s] Wave got killed!" % (participant.name, pconn.id, wavelet.wave.id)) wavelet.wave.delete() return False elif message["type"] == "OPERATION_MESSAGE_BUNDLE": # Build OpManager newdelta = OpManager(wavelet.wave.id, wavelet.id) newdelta.unserialize(message["property"]["operations"]) version = message["property"]["version"] # Transform for delta in wavelet.deltas.filter(version__gt=version): for op in delta.getOpManager().operations: newdelta.transform(op) # Trash results (an existing delta cannot be changed) i = 0 added = [] while i < len(newdelta.operations): op = newdelta.operations[i] if op.type == WAVELET_REMOVE_PARTICIPANT: # Check self removal, prevent removal of others if op.property != participant.id: self.logger.error( "[%s/%s@%s] Participant tried to remove '%s'" % (participant.name, pconn.id, wavelet.wave.id, op.property) ) newdelta.removeOperation(i) continue self.broadcast_managers( wavelet, "WAVELET_REMOVE_PARTICIPANT", {"id": op.property}, [pconn], False ) elif op.type == WAVELET_ADD_PARTICIPANT: added.append(op.property) i += 1 # Apply wavelet.applyOperations(newdelta.operations) # Send manager messages for added participants for id in added: self.broadcast_managers(wavelet, "WAVELET_ADD_PARTICIPANT", {"id": id}, [pconn]) # Raise version and store wavelet.version += 1 wavelet.save() Delta.createByOpManager(newdelta, wavelet.version).save() # Create tentative checksums blipsums = wavelet.blipsums() # Respond self.emit(pconn, "OPERATION_MESSAGE_BUNDLE_ACK", {"version": wavelet.version, "blipsums": blipsums}) self.broadcast( wavelet, "OPERATION_MESSAGE_BUNDLE", {"version": wavelet.version, "operations": newdelta.serialize(), "blipsums": blipsums}, [pconn], ) self.logger.debug( "[%s/%s@%s] Processed delta #%d -> v%d" % (participant.name, pconn.id, wavelet.wave.id, version, wavelet.version) ) if wavelet.participants.count() == 0: # Oh my god, you killed the Wave! You bastard! self.logger.info("[%s/%s@%s] Wave got killed!" % (participant.name, pconn.id, wavelet.wave.id)) wavelet.wave.delete() else: self.emit( pconn, "ERROR", {"tag": "UNKNOWN_MESSAGE", "desc": "Type '%s' not recognised" % message["type"]} ) self.logger.error( "[%s/%s@%s] Unknown message: %s" % (participant.name, pconn.id, wavelet.wave.id, message) ) else: self.emit(pconn, "ERROR", {"tag": "UNKNOWN_MESSAGE", "desc": "Message lacks 'type' field"}) self.logger.error("[%s/%s@%s] Unknown message: %s" % (participant.name, pconn.id, wavelet.wave.id, message)) return True