void Client::receiveMessages() { while(d->msgqueue->isPending()) { MessagePtr msg = d->msgqueue->getPending(); if(d->session.isNull()) { // No session? We must be in the login phase if(msg->type() == protocol::MSG_COMMAND) emit loginMessage(msg); else log(Log().about(Log::Level::Warn, Log::Topic::RuleBreak).message( QString("Got non-login message (type=%1) in login state").arg(msg->type()) )); } else { // Enforce origin ID, except when receiving a snapshot if(d->session->initUserId() != d->id) msg->setContextId(d->id); if(isHoldLocked()) d->holdqueue << msg; else d->session->handleClientMessage(*this, msg); } } }
/** * @brief Handle messages in normal session mode * * This one is pretty simple. The message is validated to make sure * the client is authorized to send it, etc. and it is added to the * main message stream, from which it is distributed to all connected clients. * @param msg the message received from the client */ void Client::handleSessionMessage(MessagePtr msg) { // Filter away blatantly unallowed messages switch(msg->type()) { using namespace protocol; case MSG_LOGIN: case MSG_USER_JOIN: case MSG_USER_ATTR: case MSG_USER_LEAVE: case MSG_SESSION_CONFIG: case MSG_STREAMPOS: logger::notice() << this << "Got server-to-user only command" << msg->type(); return; case MSG_DISCONNECT: // we don't do anything with disconnect notifications from the client return; default: break; } if(msg->isOpCommand() && !isOperator()) { logger::notice() << this << "Tried to use operator command" << msg->type(); return; } // Layer control locking if(_session->isLayerControlLocked() && !isOperator()) { switch(msg->type()) { using namespace protocol; case MSG_LAYER_CREATE: case MSG_LAYER_ATTR: case MSG_LAYER_ORDER: case MSG_LAYER_RETITLE: case MSG_LAYER_DELETE: logger::debug() << this << "Non-operator use of layer control command"; return; default: break; } } // Locking (note. applies only to command stream) if(msg->isCommand()) { if(isDropLocked()) { // ignore command return; } else if(isHoldLocked()) { _holdqueue.append(msg); return; } // Layer specific locking. Drop commands that affect layer contents switch(msg->type()) { using namespace protocol; case MSG_PEN_MOVE: if(isLayerLocked(_session->drawingContext(_id).currentLayer)) return; break; case MSG_LAYER_ATTR: if(!isOperator() && isLayerLocked(msg.cast<LayerAttributes>().id())) return; break; case MSG_LAYER_RETITLE: if(!isOperator() && isLayerLocked(msg.cast<LayerRetitle>().id())) return; break; case MSG_LAYER_DELETE: { const LayerDelete &ld = msg.cast<LayerDelete>(); if(!isOperator() && isLayerLocked(ld.id())) return; // When merging, the layer below must be unlocked (and exist!) if(ld.merge()) { const LayerState *layer = _session->getLayerBelowId(ld.id()); if(!layer || isLayerLocked(layer->id)) return; } } break; case MSG_PUTIMAGE: if(isLayerLocked(msg.cast<PutImage>().layer())) return; break; case MSG_FILLRECT: if(isLayerLocked(msg.cast<FillRect>().layer())) return; break; default: /* other types are always allowed */ break; } } // Make sure the origin user ID is set msg->setContextId(_id); // Track state and special commands switch(msg->type()) { using namespace protocol; case MSG_TOOLCHANGE: _session->drawingContextToolChange(msg.cast<ToolChange>()); break; case MSG_PEN_MOVE: _session->drawingContextPenDown(msg.cast<PenMove>()); break; case MSG_PEN_UP: _session->drawingContextPenUp(msg.cast<PenUp>()); if(_barrierlock == BARRIER_WAIT) { _barrierlock = BARRIER_LOCKED; emit barrierLocked(); } break; case MSG_LAYER_CREATE: _session->createLayer(msg.cast<LayerCreate>(), true); break; case MSG_LAYER_ORDER: _session->reorderLayers(msg.cast<LayerOrder>()); break; case MSG_LAYER_DELETE: // drop message if layer didn't exist if(!_session->deleteLayer(msg.cast<LayerDelete>().id())) return; break; case MSG_LAYER_ACL: // drop message if layer didn't exist if(!_session->updateLayerAcl(msg.cast<LayerACL>())) return; break; case MSG_ANNOTATION_CREATE: _session->createAnnotation(msg.cast<AnnotationCreate>(), true); break; case MSG_ANNOTATION_DELETE: // drop message if annotation didn't exist if(!_session->deleteAnnotation(msg.cast<AnnotationDelete>().id())) return; break; case MSG_UNDOPOINT: // keep track of undo points handleUndoPoint(); break; case MSG_UNDO: // validate undo command if(!handleUndoCommand(msg.cast<Undo>())) return; break; case MSG_CHAT: // Chat is used also for operator commands if(msg->isOpCommand()) { handleSessionOperatorCommand(this, msg.cast<Chat>().message()); return; } // Normal chat messages are not included in the session history, // except when preservechat setting is set if(!msg.cast<Chat>().isAnnouncement() && !_session->isChatPreserved()) { for(Client *c : _session->clients()) c->sendDirectMessage(msg); return; } break; case MSG_SNAPSHOT: handleSnapshotStart(msg.cast<SnapshotMode>()); return; case MSG_SESSION_TITLE: // Keep track of title changes _session->setTitle(msg.cast<SessionTitle>().title()); break; default: break; } // Add to main command stream to be distributed to everyone _session->addToCommandStream(msg); }
/** * @brief Handle messages in normal session mode * * This one is pretty simple. The message is validated to make sure * the client is authorized to send it, etc. and it is added to the * main message stream, from which it is distributed to all connected clients. * @param msg the message received from the client */ void Client::handleSessionMessage(MessagePtr msg) { // Filter away blatantly unallowed messages switch(msg->type()) { using namespace protocol; case MSG_LOGIN: case MSG_USER_JOIN: case MSG_USER_ATTR: case MSG_USER_LEAVE: case MSG_SESSION_CONFIG: case MSG_STREAMPOS: _server->printDebug(QString("Warning: user #%1 sent server-to-user only command %2").arg(_id).arg(msg->type())); return; default: break; } if(msg->isOpCommand() && !_isOperator) { _server->printDebug(QString("Warning: normal user #%1 tried to use operator command %2").arg(_id).arg(msg->type())); return; } // Layer control locking if(_server->session().layerctrllocked && !_isOperator) { switch(msg->type()) { using namespace protocol; case MSG_LAYER_CREATE: case MSG_LAYER_ATTR: case MSG_LAYER_ORDER: case MSG_LAYER_RETITLE: case MSG_LAYER_DELETE: _server->printDebug(QString("Blocked layer control command from non-operator #%1").arg(_id)); return; default: break; } } // Locking (note. applies only to command stream) if(msg->isCommand()) { if(isDropLocked()) { // ignore command return; } else if(isHoldLocked()) { _holdqueue.append(msg); return; } // Layer specific locking. Drop commands that affect layer contents switch(msg->type()) { using namespace protocol; case MSG_PEN_MOVE: if(isLayerLocked(_server->session().drawingctx[_id].currentLayer)) return; break; case MSG_LAYER_ATTR: if(!_isOperator && isLayerLocked(msg.cast<LayerAttributes>().id())) return; break; case MSG_LAYER_RETITLE: if(!_isOperator && isLayerLocked(msg.cast<LayerRetitle>().id())) return; break; case MSG_LAYER_DELETE: if(!_isOperator && isLayerLocked(msg.cast<LayerDelete>().id())) return; break; case MSG_PUTIMAGE: if(isLayerLocked(msg.cast<PutImage>().layer())) return; break; default: /* other types are always allowed */ break; } } // Make sure the origin user ID is set msg->setContextId(_id); // Track state and special commands switch(msg->type()) { using namespace protocol; case MSG_TOOLCHANGE: _server->session().drawingContextToolChange(msg.cast<ToolChange>()); break; case MSG_PEN_MOVE: _server->session().drawingContextPenDown(msg.cast<PenMove>()); break; case MSG_PEN_UP: _server->session().drawingContextPenUp(msg.cast<PenUp>()); if(_barrierlock == BARRIER_WAIT) { _barrierlock = BARRIER_LOCKED; emit barrierLocked(); } break; case MSG_LAYER_CREATE: _server->session().createLayer(msg.cast<LayerCreate>(), true); break; case MSG_LAYER_ORDER: _server->session().reorderLayers(msg.cast<LayerOrder>()); break; case MSG_LAYER_DELETE: // drop message if layer didn't exist if(!_server->session().deleteLayer(msg.cast<LayerDelete>().id())) return; break; case MSG_LAYER_ACL: // drop message if layer didn't exist if(!_server->session().updateLayerAcl(msg.cast<LayerACL>())) return; break; case MSG_ANNOTATION_CREATE: _server->session().createAnnotation(msg.cast<AnnotationCreate>(), true); break; case MSG_ANNOTATION_DELETE: // drop message if annotation didn't exist if(!_server->session().deleteAnnotation(msg.cast<AnnotationDelete>().id())) return; break; case MSG_UNDOPOINT: // keep track of undo points handleUndoPoint(); break; case MSG_UNDO: // validate undo command if(!handleUndoCommand(msg.cast<Undo>())) return; break; case MSG_CHAT: // Chat is used also for operator commands if(_isOperator && handleOperatorCommand(msg->contextId(), msg.cast<Chat>().message())) return; break; case MSG_SNAPSHOT: handleSnapshotStart(msg.cast<SnapshotMode>()); return; default: break; } // Add to main command stream to be distributed to everyone _server->addToCommandStream(msg); }