예제 #1
0
파일: writer.cpp 프로젝트: callaa/Drawpile
bool Writer::writeMessage(const protocol::Message &msg)
{
	Q_ASSERT(m_file->isOpen());

	if(m_encoding == Encoding::Binary) {
		QVarLengthArray<char> buf(msg.length());
		const int len = msg.serialize(buf.data());
		Q_ASSERT(len == buf.length());
		if(m_file->write(buf.data(), len) != len)
			return false;

	} else {
		if(msg.type() == protocol::MSG_FILTERED) {
			// Special case: Filtered messages are
			// written as comments in the text format.
			const protocol::Filtered &fm = static_cast<const protocol::Filtered&>(msg);
			auto wrapped = fm.decodeWrapped();

			QString comment;
			if(wrapped.isNull()) {
				comment = QStringLiteral("FILTERED: undecodable message type #%1 of length %2")
					.arg(fm.wrappedType())
					.arg(fm.wrappedPayloadLength());

			} else {
				comment = QStringLiteral("FILTERED: ") + wrapped->toString();
			}

			return writeComment(comment);
		}

		QByteArray line = msg.toString().toUtf8();
		if(m_file->write(line) != line.length())
			return false;
		if(m_file->write("\n", 1) != 1)
			return false;

		// Write extra newlines after certain commands to give
		// the file some visual structure
		switch(msg.type()) {
		case protocol::MSG_UNDOPOINT:
			if(m_file->write("\n", 1) != 1)
				return false;
		default: break;
		}
	}

	return true;
}
예제 #2
0
void Writer::recordMessage(const protocol::Message &msg)
{
	Q_ASSERT(_file->isOpen());

	if(!_filterMeta || msg.isCommand() || isRecordableMeta(msg.type())) {
		// Write Interval message if sufficient time has passed since last message was written
		if(_minInterval>0) {
			qint64 now = QDateTime::currentMSecsSinceEpoch();
			qint64 interval = now - _interval;
			if(interval >= _minInterval) {
				protocol::Interval imsg(qMin(qint64(0xffff), interval));
				QVarLengthArray<char> ibuf(imsg.length());
				int ilen = imsg.serialize(ibuf.data());
				_file->write(ibuf.data(), ilen);
			}
			_interval = now;
		}

		// Write the actual message
		QVarLengthArray<char> buf(msg.length());
		int len = msg.serialize(buf.data());
		Q_ASSERT(len == buf.length());
		_file->write(buf.data(), len);
	}
}
예제 #3
0
/**
 * @brief Draw brush dabs onto a specific layer
 *
 * Typically, you should call drawBrushDabs instead. However, this function
 * must be called directly when you're drawing onto a preview sublayer.
 *
 * @param msg brush dab message
 * @param layer the layer to draw onto
 */
