Example #1
0
	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
Example #2
0
    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