void drawBrushDabs(const protocol::Message &msg, paintcore::EditableLayerStack &layers) { auto layer = layers.getEditableLayer(msg.layer()); if(layer.isNull()) { qWarning("drawBrushDabs(ctx=%d, layer=%d): no such layer", msg.contextId(), msg.layer()); return; } drawBrushDabsDirect(msg, layer); }
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; }