void drawBrushDabsDirect(const protocol::Message &msg, paintcore::EditableLayer layer, int sublayer)
{
	Q_ASSERT(!layer.isNull());

	switch(msg.type()) {
	case protocol::MSG_DRAWDABS_CLASSIC:
		drawClassicBrushDabs(static_cast<const protocol::DrawDabsClassic&>(msg), layer, sublayer);
		break;
	case protocol::MSG_DRAWDABS_PIXEL:
	case protocol::MSG_DRAWDABS_PIXEL_SQUARE:
		drawPixelBrushDabs(static_cast<const protocol::DrawDabsPixel&>(msg), layer, sublayer);
		break;
	default:
		qWarning("Unhandled dab type: %s", qPrintable(msg.messageName()));
	}
}
예제 #4
0
bool AclFilter::filterMessage(const protocol::Message &msg)
{
	using namespace protocol;

	// Session and user specific locks apply to all Command type messages
	if(msg.isCommand() && (m_sessionLocked || m_userlocks.contains(msg.contextId())))
		return false;

	// This user's access level tier determines which features are available
	const Tier tier = userTier(msg.contextId());

	switch(msg.type()) {
	case MSG_USER_JOIN:
		if((static_cast<const UserJoin&>(msg).flags() & UserJoin::FLAG_AUTH))
			m_auth.set(msg.contextId());

		// Make sure the user's access bits are up to date
		emit operatorListChanged(m_ops.toList());
		emit trustedUserListChanged(m_trusted.toList());

		if(msg.contextId() == m_myId) {
			for(int i=0;i<FeatureCount;++i)
				emit featureAccessChanged(Feature(i), canUseFeature(Feature(i)));
		}

		break;

	case MSG_USER_LEAVE: {
		// User left: remove locks
		m_ops.unset(msg.contextId());
		m_trusted.unset(msg.contextId());
		m_auth.unset(msg.contextId());
		m_userlocks.unset(msg.contextId());

		QMutableHashIterator<int,LayerAcl> i(m_layers);
		while(i.hasNext()) {
			i.next();
			if(i.value().exclusive.removeAll(msg.contextId())>0)
				emit layerAclChanged(i.key());
		}

		// Refresh UI
		if(msg.contextId() == m_myId) {
			setOperator(false);
			setTrusted(false);
			m_localUserLocked = false;
			emit localLockChanged(isLocked());
		}
		break; }

	case MSG_SESSION_OWNER:
		// This command is validated by the server
		updateSessionOwnership(static_cast<const SessionOwner&>(msg));
		return true;

	case MSG_TRUSTED_USERS:
		// this command is validated by the server
		updateTrustedUserList(static_cast<const TrustedUsers&>(msg));
		return true;

	case MSG_LAYER_ACL:
		if( tier <= featureTier(Feature::EditLayers) ||
		   (tier <= featureTier(Feature::OwnLayers) && layerCreator(msg.layer()) == msg.contextId())
		) {
			const auto &lmsg = static_cast<const LayerACL&>(msg);

			if(lmsg.layer() == 0) {
				// Layer 0 sets the general session lock.
				// Exclusive user list is not used in this case.
				if(tier > Tier::Op)
					return false;
				setSessionLock(lmsg.locked());
				return true;
			}

			const Tier tier = Tier(qBound(0, int(lmsg.tier()), TierCount));
			m_layers[lmsg.layer()] = LayerAcl { lmsg.locked(), tier, lmsg.exclusive() };

			// Emit this to refresh the UI in case our selected layer was (un)locked.
			// (We don't actually know which layer is selected in the UI here.)
			emit localLockChanged(isLocked());
			emit layerAclChanged(lmsg.layer());

			return true;
		}
		return false;

	case MSG_FEATURE_LEVELS: {
		if(tier > Tier::Op)
			return false;

		const auto &flmsg = static_cast<const FeatureAccessLevels&>(msg);
		for(int i=0;i<canvas::FeatureCount;++i) {
			setFeature(Feature(i), Tier(qBound(0, int(flmsg.featureTier(i)), canvas::TierCount)));
		}
		return true;
	}

	case MSG_USER_ACL: {
		if(tier > Tier::Op)
			return false;

		const auto &lmsg = static_cast<const UserACL&>(msg);
		m_userlocks.setFromList(lmsg.ids());
		emit userLocksChanged(lmsg.ids());
		setUserLock(m_userlocks.contains(m_myId));
		return true;
	}

	case MSG_LAYER_DEFAULT:
		return tier == Tier::Op;

	case MSG_CHAT:
		// Only operators can pin messages
		if(static_cast<const protocol::Chat&>(msg).isPin() && tier > Tier::Op)
			return false;
		break;

	case MSG_LASERTRAIL:
		return tier <= featureTier(Feature::Laser);

	case MSG_CANVAS_RESIZE: return tier <= featureTier(Feature::Resize);
	case MSG_PUTTILE: return tier == Tier::Op;
	case MSG_CANVAS_BACKGROUND: return tier <= featureTier(Feature::Background);

	case MSG_LAYER_CREATE: {
		if(tier > Tier::Op && layerCreator(msg.layer()) != msg.contextId()) {
			qWarning("non-op user %d tried to create layer with context id %d", msg.contextId(), layerCreator(msg.layer()));
			return false;
		}

		// Must have either general or ownlayer permission to create layers
		return tier <= featureTier(Feature::EditLayers) || tier <= featureTier(Feature::OwnLayers);
	}
	case MSG_LAYER_ATTR:
		if(static_cast<const protocol::LayerAttributes&>(msg).sublayer()>0 && tier > Tier::Op) {
			// Direct sublayer editing is used only by operators during session init
			return false;
		}
		Q_FALLTHROUGH();

	case MSG_LAYER_RETITLE:
	case MSG_LAYER_DELETE: {
		const uint8_t createdBy = layerCreator(msg.layer());
		// EDITLAYERS feature gives permission to edit all layers
		// OWNLAYERS feature gives permission to edit layers created by this user
		if(
			(createdBy != msg.contextId() && tier > featureTier(Feature::EditLayers)) ||
			(createdBy == msg.contextId() && tier > featureTier(Feature::OwnLayers))
		  )
			return false;

		if(msg.type() == MSG_LAYER_DELETE)
			m_layers.remove(msg.layer());
		break;
	}
	case MSG_LAYER_ORDER:
		return tier <= featureTier(Feature::EditLayers);

	case MSG_PUTIMAGE:
	case MSG_FILLRECT:
		return tier <= featureTier(Feature::PutImage) && !isLayerLockedFor(msg.layer(), msg.contextId(), tier);

	case MSG_DRAWDABS_CLASSIC:
	case MSG_DRAWDABS_PIXEL:
		return !isLayerLockedFor(msg.layer(), msg.contextId(), tier);

	case MSG_REGION_MOVE:
		return tier <= featureTier(Feature::RegionMove) && !isLayerLockedFor(msg.layer(), msg.contextId(), tier);

	case MSG_ANNOTATION_CREATE:
		if(tier > featureTier(Feature::CreateAnnotation))
			return false;

		if(tier > Tier::Op && layerCreator(msg.layer()) != msg.contextId()) {
			qWarning("non-op user %d tried to create annotation with context id %d", msg.contextId(), layerCreator(msg.layer()));
			return false;
		}
		break;
	case MSG_ANNOTATION_EDIT:
		// Non-operators can't edit protected annotations created by other users
		if(m_protectedAnnotations.contains(layerCreator(msg.layer())) && tier > Tier::Op && layerCreator(msg.layer()) != msg.contextId())
			return false;

		if((static_cast<const AnnotationEdit&>(msg).flags() & protocol::AnnotationEdit::FLAG_PROTECT))
			m_protectedAnnotations.insert(msg.layer());
		else
			m_protectedAnnotations.remove(msg.layer());
		break;
	case MSG_ANNOTATION_DELETE:
	case MSG_ANNOTATION_RESHAPE:
		if(m_protectedAnnotations.contains(msg.layer()) && tier > Tier::Op && layerCreator(msg.layer())!=msg.contextId())
			return false;
		if(msg.type() == MSG_ANNOTATION_DELETE)
			m_protectedAnnotations.remove(msg.layer());
		break;

	case MSG_UNDO:
		if(tier > featureTier(Feature::Undo))
			return false;

		// Only operators can override Undos.
		if(tier > Tier::Op && static_cast<const Undo&>(msg).overrideId()>0)
			return false;
		break;

	default: break;
	}

	return true;
}
예제 #5
0
void IndexBuilder::addToIndex(const protocol::Message &msg)
{
	IndexType type = IDX_NULL;
	switch(msg.type()) {
	using namespace protocol;
	case MSG_CANVAS_RESIZE: type = IDX_RESIZE; break;

	case MSG_LAYER_CREATE:
	case MSG_LAYER_COPY: type = IDX_CREATELAYER; break;

	case MSG_LAYER_DELETE: type = IDX_DELETELAYER; break;
	case MSG_PUTIMAGE: type = IDX_PUTIMAGE; break;

	case MSG_PEN_MOVE:
	case MSG_PEN_UP: type = IDX_STROKE; break;

	case MSG_TOOLCHANGE:
		_colors[msg.contextId()] = static_cast<const protocol::ToolChange&>(msg).color_h();
		break;

	case MSG_ANNOTATION_CREATE:
	case MSG_ANNOTATION_DELETE:
	case MSG_ANNOTATION_EDIT:
	case MSG_ANNOTATION_RESHAPE: type = IDX_ANNOTATE; break;

	case MSG_CHAT: type = IDX_CHAT; break;
	case MSG_INTERVAL: type = IDX_PAUSE; break;

	case MSG_MOVEPOINTER: type = IDX_LASER; break;

	case MSG_MARKER: type = IDX_MARKER; break;

	case MSG_USER_JOIN:
		_index._ctxnames[msg.contextId()] = static_cast<const protocol::UserJoin&>(msg).name();
		return;

	default: break;
	}

	if(type==IDX_NULL) {
		return;

	} else if(type==IDX_PUTIMAGE || type==IDX_ANNOTATE) {
		// Combine consecutive messages from the same user
		for(int i=_index._index.size()-1;i>=0;--i) {
			IndexEntry &e = _index._index[i];
			if(e.context_id == msg.contextId()) {
				if(e.type == type) {
					e.end = _pos;
					return;
				}
				break;
			}
		}

	} else if(type==IDX_LASER) {
		// Combine laser pointer strokes and drop other MovePointer messages
		for(int i=_index._index.size()-1;i>=0;--i) {
			IndexEntry &e = _index._index[i];
			if(e.context_id == msg.contextId()) {
				if(e.type == type) {
					int persistence = static_cast<const protocol::MovePointer&>(msg).persistence();
					if(persistence==0) {
						e._finished = true;
						return;
					} else if(!e._finished) {
						e.end = _pos;
						return;
					}
				}
				break;
			}
		}

	} else if(type==IDX_STROKE) {
		// Combine all strokes up to last pen-up from the same user
		for(int i=_index._index.size()-1;i>=0;--i) {
			IndexEntry &e = _index._index[i];
			if(e.context_id == msg.contextId() && e.type == IDX_STROKE) {
				if(!e._finished) {
					e.end = _pos;
					if(msg.type() == protocol::MSG_PEN_UP)
						e._finished = true;
					return;
				}
				break;
			}
		}
	}

	// New index entry
	_index._index.append(IndexEntry(type, msg.contextId(), _offset, _pos, _pos, _colors[msg.contextId()]));
}