diff --git a/cbc/client-server/collaboration_client_server.pro b/cbc/client-server/collaboration_client_server.pro new file mode 100644 index 0000000..74e85a3 --- /dev/null +++ b/cbc/client-server/collaboration_client_server.pro @@ -0,0 +1,91 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2011-04-16T14:28:32 +# +#------------------------------------------------- + +QT += core gui network + +TARGET = collaboration_client_server +TEMPLATE = app + +COMMON_SOURCES += ../common/wtmessage.cpp \ + ../common/wtloginmessage.cpp \ + ../common/wtsessionlistrequest.cpp \ + ../common/messagetransceiver.cpp \ + ../common/wtlogoutrequest.cpp \ + ../common/wtpicturerequest.cpp \ + ../common/wtpictureresponse.cpp \ + ../common/wtsessioncreaterequest.cpp \ + ../common/wtsessioncreateresponse.cpp \ + ../common/wtsessionjoinrequest.cpp \ + ../common/wtsessionjoinresponse.cpp \ + ../common/wtsessionleaverequest.cpp \ + ../common/wtsessionleaveresponse.cpp \ + ../common/wtsessionlistresponse.cpp \ + ../common/wtsessionmemberupdate.cpp \ + ../common/wtupdatedrawing.cpp \ + ../common/wtupdatedrawingserver.cpp \ + ../common/wtwritepermissionrequest.cpp \ + ../common/wtwritepermissionstatus.cpp \ + ../common/wtloginresponse.cpp \ + ../common/protocolhandler.cpp \ + ../common/wtpeerhandshake.cpp \ + ../common/collaborationsession.cpp \ + +CWIDGET_SOURCES += ../collaborative_drawing_widget/basedrawingwidget.cpp \ + ../collaborative_drawing_widget/drawingdata.cpp \ + ../collaborative_drawing_widget/collaborativedrawingwidget.cpp \ + ../collaborative_drawing_widget/drawingaction.cpp + +COMMON_HEADERS += ../common/wtmessage.h \ + ../common/wtloginmessage.h \ + ../common/wtsessionlistrequest.h \ + ../common/messagetransceiver.h \ + ../common/wtlogoutrequest.h \ + ../common/wtpicturerequest.h \ + ../common/wtpictureresponse.h \ + ../common/wtsessioncreaterequest.h \ + ../common/wtsessioncreateresponse.h \ + ../common/wtsessionjoinrequest.h \ + ../common/wtsessionjoinresponse.h \ + ../common/wtsessionleaverequest.h \ + ../common/wtsessionleaveresponse.h \ + ../common/wtsessionlistresponse.h \ + ../common/wtsessionmemberupdate.h \ + ../common/wtupdatedrawing.h \ + ../common/wtupdatedrawingserver.h \ + ../common/wtwritepermissionrequest.h \ + ../common/wtwritepermissionstatus.h \ + ../common/wtloginresponse.h \ + ../common/protocolhandler.h \ + ../common/wtpeerhandshake.h \ + ../common/collaborationsession.h + +CWIDGET_HEADERS += ../collaborative_drawing_widget/basedrawingwidget.h \ + ../collaborative_drawing_widget/drawingdata.h \ + ../collaborative_drawing_widget/collaborativedrawingwidget.h \ + ../collaborative_drawing_widget/drawingaction.h \ + ../collaborative_drawing_widget/appglobals.h + +INCLUDEPATH += . \ + ../common \ + ../client \ + ../server \ + ../collaborative_drawing_widget + +SOURCES += main.cpp\ + mainwindow.cpp \ + ../server/collaborationserver.cpp \ + ../client/collaborationclient.cpp \ + $$COMMON_SOURCES \ + $$CWIDGET_SOURCES + + +HEADERS += mainwindow.h \ + ../server/collaborationserver.h \ + ../client/collaborationclient.h \ + $$COMMON_HEADERS \ + $$CWIDGET_HEADERS + +FORMS += mainwindow.ui diff --git a/cbc/client-server/main.cpp b/cbc/client-server/main.cpp new file mode 100644 index 0000000..9ae175b --- /dev/null +++ b/cbc/client-server/main.cpp @@ -0,0 +1,11 @@ +#include +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/cbc/client-server/mainwindow.cpp b/cbc/client-server/mainwindow.cpp new file mode 100644 index 0000000..ec17587 --- /dev/null +++ b/cbc/client-server/mainwindow.cpp @@ -0,0 +1,176 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include "drawingdata.h" + +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); + DrawingData *drawingData = new DrawingData(this); + ui->graphicsView->setDrawingData(drawingData); + connect(ui->actionUndo,SIGNAL(triggered()),ui->graphicsView->getDrawingData()->getUndoStack(), SLOT(undo())); + connect(ui->actionRedo,SIGNAL(triggered()),ui->graphicsView->getDrawingData()->getUndoStack(), SLOT(redo())); + + ui->stackedWidget->setCurrentIndex(1); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::on_actionRedPen_triggered() +{ + QPen current = ui->graphicsView->getDrawingPen(); + current.setColor(Qt::red); + ui->graphicsView->setDrawingPen(current); + ui->graphicsView->setDrawingMode(DRAWINGMODE_FREEHAND); +} + +void MainWindow::on_actionBlackPen_triggered() +{ + QPen current = ui->graphicsView->getDrawingPen(); + current.setColor(Qt::black); + ui->graphicsView->setDrawingPen(current); + ui->graphicsView->setDrawingMode(DRAWINGMODE_FREEHAND); +} + +void MainWindow::on_actionBluePen_triggered() +{ + QPen current = ui->graphicsView->getDrawingPen(); + current.setColor(Qt::blue); + ui->graphicsView->setDrawingPen(current); + ui->graphicsView->setDrawingMode(DRAWINGMODE_FREEHAND); +} + +void MainWindow::on_actionPenWidthInc_triggered() +{ + QPen current = ui->graphicsView->getDrawingPen(); + current.setWidth(current.width()+1); + ui->graphicsView->setDrawingPen(current); +} + +void MainWindow::on_actionPenWidthDec_triggered() +{ + QPen current = ui->graphicsView->getDrawingPen(); + current.setWidth(current.width()-1); + ui->graphicsView->setDrawingPen(current); +} + +void MainWindow::on_actionEraser_triggered() +{ + ui->graphicsView->setDrawingMode(DRAWINGMODE_ERASER); +} + +void MainWindow::sessionJoinResult(QString sessionName, QChar result, QHash users) +{ + QMessageBox m; + if(result == 1) { + m.setText("Joined session " + sessionName + ", you may start drawing"); + m.exec(); + ui->stackedWidget->setCurrentIndex(0); + } else { + m.setText("Failed to join session " + sessionName + ", check the password"); + m.exec(); + ui->stackedWidget->setCurrentIndex(1); + } + + qWarning() << "Session join result: " << sessionName << " : " << result << " : " << users.count(); +} + +void MainWindow::drawingCommitted(QString sessionName, QPicture pictureData) +{ + QByteArray picData(pictureData.data(), pictureData.size()); + client->sendDrawing(sessionName, picData); +} + +void MainWindow::on_actionDrawing_triggered() +{ + ui->stackedWidget->setCurrentIndex(0); +} + +void MainWindow::on_actionSessions_triggered() +{ + ui->stackedWidget->setCurrentIndex(1); +} + +void MainWindow::on_startServerButton_clicked() +{ + mt = new MessageTransceiver(); + ph = new ProtocolHandler(); + ph->setMessageTransceiver(mt); + server = new CollaborationServer(); + server->setProtocolHandler(ph); + client = new CollaborationClient(); + client->setProtocolHandler(ph); + + connect(client, SIGNAL(sessionJoinResult(QString,QChar,QHash)), this, SLOT(sessionJoinResult(QString,QChar,QHash))); + connect(ui->graphicsView, SIGNAL(drawingCommited(QString,QPicture)), this, SLOT(drawingCommitted(QString,QPicture))); + connect(client, SIGNAL(drawingArrived(QString,QByteArray,bool)), ui->graphicsView, SLOT(drawingArrived(QString,QByteArray,bool))); + + connect(client, SIGNAL(sessionListAvailable(QStringList)), this, SLOT(gotSessionList(QStringList))); + + + ui->startServerButton->setEnabled(false); + ui->statusLabel->setText("Running"); + server->setServerUserName(ui->userName->text()); + mt->start(); + + client->loginToServer(QHostAddress("127.0.0.1"), ui->userName->text(), ui->userName->text()); + client->refreshSessionList(); + + ui->sessionBox->setEnabled(true); +} + +void MainWindow::gotSessionList(QStringList sessionList) +{ + ui->sessionList->clear(); + ui->sessionList->addItems(sessionList); +} + +void MainWindow::on_refreshList_clicked() +{ + client->refreshSessionList(); +} + +void MainWindow::on_pushButton_clicked() +{ + client->joinSession(ui->sessionList->currentItem()->text(), ui->sessionPassword->text()); +} + +void MainWindow::on_actionClear_triggered() +{ + ui->graphicsView->clear(); +} + +void MainWindow::on_actionFreehand_triggered() +{ + ui->graphicsView->setDrawingMode(DRAWINGMODE_FREEHAND); +} + +void MainWindow::on_actionRectangle_triggered() +{ + ui->graphicsView->setDrawingMode(DRAWINGMODE_RECTANGLE); +} + +void MainWindow::on_actionStraightLine_triggered() +{ + ui->graphicsView->setDrawingMode(DRAWINGMODE_STRAIGHTLINE); +} + +void MainWindow::on_btn_createSession_clicked() +{ + client->createSession("sessozz", "1234"); +} + +void MainWindow::on_btn_leaveSession_clicked() +{ + client->leaveSession(client->getActiveSession()); + ui->stackedWidget->setCurrentIndex(1); +} diff --git a/cbc/client-server/mainwindow.h b/cbc/client-server/mainwindow.h new file mode 100644 index 0000000..cad2b8c --- /dev/null +++ b/cbc/client-server/mainwindow.h @@ -0,0 +1,52 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include "collaborationclient.h" +#include "collaborationserver.h" +#include "sessionjoindialog.h" + +namespace Ui { + class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private: + Ui::MainWindow *ui; + CollaborationClient *client; + CollaborationServer *server; + ProtocolHandler *ph; + MessageTransceiver *mt; + +private slots: + void on_btn_leaveSession_clicked(); + void on_btn_createSession_clicked(); + void on_actionStraightLine_triggered(); + void on_actionRectangle_triggered(); + void on_actionFreehand_triggered(); + void on_actionClear_triggered(); + void on_pushButton_clicked(); + void on_refreshList_clicked(); + void on_startServerButton_clicked(); + void on_actionSessions_triggered(); + void on_actionDrawing_triggered(); + void on_actionPenWidthDec_triggered(); + void on_actionPenWidthInc_triggered(); + void on_actionBluePen_triggered(); + void on_actionBlackPen_triggered(); + void on_actionRedPen_triggered(); + void on_actionEraser_triggered(); + void sessionJoinResult(QString sessionName, QChar result, QHash users); + void drawingCommitted(QString sessionName, QPicture pictureData); + void gotSessionList(QStringList sessionList); +}; + +#endif // MAINWINDOW_H diff --git a/cbc/client-server/mainwindow.ui b/cbc/client-server/mainwindow.ui new file mode 100644 index 0000000..0324715 --- /dev/null +++ b/cbc/client-server/mainwindow.ui @@ -0,0 +1,302 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + Collaborative Drawing Client-Server + + + + + + + Leave Current Session + + + + + + + + + + + false + + + + + + + + + + + Connection + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;"> +<table style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Username:</span></p></td></tr></table></body></html> + + + + + + + Server + + + 8 + + + + + + + Start server + + + + + + + not running + + + Qt::AlignCenter + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;"> +<table style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Status:</span></p></td></tr></table></body></html> + + + + + + + + + + false + + + Sessions + + + + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Enter Password: + + + + + + + + + + Refresh list + + + + + + + Join selected session + + + + + + + Create Session + + + + + + + + + + + + + + + + + + TopToolBarArea + + + false + + + + + + + + + + + + + + + + + Save + + + Save + + + Ctrl+S + + + + + Load + + + Load + + + Ctrl+O + + + + + Undo + + + Ctrl+Z + + + + + Redo + + + Ctrl+Shift+Z + + + + + Red + + + + + Black + + + + + Blue + + + + + PenW++ + + + + + PenW-- + + + + + Freehand + + + + + Rectangle + + + + + Straight Line + + + + + Ellipse + + + + + Eraser + + + + + Sessions + + + + + Drawing + + + + + Clear + + + + + + + CollaborativeDrawingWidget + QGraphicsView +
collaborativedrawingwidget.h
+
+
+ + +
diff --git a/cbc/client/appglobals.h b/cbc/client/appglobals.h new file mode 100644 index 0000000..aaef23d --- /dev/null +++ b/cbc/client/appglobals.h @@ -0,0 +1,65 @@ +#ifndef APPGLOBALS_H +#define APPGLOBALS_H + +#include +#include +#include + +typedef enum _ContentType { + CONTENTTYPE_UNDEFINED, + CONTENTTYPE_PRESENTATION, + CONTENTTYPE_VIDEO, + CONTENTTYPE_WEBPAGE +} ContentType; + +// constants and/or shortcuts +#define SCREEN_WIDTH qApp->desktop()->width() +#define SCREEN_HEIGHT qApp->desktop()->height() + +#define ERASER_SIZE 5 + +#define MOUSE_PRESSED 0 +#define MOUSE_MOVE 1 +#define MOUSE_RELEASED 2 + +// W&T specific directories +#define CONFIG_DIR qApp->applicationDirPath() + "/" + QString("configuration") +#define CACHE_DIR qApp->applicationDirPath() + "/" + QString("cache") +#define TOOLS_DIR qApp->applicationDirPath() + "/" + QString("tools") + +#define ANNOTATION_DIR qApp->applicationDirPath() + "/" + QString("annotations") +#define IMAGE_DIR qApp->applicationDirPath() + "/" + QString("images"); // for rendered images. +#define SKETCH_DIR qApp->applicationDirPath() + "/" + QString("sketch"); // for sketch. +#define SCREENSHOT_DIR qApp->applicationDirPath() + "/" + QString("screenshot"); // for sketch. +#define ANNOTATION_PREFIX QString("annotation_") +#define ANNOTATION_EXTENSION QString(".wta") + +#define NUM_RECENT_ITEMS 5 +#define RECENT_ITEMS_STORAGE CONFIG_DIR + QString("/recent.txt") + +extern class RecentlyUsed *recentlyUsed; +extern class GoogleDocsAccess *googleDocsAccess; +extern class EventGenerator *eventGenerator; + +// define the default tools for drawing +#define DEFAULT_DRAWING_PEN QPen(QBrush(Qt::black, Qt::SolidPattern), 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin) +#define DEFAULT_DRAWING_BRUSH QBrush(Qt::transparent, Qt::SolidPattern) +#define DEFAULT_ERASER QPen(Qt::red) + +typedef enum _DrawingMode { + DRAWINGMODE_FREEHAND, + DRAWINGMODE_ERASER, + DRAWINGMODE_STRAIGHTLINE, + DRAWINGMODE_ARROW, + DRAWINGMODE_RECTANGLE, + DRAWINGMODE_ELLIPSE +} DrawingMode; + +typedef enum _DrawingState { + DRAWINGSTATE_START, + DRAWINGSTATE_UPDATE, + DRAWINGSTATE_END +} DrawingState; + + +#endif // APPGLOBALS_H diff --git a/cbc/client/collaboration_client.pro b/cbc/client/collaboration_client.pro new file mode 100644 index 0000000..74e74fd --- /dev/null +++ b/cbc/client/collaboration_client.pro @@ -0,0 +1,113 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2011-04-16T14:28:32 +# +#------------------------------------------------- + +QT += core gui network + +TARGET = watchntouch_collaboration +TEMPLATE = app + +COMMON_SOURCES += ../common/wtmessage.cpp \ + ../common/wtloginmessage.cpp \ + ../common/wtsessionlistrequest.cpp \ + ../common/messagetransceiver.cpp \ + ../common/wtlogoutrequest.cpp \ + ../common/wtpicturerequest.cpp \ + ../common/wtpictureresponse.cpp \ + ../common/wtsessioncreaterequest.cpp \ + ../common/wtsessioncreateresponse.cpp \ + ../common/wtsessionjoinrequest.cpp \ + ../common/wtsessionjoinresponse.cpp \ + ../common/wtsessionleaverequest.cpp \ + ../common/wtsessionleaveresponse.cpp \ + ../common/wtsessionlistresponse.cpp \ + ../common/wtsessionmemberupdate.cpp \ + ../common/wtupdatedrawing.cpp \ + ../common/wtupdatedrawingserver.cpp \ + ../common/wtwritepermissionrequest.cpp \ + ../common/wtwritepermissionstatus.cpp \ + ../common/wtloginresponse.cpp \ + ../common/protocolhandler.cpp \ + ../common/wtpeerhandshake.cpp \ + ../common/collaborationsession.cpp + +CWIDGET_SOURCES += ../collaborative_drawing_widget/basedrawingwidget.cpp \ + ../collaborative_drawing_widget/drawingdata.cpp \ + ../collaborative_drawing_widget/collaborativedrawingwidget.cpp \ + ../collaborative_drawing_widget/drawingaction.cpp + +COMMON_HEADERS += ../common/wtmessage.h \ + ../common/wtloginmessage.h \ + ../common/wtsessionlistrequest.h \ + ../common/messagetransceiver.h \ + ../common/wtlogoutrequest.h \ + ../common/wtpicturerequest.h \ + ../common/wtpictureresponse.h \ + ../common/wtsessioncreaterequest.h \ + ../common/wtsessioncreateresponse.h \ + ../common/wtsessionjoinrequest.h \ + ../common/wtsessionjoinresponse.h \ + ../common/wtsessionleaverequest.h \ + ../common/wtsessionleaveresponse.h \ + ../common/wtsessionlistresponse.h \ + ../common/wtsessionmemberupdate.h \ + ../common/wtupdatedrawing.h \ + ../common/wtupdatedrawingserver.h \ + ../common/wtwritepermissionrequest.h \ + ../common/wtwritepermissionstatus.h \ + ../common/wtloginresponse.h \ + ../common/protocolhandler.h \ + ../common/wtpeerhandshake.h \ + ../common/collaborationsession.h + +CWIDGET_HEADERS += ../collaborative_drawing_widget/basedrawingwidget.h \ + ../collaborative_drawing_widget/drawingdata.h \ + ../collaborative_drawing_widget/collaborativedrawingwidget.h \ + ../collaborative_drawing_widget/drawingaction.h \ + ../collaborative_drawing_widget/appglobals.h + +INCLUDEPATH += . \ + ../common \ + ../collaborative_drawing_widget + +SOURCES += main.cpp\ + mainwindow.cpp \ + collaborationclient.cpp \ + sessionjoindialog.cpp \ + $$COMMON_SOURCES \ + $$CWIDGET_SOURCES \ + +HEADERS += mainwindow.h \ + collaborationclient.h \ + sessionjoindialog.h \ + $$COMMON_HEADERS \ + $$CWIDGET_HEADERS + +FORMS += mainwindow.ui \ + sessionjoindialog.ui + +OTHER_FILES += \ + android/res/drawable-mdpi/icon.png \ + android/res/drawable-ldpi/icon.png \ + android/res/drawable-hdpi/icon.png \ + android/res/values/libs.xml \ + android/res/values/strings.xml \ + android/AndroidManifest.xml \ + android/src/eu/licentia/necessitas/industrius/QtSurface.java \ + android/src/eu/licentia/necessitas/industrius/QtApplication.java \ + android/src/eu/licentia/necessitas/industrius/QtActivity.java \ + android/src/eu/licentia/necessitas/ministro/IMinistroCallback.aidl \ + android/src/eu/licentia/necessitas/ministro/IMinistro.aidl \ + android/res/drawable-mdpi/icon.png \ + android/res/drawable-ldpi/icon.png \ + android/res/drawable-hdpi/icon.png \ + android/res/values/libs.xml \ + android/res/values/strings.xml \ + android/AndroidManifest.xml \ + android/src/eu/licentia/necessitas/industrius/QtSurface.java \ + android/src/eu/licentia/necessitas/industrius/QtApplication.java \ + android/src/eu/licentia/necessitas/industrius/QtActivity.java \ + android/src/eu/licentia/necessitas/ministro/IMinistroCallback.aidl \ + android/src/eu/licentia/necessitas/ministro/IMinistro.aidl diff --git a/cbc/client/collaborationclient.cpp b/cbc/client/collaborationclient.cpp new file mode 100644 index 0000000..f6cba29 --- /dev/null +++ b/cbc/client/collaborationclient.cpp @@ -0,0 +1,447 @@ +#include "collaborationclient.h" + +CollaborationClient::CollaborationClient(QObject *parent) : + QObject(parent), + m_protocolHandler(NULL) +{ + + // set up the broadcast listener to autodiscover the server + serviceBroadcastReceiver.bind(SERVICE_BROADCAST_PORT, QUdpSocket::ShareAddress); + connect(&serviceBroadcastReceiver, SIGNAL(readyRead()), this, SLOT(gotServiceBroadcast())); +} + +void CollaborationClient::setProtocolHandler(ProtocolHandler * newProtocolHandler) +{ + if (m_protocolHandler) + { + disconnect(this); + disconnect(m_protocolHandler); + } + + connect(this, SIGNAL(sendLoginRequest(QString)), newProtocolHandler, SLOT(sendLoginRequest(QString))); + connect(this, SIGNAL(sendLogoutRequest(QString)), newProtocolHandler, SLOT(sendLogoutRequest(QString))); + connect(this, SIGNAL(sendPeerHandshake(QString,QString)), newProtocolHandler, SLOT(sendPeerHandshake(QString,QString))); + connect(this, SIGNAL(sendPictureRequest(QString,QString)), newProtocolHandler, SLOT(sendPictureRequest(QString,QString))); + connect(this, SIGNAL(sendSessionJoinRequest(QString,QString,QString)), newProtocolHandler, SLOT(sendSessionJoinRequest(QString,QString,QString))); + connect(this, SIGNAL(sendSessionLeaveRequest(QString,QString)), newProtocolHandler, SLOT(sendSessionLeaveRequest(QString,QString))); + connect(this, SIGNAL(sendSessionListRequest(QString)), newProtocolHandler, SLOT(sendSessionListRequest(QString))); + connect(this, SIGNAL(sendUpdateDrawing(QString,QString,QByteArray)), newProtocolHandler, SLOT(sendUpdateDrawing(QString,QString,QByteArray))); + connect(this, SIGNAL(sendUpdateDrawingServer(QString,QString,QByteArray)), newProtocolHandler, SLOT(sendUpdateDrawingServer(QString,QString,QByteArray))); + connect(this, SIGNAL(sendWritePermissionRequest(QString)), newProtocolHandler, SLOT(sendWritePermissionRequest(QString))); + connect(this, SIGNAL(sendSessionCreateRequest(QString,QString,QString)), newProtocolHandler, SLOT(sendSessionCreateRequest(QString,QString,QString))); + + connect(newProtocolHandler, SIGNAL(receivedLoginResponse(QString,QChar,QString)), this, SLOT(receivedLoginResponse(QString,QChar,QString))); + connect(newProtocolHandler, SIGNAL(receivedPeerHandshake(QString,QString)), this, SLOT(receivedPeerHandshake(QString,QString))); + connect(newProtocolHandler, SIGNAL(receivedPictureResponse(QString,QString,QByteArray)), this, SLOT(receivedPictureResponse(QString,QString,QByteArray))); + connect(newProtocolHandler, SIGNAL(receivedSessionJoinResponse(QString,QString,char,uint,QHash)), this, SLOT(receivedSessionJoinResponse(QString,QString,char,uint,QHash))); + connect(newProtocolHandler, SIGNAL(receivedSessionLeaveResponse(QString,QString,char)), this, SLOT(receivedSessionLeaveResponse(QString,QString,char))); + connect(newProtocolHandler, SIGNAL(receivedSessionListResponse(QString,QStringList)), this, SLOT(receivedSessionListResponse(QString,QStringList))); + connect(newProtocolHandler, SIGNAL(receivedSessionMemberUpdate(QString,QString,char,QString)), this, SLOT(receivedSessionMemberUpdate(QString,QString,char,QString))); + connect(newProtocolHandler, SIGNAL(receivedUpdateDrawing(QString,QString,QByteArray)), this, SLOT(receivedUpdateDrawing(QString,QString,QByteArray))); + connect(newProtocolHandler, SIGNAL(receivedWritePermissionStatus(QString,QChar)), this, SLOT(receivedWritePermissionStatus(QString,QChar))); + connect(newProtocolHandler, SIGNAL(receivedSessionCreateResponse(QString,QString,QChar,QString)), this, SLOT(receivedSessionCreateResponse(QString,QString,QChar,QString))); + m_protocolHandler = newProtocolHandler; +} + +ProtocolHandler * CollaborationClient::getProtocolHandler() +{ + return m_protocolHandler; +} + + +void CollaborationClient::receivedLoginResponse(QString userName, QChar result, QString infoMsg) +{ + //TODO show the infoMsg + qWarning() << "Login Response message: " << infoMsg; + + if (result == 0) + { + //TODO show error message + qWarning() << "Login is unsuccessful"; + emit loginResult(false, infoMsg); + } + else + { + qWarning() << "Login is successful"; + emit loginResult(true, infoMsg); + emit sendSessionListRequest(m_serverName); + } +} + +void CollaborationClient::receivedPeerHandshake(QString userName, QString sessionName) +{ + //Connect to the user that has just said hello. + + //If this client is the one which joins to a session + // - it is expecting to get peerHandshake messages from all the session members + if (m_currentState[sessionName] == JOIN_SESSION_STATE) + { + qWarning() << "Peer " << userName << " is acknowledged"; + m_collaborationSessions[sessionName]->acknowledgePeer(userName); + //if all members shaked hands, then request picture from server + if (m_collaborationSessions[sessionName]->isAllAcknowledged()) + { + qWarning() << "All peers are acknowledged."; + m_currentState[sessionName] = JOIN_SESSION_PEERHANDHAKE_COMPLETED; + emit sendPictureRequest(m_serverName,sessionName); + } + } + else if (m_currentState[sessionName] == MEMBER_UPDATE_JOIN_BEGIN_RECEIVED) + { + qWarning() << "Got handshake from new peer : " << userName; + //Add user to the session member list for this client + m_collaborationSessions[sessionName]->addSessionParticipant(userName, m_collaborationSessions[sessionName]->getSessionPassword(), 0); + //Shake hands with the client + emit sendPeerHandshake(userName, sessionName); + } + else + { + qWarning() << "Received unexpected peer handshake, adding to pending list"; + m_pendingHandshakes.insert(userName, sessionName); + } +} + +void CollaborationClient::receivedPictureResponse(QString userName, QString sessionName, QByteArray picData) +{ + //Show the pictureData on screen + QPicture sessState; + sessState.setData(picData.constData(), picData.size()); + //Set the current picture data of the session for this client + m_collaborationSessions[sessionName]->addDrawingStep(sessState); + //TODO this will be emitted in collaboration session + emit drawingArrived(sessionName, picData, true); + //Joining to the session has been completed. + m_currentState[sessionName] = JOIN_SESSION_COMPLETED; + emit sessionJoinResult(sessionName, 1, m_collaborationSessions[sessionName]->getSessionParticipants()); +} + +void CollaborationClient::receivedSessionJoinResponse(QString userName, QString sessionName, char result, unsigned int userCount, QHash users) +{ + if (result == 0) + { + //Session join request was unsuccessful + //TODO show error message. + qWarning() << "Session Join by " << userName << " was unsuccessful."; + emit sessionJoinResult(sessionName, result, users); + } + else + { + //Session join was successful. + qWarning() << "Session Join by " << userName << " was successful"; + + //TODO to be removed! + this->activeSession = sessionName; + + //Remove the user from the list of users not to send data to the user itself + users.remove(m_protocolHandler->getUserName()); + m_currentState.insert(sessionName, JOIN_SESSION_STATE); + m_drawingBuffer.insert(sessionName, new QVector); + + //Create a new session + CollaborationSession *collaborationSession = new CollaborationSession; + collaborationSession->setSessionName(sessionName); + + collaborationSession->setSessionParticipants(users); + + //Map it with its sessionName + m_collaborationSessions.insert(sessionName, collaborationSession); + + // the sole user in the session is this user, so no handshakes needed + // we can directly send the current state request to the server + if (users.size() == 0) + { + emit sendPictureRequest(m_serverName, sessionName); + return; + } + + //Add all members to the list that is going to which + // - this client will establish TCP connections + QHash::iterator itr; + for (itr = users.begin(); itr != users.end(); itr++) + { + //Send handshake messages to the users in the session + if (itr.key() != m_protocolHandler->getUserName()) { + // check if this peer is the server user, we already have a connection to that + if(itr.key() != m_serverName) + m_protocolHandler->addUserMapping(itr.key(), QHostAddress(itr.value()).toString()); + qWarning() << "Peer handshake has been sent to : " << itr.key(); + emit sendPeerHandshake(itr.key(), sessionName); + } + } + } +} + +void CollaborationClient::receivedSessionLeaveResponse(QString userName, QString sessionName, char result) +{ + if (result == 0) + { + qWarning() << "Session Leave by " << userName << " was unsuccesful"; + } + else + { + qWarning() << "Session Leave by " << userName << " was succesful"; + //Remove the participants from the client's collaboration session + m_collaborationSessions[sessionName]->getSessionParticipants().clear(); + //Remove the name of the session as the client has left it + m_collaborationSessions[sessionName]->setSessionName(""); + //Remove the password of the session as the client has left the session + m_collaborationSessions[sessionName]->setSessionPassword(""); + } +} + +void CollaborationClient::receivedSessionListResponse(QString userName, QStringList sessionList) +{ + //List the sessions so that a client can decide which one to join + int size = sessionList.size(); + qWarning() << "Current sessions:\n"; + + //Check if the client has already a session list + // - if yes, clear it and get the new list + if (!m_sessionList.empty()) + m_sessionList.clear(); + + m_sessionList.append(sessionList); + + //Print the session list to stdout + for (int i = 0; i < size; i++) + { + qWarning() << i << sessionList.at(i) << "\n"; + } + + emit sessionListAvailable(sessionList); +} + +void CollaborationClient::receivedSessionMemberUpdate(QString userName, QString sessionName, char updateType, QString user) +{ + + //The users in the list "users" have started to join to the session + if (updateType == UPDATE_SESSION_JOIN_BEGIN) + { + qWarning() << "New member wants to join : " << user; + m_currentState[sessionName] = MEMBER_UPDATE_JOIN_BEGIN_RECEIVED; + //The client here finishes what it is sending and stops sending + // - All what it draws is stored in a buffer + } + //The users in the list "users" have completely joined the session + else if (updateType == UPDATE_SESSION_JOIN_END) + { + m_currentState[sessionName] = MEMBER_UPDATE_JOIN_END_RECEIVED; + //Client resumes sending as the new participant is ready + sendBufferedData(sessionName, user); + + } + //The users in the list "users" have left the session + else if (updateType == UPDATE_SESSION_LEAVE) + { + //Delete the user from the list of participants of the session + QHash *participants = &(m_collaborationSessions[sessionName]->getSessionParticipants()); + QHash::iterator iter; + for (iter = participants->begin(); iter != participants->end(); iter++) + { + if (iter.key() == user) + { + participants->erase(iter); + break; + } + } + } +} + +void CollaborationClient::receivedUpdateDrawing(QString userName, QString sessionName, QByteArray picData) +{ + //Update the state with the picData + QPicture drawingStep; + drawingStep.setData(picData.constData(), picData.size()); + m_collaborationSessions[sessionName]->addDrawingStep(drawingStep); + + qWarning() << "Picdata geldi of size : " << picData.size(); + + //TODO this will be emitted in collaboration session + emit drawingArrived(sessionName, picData, false); +} + +void CollaborationClient::receivedWritePermissionStatus(QString userName, QChar status) +{ + //TODO if status 0 + //TODO - cannot draw to classroom session + //TODO else + //TODO - can draw to classroom session + //TODO so, change a state variable to show the permission accordingly +} + +void CollaborationClient::receivedSessionCreateResponse(QString userName, QString sessionName, QChar result, QString password) +{ + //Check if the operation was successful + if (result == 0) + { + //Operation was unsuccessful + qWarning() << "Creation of session with session name : " << sessionName << " failed"; + return; + } + + qWarning() << "Session " << sessionName << " has been created"; + + //As the creator of the session is assumed to automatically join to that session + // it sends automatically session join request message + qWarning() << "Join with the password : " << password; + emit sendSessionJoinRequest(m_serverName, sessionName, password); +} + + +void CollaborationClient::createSession(QString sessionName, QString password) +{ + //Send session create message with encrypted password + emit sendSessionCreateRequest(m_serverName, sessionName, password); +} + +void CollaborationClient::gotServiceBroadcast() +{ + // receive collaborationserver service broadcasts over UDP + // note that this is just for autodiscovery, we don't automatically connect to any + // of the discovered servers + + QByteArray broadcastPackage; + QString packageHeader; + QString serverUserName; + QHostAddress serverAddress; + QNetworkInterface networkInterface; + QList allInterfaces = networkInterface.allInterfaces(); + QList allAddresses; + + for (int i = 0; i < allInterfaces.size(); i++) + { + allAddresses.append(allInterfaces[i].addressEntries()); + } + + while(serviceBroadcastReceiver.hasPendingDatagrams()) + { + broadcastPackage.resize(serviceBroadcastReceiver.pendingDatagramSize()); + serviceBroadcastReceiver.readDatagram(broadcastPackage.data(), broadcastPackage.size()); + QDataStream packageStream(&broadcastPackage, QIODevice::ReadWrite); + packageStream >> packageHeader; + + if (packageHeader == "WTCOLSRV") + { + packageStream >> serverUserName; + // TODO we are using only the first entry of the multiple server addresses + // modify this part to make use of all existing entries + packageStream >> serverAddress; + + for(int i = 0; i < allAddresses.size(); i++) + { + if (allAddresses[i].ip().protocol() != QAbstractSocket::IPv4Protocol) + continue; + + if (((allAddresses[i].ip().toIPv4Address() & allAddresses[i].netmask().toIPv4Address()) == + (serverAddress.toIPv4Address() & allAddresses[i].netmask().toIPv4Address()))) + { + //qWarning() << allAddresses[i].ip() << allAddresses[i].netmask(); + //qWarning() << serverAddress << "!"; + emit foundCollaborationServer(serverAddress, serverUserName); + break; + } + } + } + } +} + + +void CollaborationClient::loginToServer(QHostAddress serverAddress, QString serverName, QString userName) +{ + // TODO check if mapping for server already exists + m_protocolHandler->addUserMapping(serverName, serverAddress.toString()); + m_serverName = serverName; + // set the origin (source) user name + m_protocolHandler->setUserName(userName); + // emit login request + emit sendLoginRequest(m_serverName); +} + +void CollaborationClient::refreshSessionList() +{ + emit sendSessionListRequest(m_serverName); +} + +void CollaborationClient::joinSession(QString sessionName, QString password) +{ + emit sendSessionJoinRequest(m_serverName, sessionName, QCryptographicHash::hash(password.toAscii(), QCryptographicHash::Md5)); +} + +void CollaborationClient::sendDrawing(QString sessionName, QByteArray picData) +{ + + //Send the picData to all the peers in the session + //Check if there is a member joining, if so, store what is drawn in a buffer + //Here it is checked if the buffer is empty, as if it is not, there are still some data being sent + // - and do not send before it finishes, in order not to mix the order up. Instead, store it in the + // - buffer too + if (m_currentState[sessionName] != MEMBER_UPDATE_JOIN_BEGIN_RECEIVED && m_drawingBuffer[sessionName]->isEmpty()) + { + QHash::iterator itr; + QHash *participants = &(m_collaborationSessions[sessionName]->getSessionParticipants()); + + for (itr = participants->begin(); itr != participants->end(); itr++) + { + //Don't send the picData to the drawer itself + if (itr.key() == m_protocolHandler->getUserName()) continue; + + qWarning() << "sent to the user : " << itr.key(); + emit sendUpdateDrawing(itr.key(), sessionName, picData); + } + //Send picData to the server + emit sendUpdateDrawingServer(m_serverName, sessionName, picData); + } + else + { + //Store the picData in order to send it later + m_drawingBuffer[sessionName]->push_back(picData); + } +} + +void CollaborationClient::leaveSession(QString sessionName) +{ + emit sendSessionLeaveRequest(m_serverName, sessionName); +} + +QString CollaborationClient::getActiveSession() +{ + return this->activeSession; +} + +void CollaborationClient::sendBufferedData(QString sessionName, QString user) +{ + QHash::iterator itr; + QHash *participants = &(m_collaborationSessions[sessionName]->getSessionParticipants()); + + QVector *drawingBuffer = m_drawingBuffer[sessionName]; + + //Send all the drawing data up to now to the users + QVector::iterator iterBuffer = drawingBuffer->begin(); + while(iterBuffer != drawingBuffer->end()) + { + for (itr = participants->begin(); itr != participants->end(); itr++) + { + //Don't send the picData to the drawer itself + if (itr.key() == m_protocolHandler->getUserName()) continue; + + qWarning() << "sent to the user : " << itr.key(); + emit sendUpdateDrawing(itr.key(), sessionName, *iterBuffer); + } + //Send picData to the server + emit sendUpdateDrawingServer(m_serverName, sessionName, *iterBuffer); + drawingBuffer->erase(iterBuffer); + } + + //If a user was joining, let it know that you finished sending finally. + if (m_currentState[sessionName] == MEMBER_UPDATE_JOIN_BEGIN_RECEIVED) + { + // check if we already received a handshake for this user and session + if(m_pendingHandshakes.contains(user) && m_pendingHandshakes.value(user, "") == sessionName) { + // acknowledge peer + receivedPeerHandshake(user, sessionName); + // clear pending ack + m_pendingHandshakes.remove(user); + } + } +} + diff --git a/cbc/client/collaborationclient.h b/cbc/client/collaborationclient.h new file mode 100644 index 0000000..b1b70c7 --- /dev/null +++ b/cbc/client/collaborationclient.h @@ -0,0 +1,100 @@ +#ifndef COLLABORATIONCLIENT_H +#define COLLABORATIONCLIENT_H + +#include +#include +#include + +#include +#include + +#define SERVICE_BROADCAST_PORT 45455 + +#define JOIN_SESSION_STATE 1 +#define JOIN_SESSION_PEERHANDHAKE_COMPLETED 2 +#define JOIN_SESSION_COMPLETED 3 +#define MEMBER_UPDATE_JOIN_BEGIN_RECEIVED 4 +#define MEMBER_UPDATE_JOIN_END_RECEIVED 5 + +class CollaborationClient : public QObject +{ + Q_OBJECT +public: + explicit CollaborationClient(QObject *parent = 0); + + void setProtocolHandler(ProtocolHandler * newProtocolHandler); + ProtocolHandler * getProtocolHandler(); + + void loginToServer(QHostAddress serverAddress, QString serverName, QString userName); + void refreshSessionList(); + void joinSession(QString sessionName, QString password); + void sendDrawing(QString sessionName, QByteArray picData); + void createSession(QString sessionName, QString password); + void leaveSession(QString sessionName); + + //TODO To be removed + QString getActiveSession(); + + void sendBufferedData(QString sessionName, QString user); + +private: + QString m_serverName; + QStringList m_userList; + QStringList m_sessionList; + ProtocolHandler *m_protocolHandler; + QHash m_collaborationSessions; + QHash m_pendingHandshakes; + + QUdpSocket serviceBroadcastReceiver; + + //State of the client in a session + QHash m_currentState; + + //Number of peers who have acknowledged already + QHash m_ackedPeers; + + //TODO to be removed + QString activeSession; + + //While a user is joining, the client keeps its data in here + QHash *> m_drawingBuffer; + +signals: + // external signals that are meant to be used for the user interface + void foundCollaborationServer(QHostAddress serverAddress, QString serverUserName); + void loginResult(bool result, QString infoMsg); + void sessionListAvailable(QStringList newSessionList); + void sessionJoinResult(QString sessionName, QChar result, QHash users); + void drawingArrived(QString sessionName, QByteArray picData, bool initialState); + + // internal signals that will be connected to the ProtocolHandler + void sendLoginRequest(QString destUserName); + void sendLogoutRequest(QString destUserName); + void sendPeerHandshake(QString destUserName, QString sessionName); + void sendPictureRequest(QString destUserName, QString sessionName); + void sendSessionJoinRequest(QString destUserName, QString sessionName, QString password); + void sendSessionLeaveRequest(QString destUserName, QString sessionName); + void sendSessionListRequest(QString destUserName); + void sendUpdateDrawing(QString destUserName, QString sessionName, QByteArray picData); + void sendUpdateDrawingServer(QString destUserName, QString sessionName, QByteArray picData); + void sendWritePermissionRequest(QString destUserName); + void sendSessionCreateRequest(QString destUserName, QString sessionName, QString password); + +private slots: + void receivedLoginResponse(QString userName, QChar result, QString infoMsg); + void receivedPeerHandshake(QString userName, QString sessionName); + void receivedPictureResponse(QString userName, QString sessionName, QByteArray picData); + void receivedSessionJoinResponse(QString userName, QString sessionName, char result, unsigned int userCount, QHash users); + void receivedSessionLeaveResponse(QString userName, QString sessionName, char result); + void receivedSessionListResponse(QString userName, QStringList sessionList); + void receivedSessionMemberUpdate(QString userName, QString sessionName, char updateType, QString user); + void receivedUpdateDrawing(QString userName, QString sessionName, QByteArray picData); + void receivedWritePermissionStatus(QString userName, QChar status); + void receivedSessionCreateResponse(QString userName, QString sessionName, QChar result, QString password); + +private slots: + void gotServiceBroadcast(); + +}; + +#endif // COLLABORATIONCLIENT_H diff --git a/cbc/client/main.cpp b/cbc/client/main.cpp new file mode 100644 index 0000000..5349f17 --- /dev/null +++ b/cbc/client/main.cpp @@ -0,0 +1,16 @@ +#include +#include "mainwindow.h" +#include +#include +#include + +#include "collaborationsession.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/cbc/client/mainwindow.cpp b/cbc/client/mainwindow.cpp new file mode 100644 index 0000000..ea4868e --- /dev/null +++ b/cbc/client/mainwindow.cpp @@ -0,0 +1,122 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include "drawingdata.h" + +#include +#include +#include +#include "collaborativedrawingwidget.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); + DrawingData *drawingData = new DrawingData(this); + ui->graphicsView->setDrawingData(drawingData); + connect(ui->actionUndo,SIGNAL(triggered()),ui->graphicsView->getDrawingData()->getUndoStack(), SLOT(undo())); + connect(ui->actionRedo,SIGNAL(triggered()),ui->graphicsView->getDrawingData()->getUndoStack(), SLOT(redo())); + + mt = new MessageTransceiver(); + ph = new ProtocolHandler(); + ph->setMessageTransceiver(mt); + client = new CollaborationClient(); + client->setProtocolHandler(ph); + + mt->start(); + + connect(client, SIGNAL(sessionJoinResult(QString,QChar,QHash)), this, SLOT(sessionJoinResult(QString,QChar,QHash))); + connect(ui->graphicsView, SIGNAL(drawingCommited(QString,QPicture)), this, SLOT(drawingCommitted(QString,QPicture))); + connect(client, SIGNAL(drawingArrived(QString,QByteArray,bool)), ui->graphicsView, SLOT(drawingArrived(QString,QByteArray,bool))); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::on_actionRedPen_triggered() +{ + QPen current = ui->graphicsView->getDrawingPen(); + current.setColor(Qt::red); + ui->graphicsView->setDrawingPen(current); + ui->graphicsView->setDrawingMode(DRAWINGMODE_FREEHAND); +} + +void MainWindow::on_actionBlackPen_triggered() +{ + QPen current = ui->graphicsView->getDrawingPen(); + current.setColor(Qt::black); + ui->graphicsView->setDrawingPen(current); + ui->graphicsView->setDrawingMode(DRAWINGMODE_FREEHAND); +} + +void MainWindow::on_actionBluePen_triggered() +{ + QPen current = ui->graphicsView->getDrawingPen(); + current.setColor(Qt::blue); + ui->graphicsView->setDrawingPen(current); + ui->graphicsView->setDrawingMode(DRAWINGMODE_FREEHAND); +} + +void MainWindow::on_actionPenWidthInc_triggered() +{ + QPen current = ui->graphicsView->getDrawingPen(); + current.setWidth(current.width()+1); + ui->graphicsView->setDrawingPen(current); +} + +void MainWindow::on_actionPenWidthDec_triggered() +{ + QPen current = ui->graphicsView->getDrawingPen(); + current.setWidth(current.width()-1); + ui->graphicsView->setDrawingPen(current); +} + +void MainWindow::on_actionConnect_triggered() +{ + SessionJoinDialog * dlg = new SessionJoinDialog(client, this); + dlg->show(); +} + +void MainWindow::on_actionEraser_triggered() +{ + ui->graphicsView->setDrawingMode(DRAWINGMODE_ERASER); +} + +void MainWindow::sessionJoinResult(QString sessionName, QChar result, QHash users) +{ + qWarning() << "Session join result: " << sessionName << " : " << result << " : " << users.count(); +} + +void MainWindow::drawingCommitted(QString sessionName, QPicture pictureData) +{ + QByteArray picData(pictureData.data(), pictureData.size()); + client->sendDrawing(sessionName, picData); +} + +void MainWindow::on_actionStraightLine_triggered() +{ + ui->graphicsView->setDrawingMode(DRAWINGMODE_STRAIGHTLINE); +} + +void MainWindow::on_actionRectangle_triggered() +{ + ui->graphicsView->setDrawingMode(DRAWINGMODE_RECTANGLE); +} + +void MainWindow::on_actionFreehand_triggered() +{ + ui->graphicsView->setDrawingMode(DRAWINGMODE_FREEHAND); +} + +void MainWindow::on_actionClear_triggered() +{ + ui->graphicsView->clear(); +} + +void MainWindow::on_actionLeave_triggered() +{ + client->leaveSession(client->getActiveSession()); +} diff --git a/cbc/client/mainwindow.h b/cbc/client/mainwindow.h new file mode 100644 index 0000000..be08e82 --- /dev/null +++ b/cbc/client/mainwindow.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include "collaborationclient.h" +#include "sessionjoindialog.h" +namespace Ui { + class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private: + Ui::MainWindow *ui; + CollaborationClient *client; + ProtocolHandler *ph; + MessageTransceiver *mt; + +private slots: + void on_actionLeave_triggered(); + void on_actionConnect_triggered(); + void on_actionPenWidthDec_triggered(); + void on_actionPenWidthInc_triggered(); + void on_actionBluePen_triggered(); + void on_actionBlackPen_triggered(); + void on_actionRedPen_triggered(); + void on_actionEraser_triggered(); + void sessionJoinResult(QString sessionName, QChar result, QHash users); + void drawingCommitted(QString sessionName, QPicture pictureData); + void on_actionStraightLine_triggered(); + void on_actionRectangle_triggered(); + void on_actionFreehand_triggered(); + void on_actionClear_triggered(); +}; + +#endif // MAINWINDOW_H diff --git a/cbc/client/mainwindow.ui b/cbc/client/mainwindow.ui new file mode 100644 index 0000000..be46d0b --- /dev/null +++ b/cbc/client/mainwindow.ui @@ -0,0 +1,185 @@ + + + MainWindow + + + + 0 + 0 + 797 + 480 + + + + Watch and Touch Collaborative Drawing Tests + + + + + + + false + + + + + + + + TopToolBarArea + + + false + + + + + + + + + + + + + + + + + + Save + + + Save + + + Ctrl+S + + + + + Load + + + Load + + + Ctrl+O + + + + + Undo + + + Ctrl+Z + + + + + Redo + + + Ctrl+Shift+Z + + + + + Red + + + Red + + + + + Black + + + Black + + + + + Blue + + + Blue + + + + + PenW++ + + + + + PenW-- + + + + + Freehand + + + + + Rectangle + + + + + Straight Line + + + + + Ellipse + + + + + Listen + + + Listen for incoming connections + + + + + Connect + + + + + Eraser + + + + + Clear + + + + + Leave + + + Leave Session + + + true + + + + + + + CollaborativeDrawingWidget + QGraphicsView +
collaborativedrawingwidget.h
+
+
+ + +
diff --git a/cbc/client/sessionjoindialog.cpp b/cbc/client/sessionjoindialog.cpp new file mode 100644 index 0000000..1bd0b43 --- /dev/null +++ b/cbc/client/sessionjoindialog.cpp @@ -0,0 +1,84 @@ +#include "sessionjoindialog.h" +#include "ui_sessionjoindialog.h" + +#include + +SessionJoinDialog::SessionJoinDialog(CollaborationClient * client, QWidget *parent ) : + QDialog(parent), + ui(new Ui::SessionJoinDialog) +{ + ui->setupUi(this); + m_client = client; + + connect(m_client, SIGNAL(foundCollaborationServer(QHostAddress, QString)), this, SLOT(foundServer(QHostAddress, QString))); + connect(m_client, SIGNAL(loginResult(bool,QString)), this, SLOT(loginResult(bool,QString))); + connect(m_client, SIGNAL(sessionListAvailable(QStringList)), this, SLOT(gotSessionList(QStringList))); +} + +void SessionJoinDialog::foundServer(QHostAddress addr, QString serverName) +{ + ui->serverName->setText(serverName); + ui->serverIP->setText(addr.toString()); +} + +SessionJoinDialog::~SessionJoinDialog() +{ + delete ui; +} + +void SessionJoinDialog::on_refreshList_clicked() +{ + m_client->refreshSessionList(); +} + +void SessionJoinDialog::on_pushButton_clicked() +{ + ui->connectionStatus->setText("Connecting..."); + m_client->loginToServer(QHostAddress(ui->serverIP->text()), ui->serverName->text(), ui->userName->text()); +} + +void SessionJoinDialog::on_sessionList_doubleClicked(QModelIndex index) +{ + +} + +void SessionJoinDialog::on_buttonBox_accepted() +{ + +} + +void SessionJoinDialog::on_buttonBox_rejected() +{ + this->hide(); + this->deleteLater(); +} + +void SessionJoinDialog::loginResult(bool result, QString infoMsg) +{ + QMessageBox m; + m.setText(infoMsg); + m.exec(); + + if(result) + ui->connectionStatus->setText("Connected"); + else + ui->connectionStatus->setText("Error!"); +} + +void SessionJoinDialog::gotSessionList(QStringList sessionList) +{ + ui->sessionList->clear(); + ui->sessionList->addItems(sessionList); +} + +void SessionJoinDialog::on_joinSession_clicked() +{ + m_client->joinSession(ui->sessionList->currentItem()->text(), ui->password->text()); + this->hide(); + this->deleteLater(); +} + +void SessionJoinDialog::on_btn_createSession_clicked() +{ + m_client->createSession("sessozz","1234"); +} diff --git a/cbc/client/sessionjoindialog.h b/cbc/client/sessionjoindialog.h new file mode 100644 index 0000000..7d61750 --- /dev/null +++ b/cbc/client/sessionjoindialog.h @@ -0,0 +1,36 @@ +#ifndef SESSIONJOINDIALOG_H +#define SESSIONJOINDIALOG_H + +#include +#include "collaborationclient.h" + +namespace Ui { + class SessionJoinDialog; +} + +class SessionJoinDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SessionJoinDialog(CollaborationClient * client, QWidget *parent = 0); + ~SessionJoinDialog(); + +private: + Ui::SessionJoinDialog *ui; + CollaborationClient *m_client; + +private slots: + void on_btn_createSession_clicked(); + void on_joinSession_clicked(); + void on_buttonBox_rejected(); + void on_buttonBox_accepted(); + void on_sessionList_doubleClicked(QModelIndex index); + void on_pushButton_clicked(); + void on_refreshList_clicked(); + void foundServer(QHostAddress addr, QString serverName); + void loginResult(bool result, QString infoMsg); + void gotSessionList(QStringList sessionList); +}; + +#endif // SESSIONJOINDIALOG_H diff --git a/cbc/client/sessionjoindialog.ui b/cbc/client/sessionjoindialog.ui new file mode 100644 index 0000000..3d9e110 --- /dev/null +++ b/cbc/client/sessionjoindialog.ui @@ -0,0 +1,160 @@ + + + SessionJoinDialog + + + + 0 + 0 + 483 + 412 + + + + Dialog + + + + + + Login to Collaboration Server + + + + + + (server name) + + + + + + + Searching... + + + Qt::AlignCenter + + + + + + + Your username: + + + + + + + + + + Connection status: + + + + + + + Not connected + + + Qt::AlignCenter + + + + + + + Log in + + + + + + + + + + Sessions + + + + + + + + + + + + Refresh list + + + + + + + Join selected session + + + + + + + Create Session + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SessionJoinDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SessionJoinDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/cbc/collaborative_drawing_widget/appglobals.h b/cbc/collaborative_drawing_widget/appglobals.h new file mode 100644 index 0000000..aaef23d --- /dev/null +++ b/cbc/collaborative_drawing_widget/appglobals.h @@ -0,0 +1,65 @@ +#ifndef APPGLOBALS_H +#define APPGLOBALS_H + +#include +#include +#include + +typedef enum _ContentType { + CONTENTTYPE_UNDEFINED, + CONTENTTYPE_PRESENTATION, + CONTENTTYPE_VIDEO, + CONTENTTYPE_WEBPAGE +} ContentType; + +// constants and/or shortcuts +#define SCREEN_WIDTH qApp->desktop()->width() +#define SCREEN_HEIGHT qApp->desktop()->height() + +#define ERASER_SIZE 5 + +#define MOUSE_PRESSED 0 +#define MOUSE_MOVE 1 +#define MOUSE_RELEASED 2 + +// W&T specific directories +#define CONFIG_DIR qApp->applicationDirPath() + "/" + QString("configuration") +#define CACHE_DIR qApp->applicationDirPath() + "/" + QString("cache") +#define TOOLS_DIR qApp->applicationDirPath() + "/" + QString("tools") + +#define ANNOTATION_DIR qApp->applicationDirPath() + "/" + QString("annotations") +#define IMAGE_DIR qApp->applicationDirPath() + "/" + QString("images"); // for rendered images. +#define SKETCH_DIR qApp->applicationDirPath() + "/" + QString("sketch"); // for sketch. +#define SCREENSHOT_DIR qApp->applicationDirPath() + "/" + QString("screenshot"); // for sketch. +#define ANNOTATION_PREFIX QString("annotation_") +#define ANNOTATION_EXTENSION QString(".wta") + +#define NUM_RECENT_ITEMS 5 +#define RECENT_ITEMS_STORAGE CONFIG_DIR + QString("/recent.txt") + +extern class RecentlyUsed *recentlyUsed; +extern class GoogleDocsAccess *googleDocsAccess; +extern class EventGenerator *eventGenerator; + +// define the default tools for drawing +#define DEFAULT_DRAWING_PEN QPen(QBrush(Qt::black, Qt::SolidPattern), 1.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin) +#define DEFAULT_DRAWING_BRUSH QBrush(Qt::transparent, Qt::SolidPattern) +#define DEFAULT_ERASER QPen(Qt::red) + +typedef enum _DrawingMode { + DRAWINGMODE_FREEHAND, + DRAWINGMODE_ERASER, + DRAWINGMODE_STRAIGHTLINE, + DRAWINGMODE_ARROW, + DRAWINGMODE_RECTANGLE, + DRAWINGMODE_ELLIPSE +} DrawingMode; + +typedef enum _DrawingState { + DRAWINGSTATE_START, + DRAWINGSTATE_UPDATE, + DRAWINGSTATE_END +} DrawingState; + + +#endif // APPGLOBALS_H diff --git a/cbc/collaborative_drawing_widget/basedrawingwidget.cpp b/cbc/collaborative_drawing_widget/basedrawingwidget.cpp new file mode 100644 index 0000000..32bca46 --- /dev/null +++ b/cbc/collaborative_drawing_widget/basedrawingwidget.cpp @@ -0,0 +1,316 @@ +#include "basedrawingwidget.h" + +#include +#include +#include + +#include "appglobals.h" + + +BaseDrawingWidget::BaseDrawingWidget(QWidget *parent) : + QGraphicsView(parent) +{ + // allocate DrawingData + setDrawingData(new DrawingData(this)); + // set the default options for drawing + drawingPen = DEFAULT_DRAWING_PEN; + drawingBrush = DEFAULT_DRAWING_BRUSH; + drawingMode = DRAWINGMODE_FREEHAND; + // set background as transparent + setStyleSheet("background: transparent"); + +} + +BaseDrawingWidget::~BaseDrawingWidget() +{ + +} + +void BaseDrawingWidget::mouseMoveEvent (QMouseEvent * event) +{ + if(event->buttons() & Qt::LeftButton) + drawingUpdate(mapToScene(event->pos())); +} + +void BaseDrawingWidget::mousePressEvent (QMouseEvent * event) +{ + if(event->button() == Qt::LeftButton) + drawingStart(mapToScene(event->pos())); +} + +void BaseDrawingWidget::mouseReleaseEvent (QMouseEvent * event) +{ + if(event->button() == Qt::LeftButton) + drawingEnd(mapToScene(event->pos())); +} + +void BaseDrawingWidget::setDrawingData(DrawingData * newData) +{ + // TODO what to do with the old data? + drawingData = newData; + setScene(drawingData); +} + +DrawingData * BaseDrawingWidget::getDrawingData() +{ + return drawingData; +} + +void BaseDrawingWidget::setDrawingPen(QPen pen) +{ + drawingPen = pen; +} + +QPen BaseDrawingWidget::getDrawingPen() +{ + return drawingPen; +} + +void BaseDrawingWidget::setDrawingBrush(QBrush brush) +{ + drawingBrush = brush; +} + +QBrush BaseDrawingWidget::getDrawingBrush() +{ + return drawingBrush; +} + +void BaseDrawingWidget::setDrawingMode(DrawingMode mode) +{ + drawingMode = mode; +} + +DrawingMode BaseDrawingWidget::getDrawingMode() +{ + return drawingMode; +} + +void BaseDrawingWidget::drawingStart(QPointF startPoint) +{ + handleDrawingState(DRAWINGSTATE_START, startPoint); + mouseDownPoint = mousePrevPoint = startPoint; +} + +void BaseDrawingWidget::drawingUpdate(QPointF updatePoint) +{ + if(updatePoint.x() < 0) + updatePoint.setX(0); + if(updatePoint.y() < 0) + updatePoint.setY(0); + if(updatePoint.x() > drawingData->width()) + updatePoint.setX(drawingData->width()); + if(updatePoint.y() > drawingData->height()) + updatePoint.setY(drawingData->height()); + + handleDrawingState(DRAWINGSTATE_UPDATE, updatePoint); + mousePrevPoint = updatePoint; +} + +void BaseDrawingWidget::drawingEnd(QPointF endPoint) +{ + handleDrawingState(DRAWINGSTATE_END, endPoint); + //getDrawingData()->registerAction(); + mousePrevPoint = mouseUpPoint = endPoint; +} + +void BaseDrawingWidget::resizeEvent(QResizeEvent * event) +{ + if(drawingData != NULL) { + drawingData->setSceneRect(0,0,width(),height()); + qWarning() << "view:" << geometry() << "scene: " << drawingData->sceneRect(); + } + + QGraphicsView::resizeEvent(event); +} + +void BaseDrawingWidget::handleDrawingState(DrawingState state, QPointF lastPoint) +{ + // handle drawing start/update/end events for the current drawing mode + + QPainter::CompositionMode prevCompMode; + + if(state == DRAWINGSTATE_START) { + // start a new series of paint operations on the picture + picturePainter.begin(&picture); + // set the drawing style for the painter + picturePainter.setPen(drawingPen); + picturePainter.setBrush(drawingBrush); + } + + switch(drawingMode) { + case DRAWINGMODE_FREEHAND: + if(state == DRAWINGSTATE_START) { + QGraphicsLineItem * newLine = new QGraphicsLineItem(QLineF(lastPoint, lastPoint), 0, getDrawingData()); + currentItem = newLine; + newLine->setPen(drawingPen); + } + + else if(state == DRAWINGSTATE_UPDATE) { + QGraphicsLineItem * newLine = new QGraphicsLineItem(currentItem, getDrawingData()); + newLine->setLine(QLineF(mousePrevPoint, lastPoint)); + newLine->setPen(drawingPen); + picturePainter.drawLine(mousePrevPoint, lastPoint); + } + + else { + QGraphicsLineItem * newLine = new QGraphicsLineItem(currentItem, getDrawingData()); + newLine->setLine(QLineF(mousePrevPoint, lastPoint)); + newLine->setPen(drawingPen); + // remove the temporary QGraphicsItem + getDrawingData()->removeItem(currentItem); + picturePainter.drawLine(mousePrevPoint, lastPoint); + } + + break; + + case DRAWINGMODE_RECTANGLE: + if(state == DRAWINGSTATE_START) { + // create a temporary QGraphicsItem + // will be committed to the drawing when the mouse is released + QGraphicsRectItem * newRect = new QGraphicsRectItem(lastPoint.x(), lastPoint.y(),0,0); + currentItem = (QGraphicsItem *) newRect; + newRect->setPen(drawingPen); + newRect->setBrush(drawingBrush); + drawingData->addItem(currentItem); + } + + else if(state == DRAWINGSTATE_UPDATE) + // update the temporary QGraphicsItem + ((QGraphicsRectItem*)currentItem)->setRect(QRectF(mouseDownPoint,lastPoint).normalized()); + + else { + // remove the temporary QGraphicsItem + getDrawingData()->removeItem(currentItem); + // commit the drawing to the stage pixmap + picturePainter.drawRect(QRectF(mouseDownPoint,lastPoint).normalized()); + } + break; + + case DRAWINGMODE_STRAIGHTLINE: + if(state == DRAWINGSTATE_START) { + // create a temporary QGraphicsItem + // will be committed to the drawing when the mouse is released + QGraphicsLineItem * newLine = new QGraphicsLineItem(QLineF(lastPoint, lastPoint)); + currentItem = (QGraphicsItem*) newLine; + newLine->setPen(drawingPen); + getDrawingData()->addItem(newLine); + } + + else if(state == DRAWINGSTATE_UPDATE) { + // update the temporary QGraphicsItem + ((QGraphicsLineItem*)currentItem)->setLine(QLineF(mouseDownPoint, lastPoint)); + } + + else { + // remove the temporary QGraphicsItem + getDrawingData()->removeItem(currentItem); + // commit the drawing to the stage pixmap + picturePainter.drawLine(QLineF(mouseDownPoint, lastPoint)); + } + break; + + case DRAWINGMODE_ELLIPSE: + if(state == DRAWINGSTATE_START) { + // create a temporary QGraphicsItem + // will be committed to the drawing when the mouse is released + QGraphicsEllipseItem * newRect = new QGraphicsEllipseItem(lastPoint.x(), lastPoint.y(),0,0); + currentItem = (QGraphicsItem *) newRect; + newRect->setPen(drawingPen); + newRect->setBrush(drawingBrush); + drawingData->addItem(currentItem); + } + + else if(state == DRAWINGSTATE_UPDATE) + // update the temporary QGraphicsItem + ((QGraphicsRectItem*)currentItem)->setRect(QRectF(mouseDownPoint,lastPoint).normalized()); + + else { + // remove the temporary QGraphicsItem + getDrawingData()->removeItem(currentItem); + // commit the drawing to the stage pixmap + picturePainter.drawEllipse(QRectF(mouseDownPoint,lastPoint).normalized()); + } + break; + + case DRAWINGMODE_ERASER: + if(state == DRAWINGSTATE_START) { + QGraphicsRectItem * newEraseRect = new QGraphicsRectItem(QRectF(lastPoint, QSizeF(drawingPen.width()+5, drawingPen.width()+5)), 0, getDrawingData()); + currentItem = newEraseRect; + newEraseRect->setPen(QPen(Qt::transparent)); + newEraseRect->setBrush(QBrush(Qt::white)); + } + + else if(state == DRAWINGSTATE_UPDATE) { + QGraphicsRectItem * newEraseRect = new QGraphicsRectItem(currentItem, getDrawingData()); + newEraseRect->setRect(QRectF(lastPoint, QSizeF(drawingPen.width()+5, drawingPen.width()+5))); + newEraseRect->setPen(QPen(Qt::transparent)); + newEraseRect->setBrush(QBrush(Qt::white)); + } + + else { + QGraphicsRectItem * newEraseRect = new QGraphicsRectItem(currentItem, getDrawingData()); + newEraseRect->setRect(QRectF(lastPoint, QSizeF(drawingPen.width()+5, drawingPen.width()+5))); + newEraseRect->setPen(QPen(Qt::transparent)); + newEraseRect->setBrush(QBrush(Qt::white)); + // remove the temporary QGraphicsItem + getDrawingData()->removeItem(currentItem); + } + // common in all cases for the eraser: + // we have to set a specific composition mode for the eraser + // back up the current value + prevCompMode = picturePainter.compositionMode(); + picturePainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + // fill the region to be erased with transparent color + picturePainter.fillRect(QRectF(lastPoint, QSizeF(drawingPen.width()+5, drawingPen.width()+5)),Qt::transparent); + // restore the old composition mode + picturePainter.setCompositionMode(prevCompMode); + break; + + case DRAWINGMODE_ARROW: + // TODO not yet implemented - implement this as well + break; + + } + + if(state == DRAWINGSTATE_END) { + // finalize the painting on the QPicture + picturePainter.end(); + commitDrawing(picture); + } +} + +void BaseDrawingWidget::commitDrawing(QPicture drawingPictureData) +{ + getDrawingData()->registerAction(drawingPictureData); + getDrawingData()->setModified(true); + // update the stage + getDrawingData()->update(drawingPictureData.boundingRect().adjusted(-drawingPen.width()-5, + -drawingPen.width()-5, + drawingPen.width()+5, + drawingPen.width()+5)); +} + +void BaseDrawingWidget::setDrawingColor(QColor color) +{ + drawingPen.setColor(color); +} + +void BaseDrawingWidget::increasePenWidth() +{ + drawingPen.setWidth(drawingPen.width()+1); +} + +void BaseDrawingWidget::decreasePenWidth() +{ + drawingPen.setWidth(drawingPen.width() > 2 ? drawingPen.width()-1 : 1); +} + +void BaseDrawingWidget::clear() +{ + picturePainter.begin(&picture); + picturePainter.fillRect(sceneRect(), Qt::white); + picturePainter.end(); + + commitDrawing(picture); +} diff --git a/cbc/collaborative_drawing_widget/basedrawingwidget.h b/cbc/collaborative_drawing_widget/basedrawingwidget.h new file mode 100644 index 0000000..8516b28 --- /dev/null +++ b/cbc/collaborative_drawing_widget/basedrawingwidget.h @@ -0,0 +1,64 @@ +#ifndef BASEDRAWINGWIDGET_H +#define BASEDRAWINGWIDGET_H +#include "appglobals.h" +#include +#include +#include +#include +#include "drawingdata.h" + + + +class BaseDrawingWidget : public QGraphicsView +{ + Q_OBJECT +public: + explicit BaseDrawingWidget(QWidget *parent = 0); + ~BaseDrawingWidget(); + + void setDrawingData(DrawingData * newData); + DrawingData * getDrawingData(); + QPen getDrawingPen(); + QBrush getDrawingBrush(); + DrawingMode getDrawingMode(); + +protected: + void mouseMoveEvent (QMouseEvent * event); + void mousePressEvent (QMouseEvent * event); + void mouseReleaseEvent (QMouseEvent * event); + void resizeEvent (QResizeEvent * event); + +protected: + QPointF mouseDownPoint; + QPointF mouseUpPoint; + QPointF mousePrevPoint; + DrawingData * drawingData; + QPen drawingPen; + QBrush drawingBrush; + DrawingMode drawingMode; + QGraphicsItem * currentItem; + QPixmap * stage; + + QPicture picture; + QPainter picturePainter; + + virtual void drawingStart(QPointF startPoint); + virtual void drawingUpdate(QPointF updatePoint); + virtual void drawingEnd(QPointF endPoint); + virtual void handleDrawingState(DrawingState state, QPointF lastPoint); + virtual void commitDrawing(QPicture drawingData); + +signals: + +public slots: + void setDrawingBrush(QBrush brush); + void setDrawingPen(QPen pen); + void setDrawingMode(DrawingMode mode); + void setDrawingColor(QColor color); + void increasePenWidth(); + void decreasePenWidth(); + void clear(); + +}; + +#endif // BASEDRAWINGWIDGET_H diff --git a/cbc/collaborative_drawing_widget/collaborativedrawingwidget.cpp b/cbc/collaborative_drawing_widget/collaborativedrawingwidget.cpp new file mode 100644 index 0000000..3716366 --- /dev/null +++ b/cbc/collaborative_drawing_widget/collaborativedrawingwidget.cpp @@ -0,0 +1,42 @@ +#include "collaborativedrawingwidget.h" + +CollaborativeDrawingWidget::CollaborativeDrawingWidget(QWidget *parent) : + BaseDrawingWidget(parent) +{ + // disable scrollbars for collaboration area + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + // collaboration widget is disabled until we join a session + setEnabled(false); +} + +void CollaborativeDrawingWidget::commitDrawing(QPicture drawingPictureData) +{ + qWarning() << "commitDrawing size" << drawingPictureData.size(); + + BaseDrawingWidget::commitDrawing(drawingPictureData); + + emit drawingCommited(m_currentSession, drawingPictureData); + +} + +void CollaborativeDrawingWidget::drawingArrived(QString sessionName, QByteArray picData, bool isInitialState) +{ + // this slot will be invoked when the user joins a new session and the server + // sends the current drawing state for this session + QPicture pic; + qWarning() << "Session state data of size : " << picData.size() << "from session" << sessionName; + if(isInitialState) { + // set the session name + m_currentSession = sessionName; + // clear up any old data + getDrawingData()->clear(); + // the widget should be enabled now + setEnabled(true); + } + pic.setData(picData.constData(), picData.size()); + + // commit the new data + BaseDrawingWidget::commitDrawing(pic); +} diff --git a/cbc/collaborative_drawing_widget/collaborativedrawingwidget.h b/cbc/collaborative_drawing_widget/collaborativedrawingwidget.h new file mode 100644 index 0000000..c69c645 --- /dev/null +++ b/cbc/collaborative_drawing_widget/collaborativedrawingwidget.h @@ -0,0 +1,26 @@ +#ifndef COLLABORATIVEDRAWINGWIDGET_H +#define COLLABORATIVEDRAWINGWIDGET_H + +#include "basedrawingwidget.h" +#include + +class CollaborativeDrawingWidget : public BaseDrawingWidget +{ + Q_OBJECT +public: + explicit CollaborativeDrawingWidget(QWidget *parent = 0); + +protected: + void commitDrawing(QPicture drawingData); + + QString m_currentSession; + +signals: + void drawingCommited(QString sessionName, QPicture drawingData); + +public slots: + void drawingArrived(QString sessionName, QByteArray picData, bool isInitialState); + +}; + +#endif // COLLABORATIVEDRAWINGWIDGET_H diff --git a/cbc/collaborative_drawing_widget/drawingaction.cpp b/cbc/collaborative_drawing_widget/drawingaction.cpp new file mode 100644 index 0000000..3d85b14 --- /dev/null +++ b/cbc/collaborative_drawing_widget/drawingaction.cpp @@ -0,0 +1,38 @@ +#include "drawingaction.h" + +#include + +DrawingAction::DrawingAction(DrawingData *scene, QUndoCommand *parent) : + QUndoCommand(parent) +{ + parentScene = scene; +} + +void DrawingAction::undo() +{ + QPainter p(parentScene->getStage()); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.drawPixmap(undoRect.topLeft(),prevPixmap); + p.end(); + parentScene->update(); +} + +void DrawingAction::redo() +{ + QPainter p(parentScene->getStage()); + drawingActions.play(&p); + p.end(); + parentScene->update(); +} + +void DrawingAction::setActions(QPicture actions) +{ + // manually copy data, otherwise all redo steps end up being the same + drawingActions.setData(actions.data(), actions.size()); +} + +void DrawingAction::setPrevPixmap(QPixmap prev, QRect rect) +{ + prevPixmap = prev; + undoRect = rect; +} diff --git a/cbc/collaborative_drawing_widget/drawingaction.h b/cbc/collaborative_drawing_widget/drawingaction.h new file mode 100644 index 0000000..c128ce0 --- /dev/null +++ b/cbc/collaborative_drawing_widget/drawingaction.h @@ -0,0 +1,34 @@ +#ifndef DRAWINGACTION_H +#define DRAWINGACTION_H + +#include +#include +#include +#include + +#include "drawingdata.h" + +class DrawingData; + +class DrawingAction : public QUndoCommand +{ +public: + explicit DrawingAction(DrawingData *scene, QUndoCommand *parent = 0); + void setActions(QPicture actions); + void setPrevPixmap(QPixmap prev, QRect rect); + void undo(); + void redo(); + +private: + DrawingData * parentScene; + QPicture drawingActions; + QPixmap prevPixmap; + QRect undoRect; + +signals: + +public slots: + +}; + +#endif // DRAWINGACTION_H diff --git a/cbc/collaborative_drawing_widget/drawingdata.cpp b/cbc/collaborative_drawing_widget/drawingdata.cpp new file mode 100644 index 0000000..d4e6268 --- /dev/null +++ b/cbc/collaborative_drawing_widget/drawingdata.cpp @@ -0,0 +1,97 @@ +#include "drawingdata.h" + +#include +#include + +#include "appglobals.h" + + +DrawingData::DrawingData(QObject *parent) : + QGraphicsScene(parent) +{ + currentAction = new DrawingAction(this); + modified = false; + stage = new QPixmap(SCREEN_WIDTH, SCREEN_HEIGHT); + stage->fill(Qt::transparent); +} + +void DrawingData::saveImage(QString fileName) +{ + // TODO do not save anything if annotation is empty, maybe delete old file? + qWarning() << "saving to" << fileName; + stage->save(fileName, "png"); + setModified(false); +} + +void DrawingData::loadImage(QString fileName) +{ + clear(); + // TODO should check for the file's resolution and upscale/downscale to current if needed + stage->load(fileName, "png"); + + setModified(false); +} + +QUndoStack * DrawingData::getUndoStack() +{ + return &undoStack; +} + +void DrawingData::registerAction(QPicture actions) +{ + QRect boundingRect = actions.boundingRect().adjusted(-2,-2,2,2); + // fix negative / too large coordinates + if(boundingRect.x() < 0) + boundingRect.setX(0); + if(boundingRect.y() < 0) + boundingRect.setY(0); + if(boundingRect.x() > width()) + boundingRect.setX(width()); + if(boundingRect.y() > height()) + boundingRect.setY(height()); + // backup the area to be painted, to be used for undo'ing later + QPixmap areaToChange = stage->copy(boundingRect); + areaToChange.save("undo.png"); + // set the properties of this drawing step for undo/redo + currentAction->setActions(actions); + currentAction->setPrevPixmap(areaToChange, boundingRect); + // add to the undo stack + undoStack.push(currentAction); + currentAction = new DrawingAction(this); +} + +bool DrawingData::isModified() +{ + return modified; +} + +void DrawingData::setModified(bool newValue) +{ + modified = newValue; +} + +void DrawingData::clear() +{ + QGraphicsScene::clear(); + undoStack.clear(); + setModified(false); + + if(currentAction) delete currentAction; + currentAction = new DrawingAction(this); + + // clear the stage pixmap + stage->fill(Qt::transparent); +} + +QPixmap * DrawingData::getStage() +{ + return stage; +} + +void DrawingData::drawBackground ( QPainter * painter, const QRectF & rect ) +{ + // draw the stage pixmap as the scene background + // the stage pixmap is where we do all the user-created drawing + painter->setClipRect(rect); + painter->drawPixmap(0,0, *stage); +} diff --git a/cbc/collaborative_drawing_widget/drawingdata.h b/cbc/collaborative_drawing_widget/drawingdata.h new file mode 100644 index 0000000..aa6b7c7 --- /dev/null +++ b/cbc/collaborative_drawing_widget/drawingdata.h @@ -0,0 +1,46 @@ +#ifndef DRAWINGDATA_H +#define DRAWINGDATA_H + +#include +#include +#include +#include +#include + +#include "drawingaction.h" + +class DrawingAction; + +class DrawingData : public QGraphicsScene +{ + Q_OBJECT +public: + explicit DrawingData(QObject *parent = 0); + void saveImage(QString fileName); + void loadImage(QString fileName); + + QUndoStack * getUndoStack(); + void registerAction(QPicture actions); + void clear(); + + bool isModified(); + void setModified(bool newValue); + + QPixmap * getStage(); + +protected: + void drawBackground ( QPainter * painter, const QRectF & rect ); + +private: + QUndoStack undoStack; + DrawingAction *currentAction; + bool modified; + QPixmap *stage; + +signals: + +public slots: + +}; + +#endif // DRAWINGDATA_H diff --git a/cbc/common/collaborationsession.cpp b/cbc/common/collaborationsession.cpp new file mode 100644 index 0000000..f9468d4 --- /dev/null +++ b/cbc/common/collaborationsession.cpp @@ -0,0 +1,116 @@ +#include "collaborationsession.h" +#include +#include + +CollaborationSession::CollaborationSession(QObject *parent) : + QObject(parent) +{ +} + +void CollaborationSession::setSessionName(QString newName) +{ + m_sessionName = newName; +} + +QString CollaborationSession::getSessionName() +{ + return m_sessionName; +} + +void CollaborationSession::setSessionPassword(QString newPassword) +{ + m_sessionPassword = newPassword; +} + +QString CollaborationSession::getSessionPassword() +{ + return m_sessionPassword; +} + +QHash CollaborationSession::getSessionParticipants() +{ + return m_sessionParticipants; +} + +void CollaborationSession::setSessionParticipants(QHash participantList) +{ + m_sessionParticipants = participantList; + + //Initialize all session members as unacknowledged + QHash::iterator itr; + for (itr = participantList.begin(); itr != participantList.end(); itr++) + { + m_sessionAcknowledgements.insert(itr.key(),false); + } +} + +bool CollaborationSession::addSessionParticipant(QString userName, QString sessionPassword, long userIpAddress) +{ + if(sessionPassword == m_sessionPassword) { + // password is correct, check if participant already exists in this session + if(m_sessionParticipants.contains(userName)) + //participant already exists in session + return false; + else { + // everything in order, we can add this participant to the session + m_sessionParticipants[userName] = userIpAddress; + + return true; + } + } else + qWarning() << "expected md5 password = " << m_sessionPassword << " found : " << sessionPassword; + // incorrect password + return false; +} + +void CollaborationSession::removeSessionParticipant(QString userName) +{ + if(m_sessionParticipants.contains(userName)) + m_sessionParticipants.remove(userName); +} + +QPicture CollaborationSession::getSessionDrawingState() +{ + return m_sessionDrawingData; +} + +void CollaborationSession::addDrawingStep(QPicture newDrawingStep) +{ + QPicture tmpPicture; + m_picturePainter.begin(&tmpPicture); + m_picturePainter.drawPicture(0,0,m_sessionDrawingData); + m_picturePainter.drawPicture(0,0,newDrawingStep); + m_picturePainter.end(); + + m_sessionDrawingData = tmpPicture; + + //TODO Remove this + QPixmap hede(800,480); + hede.fill(Qt::white); + m_picturePainter.begin(&hede); + m_picturePainter.drawPicture(0,0, m_sessionDrawingData); + m_picturePainter.end(); + hede.save("state.png"); + +} + + +void CollaborationSession::acknowledgePeer(QString userName) +{ + m_sessionAcknowledgements[userName] = true; +} + +bool CollaborationSession::isAcknowledged(QString userName) +{ + return m_sessionAcknowledgements[userName]; +} + +bool CollaborationSession::isAllAcknowledged() +{ + QHash::iterator itr; + for (itr = m_sessionAcknowledgements.begin(); itr != m_sessionAcknowledgements.end(); itr++) + { + if (!itr.value()) return false; + } + return true; +} diff --git a/cbc/common/collaborationsession.h b/cbc/common/collaborationsession.h new file mode 100644 index 0000000..c0bccde --- /dev/null +++ b/cbc/common/collaborationsession.h @@ -0,0 +1,51 @@ +#ifndef COLLABORATIONSESSION_H +#define COLLABORATIONSESSION_H + +#include +#include +#include +#include +#include + +// represents the information and actions of a single collaborative drawing session + +class CollaborationSession : public QObject +{ + Q_OBJECT +public: + explicit CollaborationSession(QObject *parent = 0); + + void setSessionName(QString newName); + QString getSessionName(); + + void setSessionPassword(QString newPassword); + QString getSessionPassword(); + + QHash getSessionParticipants(); + void setSessionParticipants(QHash participantList); + + bool addSessionParticipant(QString userName, QString sessionPassword, long userIpAddress); + void removeSessionParticipant(QString userName); + + QPicture getSessionDrawingState(); + void addDrawingStep(QPicture newDrawingStep); + + void acknowledgePeer(QString userName); + bool isAcknowledged(QString userName); + bool isAllAcknowledged(); + +protected: + QString m_sessionName; + QString m_sessionPassword; + QHash m_sessionParticipants; + QHash m_sessionAcknowledgements; + QPicture m_sessionDrawingData; + QPainter m_picturePainter; + +signals: + +public slots: + +}; + +#endif // COLLABORATIONSESSION_H diff --git a/cbc/common/messagetransceiver.cpp b/cbc/common/messagetransceiver.cpp new file mode 100644 index 0000000..381264f --- /dev/null +++ b/cbc/common/messagetransceiver.cpp @@ -0,0 +1,219 @@ +#include "messagetransceiver.h" + +// The MessageTransciever class is a generic sender / receiver for collaboration messages +// It is server/client agnostic, meaning device can run the same MessageTransciever class +// without regard to its role as client or server. It is responsible for: +// - sending collaboration messages to remote transceivers +// - receiving collaboration messages from remote transceivers + +// MessageTransceiver is mainly useful for keeping a single connection to each peer +// in the network and accessing these connections as needed + + +MessageTransceiver::MessageTransceiver(QObject *parent) : + QThread(parent) +{ + // the transciever will act both as a server and a client + // client behaviour is triggered by the connectTo slot + // server behaviour is provided by a QTcpServer object +} + +void MessageTransceiver::run() +{ + // set up the tcp server + mServer = new QTcpServer(); + // forward incoming connection requests from the tcp server to us + connect(mServer, SIGNAL(newConnection()), this, SLOT(newConnection())); + // start listening for connection + if(!mServer->listen(QHostAddress::Any, TRANSCEIVER_TCP_PORT)) { + // listening on this specified port failed + // sorry, no go! + qWarning() << "QTcpServer failed to listen!"; + return; + } + exec(); +} + +void MessageTransceiver::connectTo(QString destination) +{ + if(mOpenConnections.value(destination, NULL)) { + // a connection for this destination already exists + // TODO display error? or just return from the function quietly? + qWarning() << "Connection for destination" << destination << "already exists!"; + return; + } + + // TODO connect signals from QTcpSockets to handle errors and check if conn is established + QTcpSocket * newConnection = new QTcpSocket(this); + + // we may have to queue some messages to this socket before connection + // is established + if(!destBuffers.contains(destination)) + destBuffers[destination] = QByteArray(); + + // connect the signals for this tcp socket + connect(newConnection, SIGNAL(connected()), this, SLOT(connected())); + connect(newConnection, SIGNAL(disconnected()), this, SLOT(disconnected())); + connect(newConnection, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); + // try to establish the connection + newConnection->connectToHost(destination, TRANSCEIVER_TCP_PORT); +} + +void MessageTransceiver::sendMessage(QString destination, QByteArray msg) +{ + unsigned int messageSize = msg.length() + 12; // TODO header size should be defined + QTcpSocket * destinationSocket = mOpenConnections.value(destination, NULL); + + if(!destinationSocket) { + qWarning() << "Connection for destination" << destination << "does not exist, put data into queue"; + // queue data, will be sent when connection is established + if(!destBuffers.contains(destination)) + destBuffers[destination] = QByteArray(); + destBuffers[destination].append(TRANSCEIVER_HEADER); + destBuffers[destination].append((const char *) &messageSize, 4); + destBuffers[destination].append(msg); + // open a connection to this peer + connectTo(destination); + return; + } + // attach the transceiver-level header with message size info + destinationSocket->write(TRANSCEIVER_HEADER); + destinationSocket->write((const char *) &messageSize, 4); + // TODO add checksum? + destinationSocket->write(msg); +} + +void MessageTransceiver::sendMessageNoHeader(QTcpSocket* destinationSocket,QByteArray msg) +{ + // send message directly without attaching transceiver-level header + // meaning that the transceiver-level header is already attached + // only used internally for queued data transmission + destinationSocket->write(msg); +} + +void MessageTransceiver::newConnection() +{ + QTcpSocket * newSocket; + qWarning() << "got new connection request!"; + // establish all waiting connections + while(mServer->hasPendingConnections()) { + newSocket = mServer->nextPendingConnection(); + qWarning() << "adding new remote-initiated connection to" << newSocket->peerAddress().toString(); + mOpenConnections.insert(newSocket->peerAddress().toString(), newSocket); + originBuffers.insert(newSocket->peerAddress().toString(), QByteArray()); + originExpectedDataSize.insert(newSocket->peerAddress().toString(), 0); + // connect signals for newly arrived connection + connect(newSocket, SIGNAL(readyRead()), this, SLOT(dataArrived())); + connect(newSocket, SIGNAL(disconnected()), this, SLOT(disconnected())); + // TODO also connect and handle error signals + } +} + +void MessageTransceiver::connected() +{ + QTcpSocket * newConnection = qobject_cast(sender()); + QString destination = newConnection->peerAddress().toString(); + + if(newConnection) { + qWarning() << "adding new local-initiated connection to" << destination; + // insert new tcp socket into the list of open connections + mOpenConnections.insert(destination, newConnection); + originBuffers.insert(destination, QByteArray()); + originExpectedDataSize.insert(destination, 0); + connect(newConnection, SIGNAL(readyRead()), this, SLOT(dataArrived())); + + // if there is data waiting to be sent in the buffer to this destination, + // now we can send it + if(destBuffers[destination].size() > 0) { + qWarning() << "sending queued data to" << destination << "datasize" << destBuffers[destination].size(); + sendMessageNoHeader(newConnection, destBuffers[destination]); + // clear the send queue + destBuffers[destination] = QByteArray(); + } + } +} + +void MessageTransceiver::disconnected() +{ + QTcpSocket * oldConnection = qobject_cast(sender()); + // TODO destination empty when remote peer disconnects? + QString destination = oldConnection->peerAddress().toString(); + + // TODO emit a signal for disconnection of this client + + if(oldConnection) { + qWarning() << "peer disconnected:" << destination; + // remove from connection list + mOpenConnections.remove(destination); + } +} + + +void MessageTransceiver::dataArrived() +{ + QTcpSocket * connection = qobject_cast(sender()); + QString origin = connection->peerAddress().toString(); + QByteArray newData; + QByteArray bufferContent; + unsigned int expectedLength = 0; + + if(connection) { + qWarning() << "got new data from" << origin; + newData = connection->readAll(); + // TODO check if origin buffer for this origin exists + if(!originBuffers.contains(origin)) + originBuffers[origin] = QByteArray(); + // append data to buffer and process + originBuffers[origin].append(newData); + processOriginBuffer(origin); + } +} + +void MessageTransceiver::processOriginBuffer(QString origin) +{ + QByteArray originBuffer = originBuffers[origin]; + QByteArray currentMessage; + unsigned int currentMessageSize = 0; + bool remainingMessages = true; + + while(remainingMessages) { + if(originBuffer.length() == 0) + break; + // check if buffer starts with broken message + if(!originBuffer.startsWith(TRANSCEIVER_HEADER)) { + // buffer does not stat with header, + // possibly broken message + qWarning() << "Error! processOriginBuffer found missing transceiver header for origin" << origin; + // reset the origin buffer + // TODO instead of reset, search for next message transceiver header + originBuffers[origin] = QByteArray(); + return; + } + // read the size of next message in line + memcpy(¤tMessageSize, originBuffer.constData() + 8, 4); + qWarning() << "processOriginBuffer current message size" << currentMessageSize << "buffer length" << originBuffer.length(); + // check if this message is complete + if(currentMessageSize <= originBuffer.length()) { + // message is complete + currentMessage = originBuffer.left(currentMessageSize).right(currentMessageSize-12); // discard msgtxrx header + qWarning() << "length of current message" << currentMessage.length(); + emit gotNewData(origin, currentMessage); + // truncate the origin buffer + originBuffers[origin] = originBuffer.right(originBuffer.length() - currentMessageSize); + originBuffer = originBuffers[origin]; + qWarning() << "new origin buffer size" << originBuffer.size(); + } else { + // message is incomplete, exit loop + qWarning() << "processOriginBuffer waiting for more data"; + remainingMessages = false; + } + } +} + +void MessageTransceiver::socketError(QAbstractSocket::SocketError err) +{ + QTcpSocket * connection = qobject_cast(sender()); + QString origin = connection->peerAddress().toString(); + + qWarning() << "socket error" << err << "for" << origin; +} diff --git a/cbc/common/messagetransceiver.h b/cbc/common/messagetransceiver.h new file mode 100644 index 0000000..43857f7 --- /dev/null +++ b/cbc/common/messagetransceiver.h @@ -0,0 +1,46 @@ +#ifndef MESSAGETRANSCEIVER_H +#define MESSAGETRANSCEIVER_H + +#include +#include +#include +#include +#include + +#define TRANSCEIVER_TCP_PORT 45454 +#define TRANSCEIVER_HEADER "MSGTXRX1" + +class MessageTransceiver : public QThread +{ + Q_OBJECT +public: + explicit MessageTransceiver(QObject *parent = 0); + void run(); + +protected: + QHash mOpenConnections; + QTcpServer * mServer; + QHash originBuffers; + QHash destBuffers; + QHash originExpectedDataSize; + + void sendMessageNoHeader(QTcpSocket*,QByteArray msg); + void processOriginBuffer(QString origin); + +signals: + void gotNewData(QString origin, QByteArray data); + +public slots: + void connectTo(QString destination); + void sendMessage(QString destination, QByteArray msg); + +private slots: + void newConnection(); + void connected(); + void disconnected(); + void dataArrived(); + void socketError(QAbstractSocket::SocketError err); + +}; + +#endif // MESSAGETRANSCEIVER_H diff --git a/cbc/common/protocolhandler.cpp b/cbc/common/protocolhandler.cpp new file mode 100644 index 0000000..e47162c --- /dev/null +++ b/cbc/common/protocolhandler.cpp @@ -0,0 +1,553 @@ +#include "protocolhandler.h" + +ProtocolHandler::ProtocolHandler(QObject *parent) : + QObject(parent) +{ + m_messageTransceiver = NULL; + + // connect the loopback signal-slot pair + connect(this, SIGNAL(sendMessageLoopback(QString,QByteArray)), this, SLOT(receiveMessage(QString,QByteArray))); +} + +void ProtocolHandler::addUserMapping(QString userName, QString IP) +{ + peerMap.insert(userName, IP); +} + +QString ProtocolHandler::getUserMapping(QString userName) +{ + return peerMap.value(userName, ""); +} + +QString ProtocolHandler::getUserName() +{ + return userName; +} + +void ProtocolHandler::setUserName(QString username) +{ + this->userName = username; +} + +// receiveMessage is responsible for handling byte array data coming +// from the MessageTransceiver by checking for proper message format, +// deserializing it, constructing the appropriate message object +// and passing it to its corresponding handler +void ProtocolHandler::receiveMessage(QString origin, QByteArray data) +{ + // check for zero-length message + if(data.length() == 0) { + // TODO what to do? + qWarning() << "ProtocolHandler::receiveMessage got message with length" << data.length(); + return; + } + + WTMessage candidateMsg; + // TODO pass all byte arrays as non-pointers + candidateMsg.deserialize(data); + // check if deserialized data is of correct format! + if(candidateMsg.getVersion() != "WTC1") { + // incorrect or damaged message + qWarning() << "ProtocolHandler::receiveMessage got message with incorrect header" << candidateMsg.getVersion(); + return; + } + qWarning() << "command:" << candidateMsg.getCommand(); + if (candidateMsg.getCommand() == "LOGINREQ") + { + WTLoginMessage *msg = new WTLoginMessage(); + msg->deserialize(data); + handleLoginRequest(msg, origin); + } + else if (candidateMsg.getCommand() == "LOGINRES") + { + WTLoginResponse *msg = new WTLoginResponse(); + msg->deserialize(data); + handleLoginResponse(msg); + } + else if (candidateMsg.getCommand() == "LOGOUTRQ") + { + WTLogoutRequest *msg = new WTLogoutRequest(); + msg->deserialize(data); + handleLogoutRequest(msg); + } + else if (candidateMsg.getCommand() == "COLSTARQ") + { + WTPictureRequest *msg = new WTPictureRequest(); + msg->deserialize(data); + handlePictureRequest(msg); + } + else if (candidateMsg.getCommand() == "COLSTARS") + { + WTPictureResponse *msg = new WTPictureResponse(); + msg->deserialize(data); + handlePictureResponse(msg); + } + else if (candidateMsg.getCommand() == "SESSJOIN") + { + WTSessionJoinRequest *msg = new WTSessionJoinRequest(); + msg->deserialize(data); + handleSessionJoinRequest(msg); + } + else if (candidateMsg.getCommand() == "SESSRESP") + { + WTSessionJoinResponse *msg = new WTSessionJoinResponse(); + msg->deserialize(data); + handleSessionJoinResponse(msg); + } + else if (candidateMsg.getCommand() == "SESSQUIT") + { + WTSessionLeaveRequest *msg = new WTSessionLeaveRequest(); + msg->deserialize(data); + handleSessionLeaveRequest(msg); + } + else if (candidateMsg.getCommand() == "SESSQACK") + { + WTSessionLeaveResponse *msg = new WTSessionLeaveResponse(); + msg->deserialize(data); + handleSessionLeaveResponse(msg); + } + else if (candidateMsg.getCommand() == "SESSLREQ") + { + WTSessionListRequest *msg = new WTSessionListRequest(); + msg->deserialize(data); + handleSessionListRequest(msg); + } + else if (candidateMsg.getCommand() == "SESSLRES") + { + WTSessionListResponse *msg = new WTSessionListResponse(); + msg->deserialize(data); + handleSessionListResponse(msg); + } + else if (candidateMsg.getCommand() == "SESSMUPD") + { + WTSessionMemberUpdate *msg = new WTSessionMemberUpdate(); + msg->deserialize(data); + handleSessionMemberUpdate(msg); + } + else if (candidateMsg.getCommand() == "DRAWUPDT") + { + WTUpdateDrawing *msg = new WTUpdateDrawing(); + msg->deserialize(data); + handleUpdateDrawing(msg); + } + else if (candidateMsg.getCommand() == "DRAWSUPD") + { + WTUpdateDrawingServer *msg = new WTUpdateDrawingServer(); + msg->deserialize(data); + handleUpdateDrawingServer(msg); + } + else if (candidateMsg.getCommand() == "WRTPRMRQ") + { + WTWritePermissionRequest *msg = new WTWritePermissionRequest(); + msg->deserialize(data); + handleWritePermissionRequest(msg); + } + else if (candidateMsg.getCommand() == "WRTPRMST") + { + WTWritePermissionStatus *msg = new WTWritePermissionStatus(); + msg->deserialize(data); + handleWritePermissionStatus(msg); + } + else if (candidateMsg.getCommand() == "HELLOPAL") + { + WTPeerHandshake *msg = new WTPeerHandshake(); + msg->deserialize(data); + handlePeerHandshake(msg, origin); + } + else if (candidateMsg.getCommand() == "SESCRTRQ") + { + WTSessionCreateRequest *msg = new WTSessionCreateRequest(); + msg->deserialize(data); + handleSessionCreateRequest(msg); + } + else if (candidateMsg.getCommand() == "SESCRTRS") + { + WTSessionCreateResponse *msg = new WTSessionCreateResponse(); + msg->deserialize(data); + handleSessionCreateResponse(msg); + } +} + +// deliverMessage is responsible for handing the given message in byte array +// form to the MessageTransceiver with the corresponding peer destination +// for this username +bool ProtocolHandler::deliverMessage(WTMessage * msg) +{ + // check for null messages + if(!msg) + return false; + // serialize the message into a byte array + QByteArray msgData = msg->serialize(); + // check for loopback (send message to self) + if(msg->getDestUsername() == getUserName()) { + qWarning() << "message loopback!"; + emit sendMessageLoopback(msg->getDestUsername(), msgData); + delete msg; + return true; + } + // find the peer destination for this username + QString destination = peerMap.value(msg->getDestUsername(), ""); + if(destination == "") { + // the destination address for this peer is not defined + qWarning() << "deliverMessage: destination address for peer" << msg->getDestUsername() << "undefined"; + delete msg; + return false; + } + // time for delivery! + emit sendMessage(destination, msgData); + // we should be able to delete this message now + // TODO handle the new - delete logic more carefully! what happens when errors + // occur, for example? + delete msg; + return true; +} + + +void ProtocolHandler::handleMapRequestStatus(QString username, bool confirmed) +{ + + if(confirmed) { + // this mapping has been confirmed + // we don't need the destination parameter, look for the username + // in the pending map requests and move it to the established mappings + QString destination = pendingMapRequests.value(username, ""); + if(destination == "") { + // oops - something went wrong + // no such mapping request pending + // TODO deeper error handling? + qWarning() << "no pending mapping request found for confirming" << username; + return; + } else { + // add this mapping to the list of established peer mappings + peerMap.insert(username, destination); + } + } + + // remove from list of pending requests + pendingMapRequests.remove(username); +} + + +void ProtocolHandler::handleLoginRequest(WTLoginMessage *msg, QString requestOrigin) +{ + // Local peer role: Server + // add a mapping request for the remote peer + // this will be verified or discarded upon login result + pendingMapRequests.insert(msg->getSrcUsername(), requestOrigin); + + emit receivedLoginRequest(msg->getSrcUsername()); +} + +void ProtocolHandler::handleLoginResponse(WTLoginResponse *msg) +{ + // Local peer role: Client + emit receivedLoginResponse(msg->getSrcUsername(), msg->getResult(), msg->getInfomsg()); +} + +void ProtocolHandler::handleLogoutRequest(WTLogoutRequest *msg) +{ + // Local peer role: Server + // remove mapping for this peer + peerMap.remove(msg->getSrcUsername()); + emit receivedLogoutRequest(msg->getSrcUsername()); +} + +void ProtocolHandler::handlePictureRequest(WTPictureRequest *msg) +{ + //Local peer role: Server + emit receivedPictureRequest(msg->getSrcUsername(), msg->getSessionName()); +} + +void ProtocolHandler::handlePictureResponse(WTPictureResponse *msg) +{ + //Local peer role: Client + emit receivedPictureResponse(msg->getSrcUsername(), msg->getSessionName(), msg->getPicData()); +} + +void ProtocolHandler::handleSessionJoinRequest(WTSessionJoinRequest *msg) +{ + //Local peer role: Server + emit receivedSessionJoinRequest(msg->getSrcUsername(), msg->getSessionName(), msg->getPassword()); +} + +void ProtocolHandler::handleSessionJoinResponse(WTSessionJoinResponse *msg) +{ + //Local peer role: Client + emit receivedSessionJoinResponse(msg->getSrcUsername(), msg->getSessionName(), msg->getResult(), msg->getUserCount(), msg->getUsers()); +} + +void ProtocolHandler::handleSessionLeaveRequest(WTSessionLeaveRequest *msg) +{ + //Local peer role: Server + emit receivedSessionLeaveRequest(msg->getSrcUsername(), msg->getSessionName()); +} + +void ProtocolHandler::handleSessionLeaveResponse(WTSessionLeaveResponse *msg) +{ + //Local peer role: Client + emit receivedSessionLeaveResponse(msg->getSrcUsername(), msg->getSessionName(), msg->getResult()); +} + +void ProtocolHandler::handleSessionListRequest(WTSessionListRequest *msg) +{ + //Local peer role: Server + emit receivedSessionListRequest(msg->getSrcUsername()); +} + +void ProtocolHandler::handleSessionListResponse(WTSessionListResponse *msg) +{ + //Local peer role: Client + emit receivedSessionListResponse(msg->getSrcUsername(), msg->getSessionList()); +} + +void ProtocolHandler::handleSessionMemberUpdate(WTSessionMemberUpdate *msg) +{ + //Local peer role: Server + emit receivedSessionMemberUpdate(msg->getSrcUsername(), msg->getSessionName(), msg->getUpdateType(), msg->getUser()); +} + +void ProtocolHandler::handleUpdateDrawing(WTUpdateDrawing *msg) +{ + //Local peer role: Client + emit receivedUpdateDrawing(msg->getSrcUsername(), msg->getSessionName(), msg->getPicData()); +} + +void ProtocolHandler::handleUpdateDrawingServer(WTUpdateDrawingServer *msg) +{ + //Local peer role: Server + emit receivedUpdateDrawingServer(msg->getSrcUsername(), msg->getSessionName(), msg->getPicData()); +} + +void ProtocolHandler::handleWritePermissionRequest(WTWritePermissionRequest *msg) +{ + //Local peer role: Server + emit receivedWritePermissionRequest(msg->getSrcUsername()); +} + +void ProtocolHandler::handleWritePermissionStatus(WTWritePermissionStatus *msg) +{ + //Local peer role: Client + emit receivedWritePermissionStatus(msg->getSrcUsername(), msg->getStatus()); +} + +void ProtocolHandler::handlePeerHandshake(WTPeerHandshake *msg, QString requestOrigin) +{ + //Local peer role: Client + peerMap.insert(msg->getSrcUsername(), requestOrigin); + emit receivedPeerHandshake(msg->getSrcUsername(), msg->getSessionName()); +} + +void ProtocolHandler::handleSessionCreateRequest(WTSessionCreateRequest *msg) +{ + //Local peer role: Server + emit receivedSessionCreateRequest(msg->getSrcUsername(),msg->getSessionName(), msg->getPassword()); +} + +void ProtocolHandler::handleSessionCreateResponse(WTSessionCreateResponse *msg) +{ + //Local peer role: Client + emit receivedSessionCreateResponse(msg->getSrcUsername(), msg->getSessionName(), msg->getResult(), msg->getPassword()); +} + + +void ProtocolHandler::sendLoginRequest(QString destUserName) +{ + WTLoginMessage *msg = new WTLoginMessage; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + deliverMessage(msg); +} + +void ProtocolHandler::sendLoginResponse(QString destUserName, char result, QString infoMsg) +{ + // Local peer role: server + // if login was OK, add pending mapping + // TODO encode this '1' in a more meaningful way + handleMapRequestStatus(destUserName, (bool) result); + WTLoginResponse *msg = new WTLoginResponse; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setResult(result); + msg->setInfomsg(infoMsg); + deliverMessage(msg); +} + +void ProtocolHandler::sendLogoutRequest(QString destUserName) +{ + WTLogoutRequest *msg = new WTLogoutRequest; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + deliverMessage(msg); +} + +void ProtocolHandler::sendPeerHandshake(QString destUserName, QString sessionName) +{ + WTPeerHandshake *msg = new WTPeerHandshake; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionName(sessionName); + deliverMessage(msg); +} + +void ProtocolHandler::sendPictureRequest(QString destUserName, QString sessionName) +{ + WTPictureRequest *msg = new WTPictureRequest; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionName(sessionName); + deliverMessage(msg); +} + +void ProtocolHandler::sendPictureResponse(QString destUserName, QString sessionName, QByteArray picData) +{ + WTPictureResponse *msg = new WTPictureResponse; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionName(sessionName); + msg->setPicData(picData); + deliverMessage(msg); +} + +void ProtocolHandler::sendSessionJoinRequest(QString destUserName, QString sessionName, QString password) +{ + WTSessionJoinRequest *msg = new WTSessionJoinRequest; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionName(sessionName); + msg->setPassword(password); + deliverMessage(msg); +} + +void ProtocolHandler::sendSessionJoinResponse(QString destUserName, QString sessionName, char result, QHash users) +{ + WTSessionJoinResponse *msg = new WTSessionJoinResponse; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionName(sessionName); + msg->setResult(result); + msg->setUsers(users); + deliverMessage(msg); +} + +void ProtocolHandler::sendSessionLeaveRequest(QString destUserName, QString sessionName) +{ + WTSessionLeaveRequest *msg = new WTSessionLeaveRequest; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionName(sessionName); + deliverMessage(msg); +} + +void ProtocolHandler::sendSessionLeaveResponse(QString destUserName, QString sessionName, char result) +{ + WTSessionLeaveResponse *msg = new WTSessionLeaveResponse; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionName(sessionName); + msg->setResult(result); + deliverMessage(msg); +} + +void ProtocolHandler::sendSessionListRequest(QString destUserName) +{ + WTSessionListRequest *msg = new WTSessionListRequest; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + deliverMessage(msg); +} + +void ProtocolHandler::sendSessionListResponse(QString destUserName, QStringList sessionList) +{ + WTSessionListResponse *msg = new WTSessionListResponse; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionList(sessionList); + deliverMessage(msg); +} + +void ProtocolHandler::sendSessionMemberUpdate(QString destUserName, QString sessionName, char updateType, QString user) +{ + WTSessionMemberUpdate *msg = new WTSessionMemberUpdate; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionName(sessionName); + msg->setUpdateType(updateType); + msg->setUser(user); + deliverMessage(msg); +} + +void ProtocolHandler::sendUpdateDrawing(QString destUserName, QString sessionName, QByteArray picData) +{ + WTUpdateDrawing *msg = new WTUpdateDrawing; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionName(sessionName); + msg->setPicData(picData); + deliverMessage(msg); +} + +void ProtocolHandler::sendUpdateDrawingServer(QString destUserName, QString sessionName, QByteArray picData) +{ + WTUpdateDrawingServer *msg = new WTUpdateDrawingServer; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionName(sessionName); + msg->setPicData(picData); + deliverMessage(msg); +} + +void ProtocolHandler::sendWritePermissionRequest(QString destUserName) +{ + WTWritePermissionRequest *msg = new WTWritePermissionRequest; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + deliverMessage(msg); +} + +void ProtocolHandler::sendWritePermissionStatus(QString destUserName, QChar status) +{ + WTWritePermissionStatus *msg = new WTWritePermissionStatus; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setStatus(status.toAscii()); + deliverMessage(msg); +} + +void ProtocolHandler::sendSessionCreateRequest(QString destUserName, QString sessionName, QString password) +{ + WTSessionCreateRequest *msg = new WTSessionCreateRequest; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionName(sessionName); + msg->setPassword(password); + deliverMessage(msg); +} + +void ProtocolHandler::sendSessionCreateResponse(QString destUserName, QString sessionName, QChar result, QString password) +{ + WTSessionCreateResponse *msg = new WTSessionCreateResponse; + msg->setSrcUsername(this->userName); + msg->setDestUsername(destUserName); + msg->setSessionName(sessionName); + msg->setResult(result); + msg->setPassword(password); + deliverMessage(msg); +} + + +void ProtocolHandler::setMessageTransceiver(MessageTransceiver * newMesssageTransceiver) +{ + if(m_messageTransceiver) { + // disconnect all signals and slots from previous MessageTransceiver + disconnect(this); + disconnect(m_messageTransceiver); + } + // connect signals and slots + connect(this, SIGNAL(sendMessage(QString,QByteArray)), newMesssageTransceiver, SLOT(sendMessage(QString,QByteArray))); + connect(newMesssageTransceiver, SIGNAL(gotNewData(QString,QByteArray)), this, SLOT(receiveMessage(QString,QByteArray))); +} + +MessageTransceiver* ProtocolHandler::getMessageTransceiver() +{ + return m_messageTransceiver; +} diff --git a/cbc/common/protocolhandler.h b/cbc/common/protocolhandler.h new file mode 100644 index 0000000..15afbd5 --- /dev/null +++ b/cbc/common/protocolhandler.h @@ -0,0 +1,124 @@ +#ifndef PROTOCOLHANDLER_H +#define PROTOCOLHANDLER_H + +#include +#include + +#include "wtmessage.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "messagetransceiver.h" + +class ProtocolHandler : public QObject +{ + Q_OBJECT +public: + explicit ProtocolHandler(QObject *parent = 0); + + void addUserMapping(QString userName, QString IP); + QString getUserMapping(QString userName); + + void setMessageTransceiver(MessageTransceiver * newMesssageTransceiver); + MessageTransceiver* getMessageTransceiver(); + + QString getUserName(); + void setUserName(QString username); + +private: + MessageTransceiver * m_messageTransceiver; + QHash peerMap; + QHash pendingMapRequests; + QString userName; + + bool deliverMessage(WTMessage * msg); + + void handleMapRequestStatus(QString username, bool confirmed); + + void handleLoginRequest(WTLoginMessage *msg, QString requestOrigin); + void handleLoginResponse(WTLoginResponse *msg); + void handleLogoutRequest(WTLogoutRequest *msg); + void handlePictureRequest(WTPictureRequest *msg); + void handlePictureResponse(WTPictureResponse *msg); + void handleSessionJoinRequest(WTSessionJoinRequest *msg); + void handleSessionJoinResponse(WTSessionJoinResponse *msg); + void handleSessionLeaveRequest(WTSessionLeaveRequest *msg); + void handleSessionLeaveResponse(WTSessionLeaveResponse *msg); + void handleSessionListRequest(WTSessionListRequest *msg); + void handleSessionListResponse(WTSessionListResponse *msg); + void handleSessionMemberUpdate(WTSessionMemberUpdate *msg); + void handleUpdateDrawing(WTUpdateDrawing *msg); + void handleUpdateDrawingServer(WTUpdateDrawingServer *msg); + void handleWritePermissionRequest(WTWritePermissionRequest *msg); + void handleWritePermissionStatus(WTWritePermissionStatus *msg); + void handlePeerHandshake(WTPeerHandshake *msg, QString requestOrigin); + void handleSessionCreateRequest(WTSessionCreateRequest *msg); + void handleSessionCreateResponse(WTSessionCreateResponse *msg); + + +signals: + void sendMessage(QString destination, QByteArray data); + void sendMessageLoopback(QString desination, QByteArray data); + + void receivedLoginRequest(QString userName); + void receivedLoginResponse(QString userName, QChar result, QString infoMsg); + void receivedLogoutRequest(QString userName); + void receivedPeerHandshake(QString userName, QString sessionName); + void receivedPictureRequest(QString userName, QString sessionName); + void receivedPictureResponse(QString userName, QString sessionName, QByteArray picData); + void receivedSessionJoinRequest(QString userName, QString sessionName, QString password); + void receivedSessionJoinResponse(QString userName, QString sessionName, char result, unsigned int userCount, QHash users); + void receivedSessionLeaveRequest(QString userName, QString sessionName); + void receivedSessionLeaveResponse(QString userName, QString sessionName, char result); + void receivedSessionListRequest(QString userName); + void receivedSessionListResponse(QString userName, QStringList sessionList); + void receivedSessionMemberUpdate(QString userName, QString sessionName, char updateType, QString user); + void receivedUpdateDrawing(QString userName, QString sessionName, QByteArray picData); + void receivedUpdateDrawingServer(QString userName, QString sessionName, QByteArray picData); + void receivedWritePermissionRequest(QString userName); + void receivedWritePermissionStatus(QString userName, QChar status); + void receivedSessionCreateRequest(QString userName, QString sessionName, QString password); + void receivedSessionCreateResponse(QString userName, QString sessionName, QChar result, QString password); + +public slots: + void receiveMessage(QString origin, QByteArray data); + + void sendLoginRequest(QString destUserName); + void sendLoginResponse(QString destUserName, char result, QString infoMsg); + void sendLogoutRequest(QString destUserName); + void sendPeerHandshake(QString destUserName, QString sessionName); + void sendPictureRequest(QString destUserName, QString sessionName); + void sendPictureResponse(QString destUserName, QString sessionName, QByteArray picData); + void sendSessionJoinRequest(QString destUserName, QString sessionName, QString password); + void sendSessionJoinResponse(QString destUserName, QString sessionName, char result, QHash users); + void sendSessionLeaveRequest(QString destUserName, QString sessionName); + void sendSessionLeaveResponse(QString destUserName, QString sessionName, char result); + void sendSessionListRequest(QString destUserName); + void sendSessionListResponse(QString destUserName, QStringList sessionList); + void sendSessionMemberUpdate(QString destUserName, QString sessionName, char updateType, QString users); + void sendUpdateDrawing(QString destUserName, QString sessionName, QByteArray picData); + void sendUpdateDrawingServer(QString destUserName, QString sessionName, QByteArray picData); + void sendWritePermissionRequest(QString destUserName); + void sendWritePermissionStatus(QString destUserName, QChar status); + void sendSessionCreateRequest(QString destUserName, QString sessionName, QString password); + void sendSessionCreateResponse(QString destUserName, QString sessionName, QChar result, QString password); +}; + +#endif // PROTOCOLHANDLER_H diff --git a/cbc/common/wtloginmessage.cpp b/cbc/common/wtloginmessage.cpp new file mode 100644 index 0000000..d565630 --- /dev/null +++ b/cbc/common/wtloginmessage.cpp @@ -0,0 +1,7 @@ +#include "wtloginmessage.h" + +WTLoginMessage::WTLoginMessage() : + WTMessage() +{ + command = "LOGINREQ"; +} diff --git a/cbc/common/wtloginmessage.h b/cbc/common/wtloginmessage.h new file mode 100644 index 0000000..fc7f3ad --- /dev/null +++ b/cbc/common/wtloginmessage.h @@ -0,0 +1,17 @@ +#ifndef WTLOGINMESSAGE_H +#define WTLOGINMESSAGE_H + +#include "wtmessage.h" + +class WTLoginMessage : public WTMessage +{ +public: + explicit WTLoginMessage(); + +signals: + +public slots: + +}; + +#endif // WTLOGINMESSAGE_H diff --git a/cbc/common/wtloginresponse.cpp b/cbc/common/wtloginresponse.cpp new file mode 100644 index 0000000..b68ea12 --- /dev/null +++ b/cbc/common/wtloginresponse.cpp @@ -0,0 +1,56 @@ +#include "wtloginresponse.h" + +WTLoginResponse::WTLoginResponse() : + WTMessage() +{ + command = "LOGINRES"; +} + +QByteArray WTLoginResponse::serialize() +{ + //Size of result + size of msg size + size of msg + msgSize += 1 + 2 + infomsg.length(); + QByteArray data = WTMessage::serialize(); + data.append(result); + data.append(QByteArray::fromRawData((const char*)&infomsgSize,2)); + data.append(infomsg.toAscii()); + return data; +} + +void WTLoginResponse::deserialize(QByteArray data) +{ + QDataStream dataStream(data); + char result; + char *infomsg; + WTMessage::deserialize(data); + + //Skip header and usernames + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(&result, 1); + dataStream.readRawData((char *)&infomsgSize, 2); + infomsg = new char[infomsgSize+1]; + dataStream.readRawData(infomsg, infomsgSize); + infomsg[infomsgSize] = '\0'; + this->infomsg = QString(infomsg); +} + +void WTLoginResponse::setInfomsg(QString msg) +{ + this->infomsg = msg; + infomsgSize = msg.size(); +} + +QString WTLoginResponse::getInfomsg() +{ + return this->infomsg; +} + +void WTLoginResponse::setResult(char result) +{ + this->result = result; +} + +char WTLoginResponse::getResult() +{ + return this->result; +} diff --git a/cbc/common/wtloginresponse.h b/cbc/common/wtloginresponse.h new file mode 100644 index 0000000..83ac99b --- /dev/null +++ b/cbc/common/wtloginresponse.h @@ -0,0 +1,26 @@ +#ifndef WTLOGINRESPONSE_H +#define WTLOGINRESPONSE_H + +#include + +class WTLoginResponse : public WTMessage +{ +public: + explicit WTLoginResponse(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + void setInfomsg(QString msg); + QString getInfomsg(); + + void setResult(char result); + char getResult(); + +private: + char result; + QString infomsg; + short infomsgSize; +}; + +#endif // WTLOGINRESPONSE_H diff --git a/cbc/common/wtlogoutrequest.cpp b/cbc/common/wtlogoutrequest.cpp new file mode 100644 index 0000000..60b6e68 --- /dev/null +++ b/cbc/common/wtlogoutrequest.cpp @@ -0,0 +1,7 @@ +#include "wtlogoutrequest.h" + +WTLogoutRequest::WTLogoutRequest() : + WTMessage() +{ + command = "LOGOUTRQ"; +} diff --git a/cbc/common/wtlogoutrequest.h b/cbc/common/wtlogoutrequest.h new file mode 100644 index 0000000..67e9016 --- /dev/null +++ b/cbc/common/wtlogoutrequest.h @@ -0,0 +1,13 @@ +#ifndef WTLOGOUTREQUEST_H +#define WTLOGOUTREQUEST_H + +#include + +class WTLogoutRequest : public WTMessage +{ +public: + explicit WTLogoutRequest(); + +}; + +#endif // WTLOGOUTREQUEST_H diff --git a/cbc/common/wtmessage.cpp b/cbc/common/wtmessage.cpp new file mode 100644 index 0000000..7f5c9bb --- /dev/null +++ b/cbc/common/wtmessage.cpp @@ -0,0 +1,86 @@ +#include "wtmessage.h" + +WTMessage::WTMessage() : + msgSize(HEADER_SIZE) //Constant header size + username +{ + version = "WTC1"; +} + +WTMessage::~WTMessage() +{ + +} + +QByteArray WTMessage::serialize() +{ + QByteArray data(version.toAscii()); + data.append(command.toAscii()); + data.append(QByteArray::fromRawData((const char*)&msgSize, 4)); + data.append(srcUsername.leftJustified(8, ' ').toAscii()); + data.append(destUsername.leftJustified(8, ' ').toAscii()); + return data; +} + +void WTMessage::deserialize(QByteArray data) +{ + //Assuming the header is always of the same size. + char version[5]; + char command[9]; + char srcUsername[9]; + char destUsername[9]; + QDataStream dataStream(data); + dataStream.readRawData(version, 4); + version[4] = '\0'; + dataStream.readRawData(command, 8); + command[8] = '\0'; + dataStream.readRawData((char *)&msgSize, 4); + dataStream.readRawData(srcUsername, 8); + srcUsername[8] = '\0'; + dataStream.readRawData(destUsername, 8); + destUsername[8] = '\0'; + this->version = QString(version); + this->command = QString(command); + this->srcUsername = QString(srcUsername).trimmed(); + this->destUsername = QString(destUsername).trimmed(); +} + + +void WTMessage::setSrcUsername(QString username) +{ + this->srcUsername = username; +} + +QString WTMessage::getSrcUsername() +{ + return this->srcUsername; +} + +void WTMessage::setDestUsername(QString username) +{ + this->destUsername = username; +} + +QString WTMessage::getDestUsername() +{ + return this->destUsername; +} + +void WTMessage::setCommand(QString command) +{ + this->command = command; +} + +QString WTMessage::getCommand() +{ + return this->command; +} + +void WTMessage::setVersion(QString version) +{ + this->version = version; +} + +QString WTMessage::getVersion() +{ + return this->version; +} diff --git a/cbc/common/wtmessage.h b/cbc/common/wtmessage.h new file mode 100644 index 0000000..1ac4baa --- /dev/null +++ b/cbc/common/wtmessage.h @@ -0,0 +1,37 @@ +#ifndef WTMESSAGE_H +#define WTMESSAGE_H + +#include +#include +#include + +#define HEADER_SIZE 32 + +class WTMessage +{ + +public: + explicit WTMessage(); + ~WTMessage(); + virtual QByteArray serialize(); + virtual void deserialize(QByteArray data); + void setSrcUsername(QString username); + QString getSrcUsername(); + void setDestUsername(QString username); + QString getDestUsername(); + void setCommand(QString command); + QString getCommand(); + void setVersion(QString version); + QString getVersion(); + +protected: + QString version; + QString command; + int msgSize; + QString srcUsername; + QString destUsername; + + +}; + +#endif // WTMESSAGE_H diff --git a/cbc/common/wtpeerhandshake.cpp b/cbc/common/wtpeerhandshake.cpp new file mode 100644 index 0000000..7697430 --- /dev/null +++ b/cbc/common/wtpeerhandshake.cpp @@ -0,0 +1,40 @@ +#include "wtpeerhandshake.h" + +WTPeerHandshake::WTPeerHandshake() : + WTMessage() +{ + command = "HELLOPAL"; +} + + +QByteArray WTPeerHandshake::serialize() +{ + //Size of sessionName + msgSize += 8; + QByteArray data = WTMessage::serialize(); + data.append(sessionName.leftJustified(8, ' ').toAscii()); + return data; +} + + +void WTPeerHandshake::deserialize(QByteArray data) +{ + char sessionName[9]; + QDataStream dataStream(data); + WTMessage::deserialize(data); + //Skip the header and username + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(sessionName, 8); + sessionName[8] = '\0'; + this->sessionName = QString(sessionName).trimmed(); +} + +QString WTPeerHandshake::getSessionName() +{ + return this->sessionName; +} + +void WTPeerHandshake::setSessionName(QString sessionName) +{ + this->sessionName = sessionName; +} diff --git a/cbc/common/wtpeerhandshake.h b/cbc/common/wtpeerhandshake.h new file mode 100644 index 0000000..a68c2fb --- /dev/null +++ b/cbc/common/wtpeerhandshake.h @@ -0,0 +1,21 @@ +#ifndef WTPEERHANDSHAKE_H +#define WTPEERHANDSHAKE_H + +#include + +class WTPeerHandshake : public WTMessage +{ +public: + explicit WTPeerHandshake(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + QString getSessionName(); + void setSessionName(QString sessionName); +private: + QString sessionName; + +}; + +#endif // WTPEERHANDSHAKE_H diff --git a/cbc/common/wtpicturerequest.cpp b/cbc/common/wtpicturerequest.cpp new file mode 100644 index 0000000..c53d9be --- /dev/null +++ b/cbc/common/wtpicturerequest.cpp @@ -0,0 +1,38 @@ +#include "wtpicturerequest.h" + +WTPictureRequest::WTPictureRequest() : + WTMessage() +{ + command = "COLSTARQ"; +} + +QByteArray WTPictureRequest::serialize() +{ + //Size of sessionName + msgSize += 8; + QByteArray data = WTMessage::serialize(); + data.append(sessionName.leftJustified(8, ' ').toAscii()); + return data; +} + +void WTPictureRequest::deserialize(QByteArray data) +{ + char sessionName[9]; + QDataStream dataStream(data); + WTMessage::deserialize(data); + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(sessionName,8); + sessionName[8] = '\0'; + this->sessionName = QString(sessionName).trimmed(); +} + +QString WTPictureRequest::getSessionName() +{ + return this->sessionName; +} + +void WTPictureRequest::setSessionName(QString sessionName) +{ + this->sessionName = sessionName; +} + diff --git a/cbc/common/wtpicturerequest.h b/cbc/common/wtpicturerequest.h new file mode 100644 index 0000000..ed5e5ea --- /dev/null +++ b/cbc/common/wtpicturerequest.h @@ -0,0 +1,22 @@ +#ifndef WTPICTUREREQUEST_H +#define WTPICTUREREQUEST_H + +#include + +class WTPictureRequest : public WTMessage +{ +public: + explicit WTPictureRequest(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + QString getSessionName(); + void setSessionName(QString sessionName); + +private: + QString sessionName; + +}; + +#endif // WTPICTUREREQUEST_H diff --git a/cbc/common/wtpictureresponse.cpp b/cbc/common/wtpictureresponse.cpp new file mode 100644 index 0000000..39de76a --- /dev/null +++ b/cbc/common/wtpictureresponse.cpp @@ -0,0 +1,54 @@ +#include "wtpictureresponse.h" + +WTPictureResponse::WTPictureResponse() : + WTMessage() +{ + command = "COLSTARS"; +} + +QByteArray WTPictureResponse::serialize() +{ + //size of sessionName + size of picdata + msgSize += 8 + picData.size(); + QByteArray data = WTMessage::serialize(); + data.append(sessionName.leftJustified(8, ' ').toAscii()); + data.append(picData); + return data; +} + +void WTPictureResponse::deserialize(QByteArray data) +{ + char sessionName[9]; + char *picData; + QDataStream dataStream(data); + WTMessage::deserialize(data); + //Skip header and username + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(sessionName, 8); + sessionName[8] = '\0'; + this->sessionName = QString(sessionName).trimmed(); + + picData = new char[data.size()-(HEADER_SIZE + 8)]; + dataStream.readRawData(picData, data.size() - (HEADER_SIZE + 8)); + this->picData.setRawData(picData, data.size() - (HEADER_SIZE + 8)); +} + +QString WTPictureResponse::getSessionName() +{ + return this->sessionName; +} + +void WTPictureResponse::setSessionName(QString sessionName) +{ + this->sessionName = sessionName; +} + +QByteArray WTPictureResponse::getPicData() +{ + return picData; +} + +void WTPictureResponse::setPicData(QByteArray picData) +{ + this->picData = picData; +} diff --git a/cbc/common/wtpictureresponse.h b/cbc/common/wtpictureresponse.h new file mode 100644 index 0000000..d7a3c36 --- /dev/null +++ b/cbc/common/wtpictureresponse.h @@ -0,0 +1,27 @@ +#ifndef WTPICTURERESPONSE_H +#define WTPICTURERESPONSE_H + +#include + +class WTPictureResponse : public WTMessage +{ +public: + explicit WTPictureResponse(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + QString getSessionName(); + void setSessionName(QString sessionName); + + QByteArray getPicData(); + void setPicData(QByteArray picData); + +private: + QString sessionName; + QByteArray picData; + + +}; + +#endif // WTPICTURERESPONSE_H diff --git a/cbc/common/wtsessioncreaterequest.cpp b/cbc/common/wtsessioncreaterequest.cpp new file mode 100644 index 0000000..d566153 --- /dev/null +++ b/cbc/common/wtsessioncreaterequest.cpp @@ -0,0 +1,58 @@ +#include "wtsessioncreaterequest.h" + + +WTSessionCreateRequest::WTSessionCreateRequest() : + WTMessage() +{ + command = "SESCRTRQ"; +} + + +QByteArray WTSessionCreateRequest::serialize() +{ + //Message size is the size of common header + session name + password in md5 + msgSize += 8 + 32; + QByteArray data = WTMessage::serialize(); + data.append(sessionName.leftJustified(8, ' ').toAscii()); + //Send md5 code of the password + data.append(password.leftJustified(32, ' ').toAscii()); + return data; +} + +void WTSessionCreateRequest::deserialize(QByteArray data) +{ + char sessionName[9]; + char password[33]; + + QDataStream dataStream(data); + WTMessage::deserialize(data); + //Skip header and username as they have already been deserialized above + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(sessionName, 8); + sessionName[8] = '\0'; + dataStream.readRawData(password, 32); + password[32] = '\0'; + this->sessionName = QString(sessionName).trimmed(); + this->password = QString(password).trimmed(); +} + +QString WTSessionCreateRequest::getSessionName() +{ + return this->sessionName; +} + +void WTSessionCreateRequest::setSessionName(QString sessionName) +{ + this->sessionName = sessionName; +} + +QString WTSessionCreateRequest::getPassword() +{ + return this->password; +} + +void WTSessionCreateRequest::setPassword(QString password) +{ + //Convert the password into md5 + this->password = QCryptographicHash::hash(password.toAscii(), QCryptographicHash::Md5); +} diff --git a/cbc/common/wtsessioncreaterequest.h b/cbc/common/wtsessioncreaterequest.h new file mode 100644 index 0000000..af3d6b5 --- /dev/null +++ b/cbc/common/wtsessioncreaterequest.h @@ -0,0 +1,27 @@ +#ifndef WTSESSIONCREATEREQUEST_H +#define WTSESSIONCREATEREQUEST_H + +#include +#include + +class WTSessionCreateRequest : public WTMessage +{ +public: + explicit WTSessionCreateRequest(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + QString getSessionName(); + void setSessionName(QString sessionName); + + QString getPassword(); + void setPassword(QString password); + +private: + QString sessionName; + QString password; + +}; + +#endif // WTSESSIONCREATEREQUEST_H diff --git a/cbc/common/wtsessioncreateresponse.cpp b/cbc/common/wtsessioncreateresponse.cpp new file mode 100644 index 0000000..8adf22d --- /dev/null +++ b/cbc/common/wtsessioncreateresponse.cpp @@ -0,0 +1,71 @@ +#include "wtsessioncreateresponse.h" + +WTSessionCreateResponse::WTSessionCreateResponse() : + WTMessage() +{ + command = "SESCRTRS"; +} + + +QByteArray WTSessionCreateResponse::serialize() +{ + //Size of common header + result + session name + encrypted password + msgSize += 8 + 1 + 32; + QByteArray data = WTMessage::serialize(); + data.append(result); + data.append(sessionName.leftJustified(8, ' ').toAscii()); + data.append(password.leftJustified(32, ' ').toAscii()); + return data; +} + +void WTSessionCreateResponse::deserialize(QByteArray data) +{ + char sessionName[9]; + char password[33]; + char result; + + QDataStream dataStream(data); + WTMessage::deserialize(data); + //Skip header and username as they have already been deserialized above + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(&result, 1); + dataStream.readRawData(sessionName, 8); + sessionName[8] = '\0'; + dataStream.readRawData(password, 32); + password[32] = '\0'; + + this->sessionName = QString(sessionName).trimmed(); + this->result = QChar(result); + this->password = QString(password).trimmed(); +} + +QString WTSessionCreateResponse::getSessionName() +{ + return this->sessionName; +} + +void WTSessionCreateResponse::setSessionName(QString sessionName) +{ + this->sessionName = sessionName; +} + +QChar WTSessionCreateResponse::getResult() +{ + return this->result; +} + +void WTSessionCreateResponse::setResult(QChar result) +{ + this->result = result; +} + +QString WTSessionCreateResponse::getPassword() +{ + return this->password; +} + +void WTSessionCreateResponse::setPassword(QString password) +{ + //It is assumed here that the password is already encrypted + this->password = password; +} diff --git a/cbc/common/wtsessioncreateresponse.h b/cbc/common/wtsessioncreateresponse.h new file mode 100644 index 0000000..8dd4b12 --- /dev/null +++ b/cbc/common/wtsessioncreateresponse.h @@ -0,0 +1,32 @@ +#ifndef WTSESSIONCREATERESPONSE_H +#define WTSESSIONCREATERESPONSE_H + +#include "wtmessage.h" + +class WTSessionCreateResponse : public WTMessage +{ +public: + explicit WTSessionCreateResponse(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + QString getSessionName(); + void setSessionName(QString sessionName); + + QChar getResult(); + void setResult(QChar result); + + QString getPassword(); + void setPassword(QString password); + +private: + QString sessionName; + //Result info: + // 0 - Unsuccessful - Password might be wrong + // 1 - Successful + QChar result; + QString password; +}; + +#endif // WTSESSIONCREATERESPONSE_H diff --git a/cbc/common/wtsessionjoinrequest.cpp b/cbc/common/wtsessionjoinrequest.cpp new file mode 100644 index 0000000..9c8f04d --- /dev/null +++ b/cbc/common/wtsessionjoinrequest.cpp @@ -0,0 +1,57 @@ +#include "wtsessionjoinrequest.h" + +WTSessionJoinRequest::WTSessionJoinRequest() : + WTMessage() +{ + command = "SESSJOIN"; +} + +QByteArray WTSessionJoinRequest::serialize() +{ + //Size of sessionName + size of encrypted password + msgSize += 8 + 32; + QByteArray data = WTMessage::serialize(); + data.append(sessionName.leftJustified(8,' ').toAscii()); + data.append(password.leftJustified(32, ' ').toAscii()); + return data; +} + +void WTSessionJoinRequest::deserialize(QByteArray data) +{ + QDataStream dataStream(data); + char password[33]; + char sessionName[9]; + WTMessage::deserialize(data); + //Skip header and username + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(sessionName, 8); + dataStream.readRawData(password, 32); + sessionName[8] = '\0'; + password[32] = '\0'; + this->sessionName = QString(sessionName).trimmed(); + this->password = QString(password).trimmed(); +} + +void WTSessionJoinRequest::setSessionName(QString sessionName) +{ + this->sessionName = sessionName; +} + +QString WTSessionJoinRequest::getSessionName() +{ + return this->sessionName; +} + +void WTSessionJoinRequest::setPassword(QString password) +{ + //Store the password as encrypted + this->password = password; + //this->password = QCryptographicHash::hash(password.toAscii(), QCryptographicHash::Md5); +} + +QString WTSessionJoinRequest::getPassword() +{ + return password; +} + + diff --git a/cbc/common/wtsessionjoinrequest.h b/cbc/common/wtsessionjoinrequest.h new file mode 100644 index 0000000..7838dda --- /dev/null +++ b/cbc/common/wtsessionjoinrequest.h @@ -0,0 +1,28 @@ +#ifndef WTSESSIONJOINREQUEST_H +#define WTSESSIONJOINREQUEST_H + +#include +#include + + +class WTSessionJoinRequest : public WTMessage +{ +public: + explicit WTSessionJoinRequest(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + void setSessionName(QString sessionName); + QString getSessionName(); + + void setPassword(QString password); + QString getPassword(); + +private: + QString sessionName; + QString password; + +}; + +#endif // WTSESSIONJOINREQUEST_H diff --git a/cbc/common/wtsessionjoinresponse.cpp b/cbc/common/wtsessionjoinresponse.cpp new file mode 100644 index 0000000..63304fd --- /dev/null +++ b/cbc/common/wtsessionjoinresponse.cpp @@ -0,0 +1,98 @@ +#include "wtsessionjoinresponse.h" + +WTSessionJoinResponse::WTSessionJoinResponse() : + WTMessage(), + userCount(0) +{ + command = "SESSRESP"; +} + +QByteArray WTSessionJoinResponse::serialize() +{ + //Size of result + size of sessionname + size of usercount + size of usernames and ips + msgSize += 1 + 8 + 4 + 12 * userCount; + QByteArray data = WTMessage::serialize(); + data.append(result); + data.append(sessionName.leftJustified(8,' ').toAscii()); + data.append(QByteArray::fromRawData((const char *)&userCount,4)); + QHash::iterator iter; + for (iter = users.begin(); iter != users.end(); iter++) + { + data.append(QString(iter.key()).leftJustified(8, ' ').toAscii()); + data.append(QByteArray::fromRawData((const char *)&(iter.value()),4)); + } + return data; +} + +void WTSessionJoinResponse::deserialize(QByteArray data) +{ + char sessionName[9]; + QDataStream dataStream(data); + WTMessage::deserialize(data); + //Skip header + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(&result,1); + dataStream.readRawData(sessionName, 8); + sessionName[8] = '\0'; + dataStream.readRawData((char *)&userCount, 4); + for (unsigned int i = 0; i < userCount; i++) + { + char username[9]; + long userIP; + dataStream.readRawData(username,8); + username[8] = '\0'; + dataStream.readRawData((char *)&userIP, 4); + users.insert(QString(username).trimmed(), userIP); + } + this->sessionName = QString(sessionName).trimmed(); +} + + +void WTSessionJoinResponse::setResult(char result) +{ + this->result = result; +} + +char WTSessionJoinResponse::getResult() +{ + return this->result; +} + +void WTSessionJoinResponse::setSessionName(QString sessionName) +{ + this->sessionName = sessionName; +} + +QString WTSessionJoinResponse::getSessionName() +{ + return this->sessionName; +} + +unsigned int WTSessionJoinResponse::getUserCount() +{ + return this->userCount; +} + +void WTSessionJoinResponse::addUser(QString username, long userIP) +{ + this->userCount++; + users.insert(username, userIP); +} + +long WTSessionJoinResponse::getUserIP(QString username) +{ + QHash::const_iterator iter = users.find(username); + return iter.value(); +} + +void WTSessionJoinResponse::setUsers(QHash users) +{ + this->users = users; + this->userCount = users.size(); +} + +QHash WTSessionJoinResponse::getUsers() +{ + return this->users; +} + diff --git a/cbc/common/wtsessionjoinresponse.h b/cbc/common/wtsessionjoinresponse.h new file mode 100644 index 0000000..a4a803d --- /dev/null +++ b/cbc/common/wtsessionjoinresponse.h @@ -0,0 +1,31 @@ +#ifndef WTSESSIONJOINRESPONSE_H +#define WTSESSIONJOINRESPONSE_H + +#include + +class WTSessionJoinResponse : public WTMessage +{ +public: + explicit WTSessionJoinResponse(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + void setResult(char result); + char getResult(); + void setSessionName(QString sessionName); + QString getSessionName(); + unsigned int getUserCount(); + void addUser(QString username, long userIP); + long getUserIP(QString username); + void setUsers(QHash users); + QHash getUsers(); + +private: + char result; + QString sessionName; + unsigned int userCount; + QHash users; +}; + +#endif // WTSESSIONJOINRESPONSE_H diff --git a/cbc/common/wtsessionleaverequest.cpp b/cbc/common/wtsessionleaverequest.cpp new file mode 100644 index 0000000..da8594d --- /dev/null +++ b/cbc/common/wtsessionleaverequest.cpp @@ -0,0 +1,38 @@ +#include "wtsessionleaverequest.h" + +WTSessionLeaveRequest::WTSessionLeaveRequest() : + WTMessage() +{ + command = "SESSQUIT"; +} + +QByteArray WTSessionLeaveRequest::serialize() +{ + //Size of sessionName + msgSize += 8; + QByteArray data = WTMessage::serialize(); + data.append(sessionName.leftJustified(8, ' ').toAscii()); + return data; +} + +void WTSessionLeaveRequest::deserialize(QByteArray data) +{ + char sessionName[9]; + QDataStream dataStream(data); + WTMessage::deserialize(data); + //Skip header and username + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(sessionName, 8); + sessionName[8] = '\0'; + this->sessionName = QString(sessionName).trimmed(); +} + +QString WTSessionLeaveRequest::getSessionName() +{ + return this->sessionName; +} + +void WTSessionLeaveRequest::setSessionName(QString sessionName) +{ + this->sessionName = sessionName; +} diff --git a/cbc/common/wtsessionleaverequest.h b/cbc/common/wtsessionleaverequest.h new file mode 100644 index 0000000..3995085 --- /dev/null +++ b/cbc/common/wtsessionleaverequest.h @@ -0,0 +1,22 @@ +#ifndef WTSESSIONLEAVEREQUEST_H +#define WTSESSIONLEAVEREQUEST_H + +#include + +class WTSessionLeaveRequest : public WTMessage +{ +public: + explicit WTSessionLeaveRequest(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + void setSessionName(QString sessionName); + QString getSessionName(); + +private: + QString sessionName; + +}; + +#endif // WTSESSIONLEAVEREQUEST_H diff --git a/cbc/common/wtsessionleaveresponse.cpp b/cbc/common/wtsessionleaveresponse.cpp new file mode 100644 index 0000000..26b47be --- /dev/null +++ b/cbc/common/wtsessionleaveresponse.cpp @@ -0,0 +1,50 @@ +#include "wtsessionleaveresponse.h" + +WTSessionLeaveResponse::WTSessionLeaveResponse() : + WTMessage() +{ + command = "SESSQACK"; +} + +QByteArray WTSessionLeaveResponse::serialize() +{ + //Size of sessionName + size of result + msgSize += 8 + 1; + QByteArray data = WTMessage::serialize(); + data.append(sessionName.leftJustified(8, ' ').toAscii()); + data.append(result); + return data; +} + +void WTSessionLeaveResponse::deserialize(QByteArray data) +{ + char sessionName[9]; + QDataStream dataStream(data); + WTMessage::deserialize(data); + //Skip header and username + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(sessionName, 8); + sessionName[8] = '\0'; + dataStream.readRawData(&result,1); + this->sessionName = QString(sessionName).trimmed(); +} + +char WTSessionLeaveResponse::getResult() +{ + return this->result; +} + +void WTSessionLeaveResponse::setResult(char result) +{ + this->result = result; +} + +QString WTSessionLeaveResponse::getSessionName() +{ + return this->sessionName; +} + +void WTSessionLeaveResponse::setSessionName(QString sessionName) +{ + this->sessionName = sessionName; +} diff --git a/cbc/common/wtsessionleaveresponse.h b/cbc/common/wtsessionleaveresponse.h new file mode 100644 index 0000000..4c74353 --- /dev/null +++ b/cbc/common/wtsessionleaveresponse.h @@ -0,0 +1,26 @@ +#ifndef WTSESSIONLEAVERESPONSE_H +#define WTSESSIONLEAVERESPONSE_H + +#include + +class WTSessionLeaveResponse : public WTMessage +{ +public: + explicit WTSessionLeaveResponse(); + + void deserialize(QByteArray data); + QByteArray serialize(); + + char getResult(); + void setResult(char result); + + void setSessionName(QString sessionName); + QString getSessionName(); + +private: + QString sessionName; + char result; + +}; + +#endif // WTSESSIONLEAVERESPONSE_H diff --git a/cbc/common/wtsessionlistrequest.cpp b/cbc/common/wtsessionlistrequest.cpp new file mode 100644 index 0000000..924c588 --- /dev/null +++ b/cbc/common/wtsessionlistrequest.cpp @@ -0,0 +1,7 @@ +#include "wtsessionlistrequest.h" + +WTSessionListRequest::WTSessionListRequest() : + WTMessage() +{ + command = "SESSLREQ"; +} diff --git a/cbc/common/wtsessionlistrequest.h b/cbc/common/wtsessionlistrequest.h new file mode 100644 index 0000000..d35c5d8 --- /dev/null +++ b/cbc/common/wtsessionlistrequest.h @@ -0,0 +1,17 @@ +#ifndef WTSESSIONLISTREQUEST_H +#define WTSESSIONLISTREQUEST_H + +#include "wtmessage.h" + +class WTSessionListRequest : public WTMessage +{ +public: + explicit WTSessionListRequest(); + +signals: + +public slots: + +}; + +#endif // WTSESSIONLISTREQUEST_H diff --git a/cbc/common/wtsessionlistresponse.cpp b/cbc/common/wtsessionlistresponse.cpp new file mode 100644 index 0000000..fa5bfc8 --- /dev/null +++ b/cbc/common/wtsessionlistresponse.cpp @@ -0,0 +1,60 @@ +#include "wtsessionlistresponse.h" + +WTSessionListResponse::WTSessionListResponse() : + WTMessage(), + sesscnt(0) +{ + command = "SESSLRES"; +} + +QByteArray WTSessionListResponse::serialize() +{ + //size of sesscnt + total size of session names + msgSize += 4 + sesscnt * 8; + QByteArray data = WTMessage::serialize(); + data.append(QByteArray::fromRawData((const char *)&sesscnt, 4)); + + int size = sessionList.size(); + for (int i = 0; i < size; i++) + { + data.append(((QString)sessionList.at(i)).leftJustified(8, ' ').toAscii()); + } + return data; +} + +void WTSessionListResponse::deserialize(QByteArray data) +{ + QDataStream dataStream(data); + WTMessage::deserialize(data); + //Skip header and username + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData((char *)&sesscnt, 4); + qWarning() << sesscnt; + for (unsigned int i = 0; i < sesscnt; i++) + { + char sessionName[9]; + dataStream.readRawData(sessionName, 8); + sessionName[8] = '\0'; + sessionList.append(QString(sessionName).trimmed()); + } +} + +QStringList WTSessionListResponse::getSessionList() +{ + return sessionList; +} + +void WTSessionListResponse::setSessionList(QStringList &sessionList) +{ + this->sessionList = sessionList; + sesscnt = sessionList.count(); +} + +void WTSessionListResponse::addSession(QString sessionName) +{ + sesscnt++; + sessionList.append(sessionName); +} + + + diff --git a/cbc/common/wtsessionlistresponse.h b/cbc/common/wtsessionlistresponse.h new file mode 100644 index 0000000..04a120e --- /dev/null +++ b/cbc/common/wtsessionlistresponse.h @@ -0,0 +1,24 @@ +#ifndef WTSESSIONLISTRESPONSE_H +#define WTSESSIONLISTRESPONSE_H + +#include + +class WTSessionListResponse : public WTMessage +{ +public: + explicit WTSessionListResponse(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + QStringList getSessionList(); + void setSessionList(QStringList &sessionList); + void addSession(QString sessionName); + +private: + uint sesscnt; + QStringList sessionList; + +}; + +#endif // WTSESSIONLISTRESPONSE_H diff --git a/cbc/common/wtsessionmemberupdate.cpp b/cbc/common/wtsessionmemberupdate.cpp new file mode 100644 index 0000000..5ed3933 --- /dev/null +++ b/cbc/common/wtsessionmemberupdate.cpp @@ -0,0 +1,67 @@ +#include "wtsessionmemberupdate.h" + +WTSessionMemberUpdate::WTSessionMemberUpdate() : + WTMessage() +{ + command = "SESSMUPD"; +} + +QByteArray WTSessionMemberUpdate::serialize() +{ + //Size of sessionName + size of updatedMemberCount + size of updateType + // + total size of members' usernames and IPs + msgSize += 8 + 4 + 1 + 8; + QByteArray data = WTMessage::serialize(); + data.append(sessionName.leftJustified(8, ' ').toAscii()); + data.append(updateType); + data.append(user.leftJustified(8, ' ').toAscii()); + return data; +} + +void WTSessionMemberUpdate::deserialize(QByteArray data) +{ + char sessionName[9]; + char userName[9]; + QDataStream dataStream(data); + WTMessage::deserialize(data); + //Skip header and username + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(sessionName,8); + sessionName[8] = '\0'; + dataStream.readRawData(&updateType, 1); + dataStream.readRawData(userName, 8); + userName[8] = '\0'; + this->sessionName = QString(sessionName).trimmed(); + this->user = QString(userName).trimmed(); +} + +QString WTSessionMemberUpdate::getSessionName() +{ + return this->sessionName; +} + +void WTSessionMemberUpdate::setSessionName(QString sessionName) +{ + this->sessionName = sessionName; +} + + +void WTSessionMemberUpdate::setUpdateType(char updateType) +{ + this->updateType = updateType; +} + +char WTSessionMemberUpdate::getUpdateType() +{ + return this->updateType; +} + +QString WTSessionMemberUpdate::getUser() +{ + return user; +} + +void WTSessionMemberUpdate::setUser(QString user) +{ + this->user = user; +} diff --git a/cbc/common/wtsessionmemberupdate.h b/cbc/common/wtsessionmemberupdate.h new file mode 100644 index 0000000..79858b5 --- /dev/null +++ b/cbc/common/wtsessionmemberupdate.h @@ -0,0 +1,33 @@ +#ifndef WTSESSIONMEMBERUPDATE_H +#define WTSESSIONMEMBERUPDATE_H + +#include + +#define UPDATE_SESSION_JOIN_BEGIN 0 +#define UPDATE_SESSION_JOIN_END 1 +#define UPDATE_SESSION_LEAVE 2 + +class WTSessionMemberUpdate : public WTMessage +{ +public: + explicit WTSessionMemberUpdate(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + QString getSessionName(); + void setSessionName(QString sessionName); + + char getUpdateType(); + void setUpdateType(char updateType); + + QString getUser(); + void setUser(QString user); + +private: + QString sessionName; + char updateType; + QString user; +}; + +#endif // WTSESSIONMEMBERUPDATE_H diff --git a/cbc/common/wtupdatedrawing.cpp b/cbc/common/wtupdatedrawing.cpp new file mode 100644 index 0000000..f15881a --- /dev/null +++ b/cbc/common/wtupdatedrawing.cpp @@ -0,0 +1,54 @@ +#include "wtupdatedrawing.h" + +WTUpdateDrawing::WTUpdateDrawing() : + WTMessage() +{ + command = "DRAWUPDT"; +} + +QByteArray WTUpdateDrawing::serialize() +{ + msgSize += 8 + picData.size(); + QByteArray data = WTMessage::serialize(); + data.append(sessionName.leftJustified(8, ' ').toAscii()); + data.append(picData); + return data; +} + +void WTUpdateDrawing::deserialize(QByteArray data) +{ + char sessionName[9]; + char *picData; + QDataStream dataStream(data); + WTMessage::deserialize(data); + //Skip header and username + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(sessionName,8); + sessionName[8] = '\0'; + this->sessionName = QString(sessionName).trimmed(); + //Rest of the characters belong to picture data + picData = new char[data.size()-(HEADER_SIZE+8)]; + dataStream.readRawData(picData, data.size() - (HEADER_SIZE+8)); + this->picData.setRawData(picData, data.size()-(HEADER_SIZE+8)); +} + +QString WTUpdateDrawing::getSessionName() +{ + return this->sessionName; +} + +void WTUpdateDrawing::setSessionName(QString sessionName) +{ + this->sessionName = sessionName; +} + +QByteArray WTUpdateDrawing::getPicData() +{ + return picData; +} + +void WTUpdateDrawing::setPicData(QByteArray picData) +{ + this->picData = picData; +} + diff --git a/cbc/common/wtupdatedrawing.h b/cbc/common/wtupdatedrawing.h new file mode 100644 index 0000000..a255b2c --- /dev/null +++ b/cbc/common/wtupdatedrawing.h @@ -0,0 +1,25 @@ +#ifndef WTUPDATEDRAWING_H +#define WTUPDATEDRAWING_H + +#include + +class WTUpdateDrawing : public WTMessage +{ +public: + explicit WTUpdateDrawing(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + QString getSessionName(); + void setSessionName(QString sessionName); + + QByteArray getPicData(); + void setPicData(QByteArray picData); + +private: + QString sessionName; + QByteArray picData; +}; + +#endif // WTUPDATEDRAWING_H diff --git a/cbc/common/wtupdatedrawingserver.cpp b/cbc/common/wtupdatedrawingserver.cpp new file mode 100644 index 0000000..db62496 --- /dev/null +++ b/cbc/common/wtupdatedrawingserver.cpp @@ -0,0 +1,56 @@ +#include "wtupdatedrawingserver.h" + +// exactly the same as WTUpdateDrawing but meant for the server + +WTUpdateDrawingServer::WTUpdateDrawingServer() : + WTMessage() +{ + command = "DRAWSUPD"; +} + +QByteArray WTUpdateDrawingServer::serialize() +{ + msgSize += 8 + picData.size(); + QByteArray data = WTMessage::serialize(); + data.append(sessionName.leftJustified(8, ' ').toAscii()); + data.append(picData); + return data; +} + +void WTUpdateDrawingServer::deserialize(QByteArray data) +{ + char sessionName[9]; + char *picData; + QDataStream dataStream(data); + WTMessage::deserialize(data); + //Skip header and username + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(sessionName,8); + sessionName[8] = '\0'; + this->sessionName = QString(sessionName).trimmed(); + //Rest of the characters belong to picture data + picData = new char[data.size()-(HEADER_SIZE+8)]; + dataStream.readRawData(picData, data.size() - (HEADER_SIZE+8)); + this->picData.setRawData(picData, data.size()-(HEADER_SIZE+8)); +} + +QString WTUpdateDrawingServer::getSessionName() +{ + return this->sessionName; +} + +void WTUpdateDrawingServer::setSessionName(QString sessionName) +{ + this->sessionName = sessionName; +} + +QByteArray WTUpdateDrawingServer::getPicData() +{ + return picData; +} + +void WTUpdateDrawingServer::setPicData(QByteArray picData) +{ + this->picData = picData; +} + diff --git a/cbc/common/wtupdatedrawingserver.h b/cbc/common/wtupdatedrawingserver.h new file mode 100644 index 0000000..bf641b6 --- /dev/null +++ b/cbc/common/wtupdatedrawingserver.h @@ -0,0 +1,26 @@ +#ifndef WTUPDATEDRAWINGSERVER_H +#define WTUPDATEDRAWINGSERVER_H + +#include "wtmessage.h" + +class WTUpdateDrawingServer : public WTMessage +{ +public: + explicit WTUpdateDrawingServer(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + QString getSessionName(); + void setSessionName(QString sessionName); + + QByteArray getPicData(); + void setPicData(QByteArray picData); + +private: + QString sessionName; + QByteArray picData; + +}; + +#endif // WTUPDATEDRAWINGSERVER_H diff --git a/cbc/common/wtwritepermissionrequest.cpp b/cbc/common/wtwritepermissionrequest.cpp new file mode 100644 index 0000000..6a61aa6 --- /dev/null +++ b/cbc/common/wtwritepermissionrequest.cpp @@ -0,0 +1,7 @@ +#include "wtwritepermissionrequest.h" + +WTWritePermissionRequest::WTWritePermissionRequest() : + WTMessage() +{ + command = "WRTPRMRQ"; +} diff --git a/cbc/common/wtwritepermissionrequest.h b/cbc/common/wtwritepermissionrequest.h new file mode 100644 index 0000000..e5ccfaa --- /dev/null +++ b/cbc/common/wtwritepermissionrequest.h @@ -0,0 +1,13 @@ +#ifndef WTWRITEPERMISSIONREQUEST_H +#define WTWRITEPERMISSIONREQUEST_H + +#include + +class WTWritePermissionRequest : public WTMessage +{ +public: + explicit WTWritePermissionRequest(); + +}; + +#endif // WTWRITEPERMISSIONREQUEST_H diff --git a/cbc/common/wtwritepermissionstatus.cpp b/cbc/common/wtwritepermissionstatus.cpp new file mode 100644 index 0000000..3d13e44 --- /dev/null +++ b/cbc/common/wtwritepermissionstatus.cpp @@ -0,0 +1,35 @@ +#include "wtwritepermissionstatus.h" + +WTWritePermissionStatus::WTWritePermissionStatus() : + WTMessage() +{ + command = "WRTPRMST"; +} + +QByteArray WTWritePermissionStatus::serialize() +{ + //Size of status + msgSize += 1; + QByteArray data = WTMessage::serialize(); + data.append(status); + return data; +} + +void WTWritePermissionStatus::deserialize(QByteArray data) +{ + QDataStream dataStream(data); + WTMessage::deserialize(data); + //Skip header and username + dataStream.skipRawData(HEADER_SIZE); + dataStream.readRawData(&status, 1); +} + +char WTWritePermissionStatus::getStatus() +{ + return this->status; +} + +void WTWritePermissionStatus::setStatus(char status) +{ + this->status = status; +} diff --git a/cbc/common/wtwritepermissionstatus.h b/cbc/common/wtwritepermissionstatus.h new file mode 100644 index 0000000..ee91339 --- /dev/null +++ b/cbc/common/wtwritepermissionstatus.h @@ -0,0 +1,21 @@ +#ifndef WTWRITEPERMISSIONSTATUS_H +#define WTWRITEPERMISSIONSTATUS_H + +#include + +class WTWritePermissionStatus : public WTMessage +{ +public: + explicit WTWritePermissionStatus(); + + QByteArray serialize(); + void deserialize(QByteArray data); + + char getStatus(); + void setStatus(char status); + +private: + char status; +}; + +#endif // WTWRITEPERMISSIONSTATUS_H diff --git a/cbc/server/collaboration_server.pro b/cbc/server/collaboration_server.pro new file mode 100644 index 0000000..44a79dd --- /dev/null +++ b/cbc/server/collaboration_server.pro @@ -0,0 +1,69 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2011-04-16T14:28:32 +# +#------------------------------------------------- + +QT += core gui network + +TARGET = collaboration_server +TEMPLATE = app + +COMMON_SOURCES += ../common/wtmessage.cpp \ + ../common/wtloginmessage.cpp \ + ../common/wtsessionlistrequest.cpp \ + ../common/messagetransceiver.cpp \ + ../common/wtlogoutrequest.cpp \ + ../common/wtpicturerequest.cpp \ + ../common/wtpictureresponse.cpp \ + ../common/wtsessioncreaterequest.cpp \ + ../common/wtsessioncreateresponse.cpp \ + ../common/wtsessionjoinrequest.cpp \ + ../common/wtsessionjoinresponse.cpp \ + ../common/wtsessionleaverequest.cpp \ + ../common/wtsessionleaveresponse.cpp \ + ../common/wtsessionlistresponse.cpp \ + ../common/wtsessionmemberupdate.cpp \ + ../common/wtupdatedrawing.cpp \ + ../common/wtwritepermissionrequest.cpp \ + ../common/wtwritepermissionstatus.cpp \ + ../common/wtloginresponse.cpp \ + ../common/protocolhandler.cpp \ + ../common/wtpeerhandshake.cpp \ + ../common/collaborationsession.cpp \ + ../common/wtupdatedrawingserver.cpp \ + +COMMON_HEADERS += ../common/wtmessage.h \ + ../common/wtloginmessage.h \ + ../common/wtsessionlistrequest.h \ + ../common/messagetransceiver.h \ + ../common/wtlogoutrequest.h \ + ../common/wtpicturerequest.h \ + ../common/wtpictureresponse.h \ + ../common/wtsessioncreaterequest.h \ + ../common/wtsessioncreateresponse.h \ + ../common/wtsessionjoinrequest.h \ + ../common/wtsessionjoinresponse.h \ + ../common/wtsessionleaverequest.h \ + ../common/wtsessionleaveresponse.h \ + ../common/wtsessionlistresponse.h \ + ../common/wtsessionmemberupdate.h \ + ../common/wtupdatedrawing.h \ + ../common/wtwritepermissionrequest.h \ + ../common/wtwritepermissionstatus.h \ + ../common/wtloginresponse.h \ + ../common/protocolhandler.h \ + ../common/wtpeerhandshake.h \ + ../common/collaborationsession.h \ + ../common/wtupdatedrawingserver.h + +SOURCES += main.cpp \ + collaborationserver.cpp \ + $$COMMON_SOURCES + + +HEADERS += collaborationserver.h \ + $$COMMON_HEADERS + +INCLUDEPATH += . \ + ../common diff --git a/cbc/server/collaborationserver.cpp b/cbc/server/collaborationserver.cpp new file mode 100644 index 0000000..fc86904 --- /dev/null +++ b/cbc/server/collaborationserver.cpp @@ -0,0 +1,280 @@ +#include "collaborationserver.h" + +// the CollaborationServer class acts as the backbone of the p2p/server-client +// hybrid collaboration architecture. its primary responsibilities are: +// - broadcast its own IP to the local network over UDP 45455 so that clients +// can find it +// - keep track of active clients via login/logout messages +// - keep track of collaboration sessions and their participants, store the current +// state of the collaborative drawing + +// all actual communication is meant to be handled by the ProtocolHandler class, +// via signal/slot connections to the CollaborationServer + +CollaborationServer::CollaborationServer(QObject *parent) : + QObject(parent) +{ + m_protocolHandler = NULL; + m_serverUserName = COLLABORATION_SERVER_NAME; + connect(&serviceBroadcastTimer, SIGNAL(timeout()), this, SLOT(serviceBroadcastTimeout())); + serviceBroadcastTimer.start(SERVICE_BROADCAST_PERIOD_MS); +} + +void CollaborationServer::receivedLoginRequest(QString userName) +{ + // the specified user wants to log in + // check if there is a user with this username already logged in + if(m_userList.contains(userName)) { + // user with this name already logged in + qWarning() << userName << "requested to log in but failed, already exists"; + emit sendLoginResponse(userName, 0, "User " + userName + " already exists!"); + return; + } else { + // add user to list of logged in users + m_userList.append(userName); + qWarning() << userName << "logged in successfully"; + qWarning() << "list of inloggad users" << m_userList; + emit sendLoginResponse(userName, 1, "Login for user " + userName + " successful!"); + } +} + +void CollaborationServer::receivedLogoutRequest(QString userName) +{ + // the specified user wants to log out + // check if there is a user with this username already logged in + if(m_userList.contains(userName)) { + // user with this name already logged in + // so we can log him/her out + // TODO remove user from existing sessions and do necessary updates + m_userList.removeOne(userName); + qWarning() << userName << "logged out successfully"; + return; + } else { + // user is not in the logged in list, can't log out + // do nothing + qWarning() << userName << "requested to log out but was not logged in!"; + return; + } +} + +void CollaborationServer::receivedPictureRequest(QString userName, QString sessionName) +{ + // first, check if the session with given name exists + if(!m_sessionList.contains(sessionName)) { + // no such session + qWarning() << "receivedPictureRequest error: session" << sessionName << "does not exist!"; + // TODO send error reply to client + return; + } + + // now check if this user has joined this session + if(!m_sessionData[sessionName]->getSessionParticipants().contains(userName)) { + // user is not a member of this sesion + qWarning() << "receivedPictureRequest error: user" << userName << "is not a member of" << sessionName; + return; + } + + // everything OK, we can send the session drawing state + QPicture tmpPic = m_sessionData[sessionName]->getSessionDrawingState(); + + qWarning() << "sending picture response" << userName << sessionName << tmpPic.size(); + + emit sendPictureResponse(userName, sessionName, QByteArray::fromRawData(tmpPic.data(), tmpPic.size())); + + + //TODO How do we know sending the picture has been completed? + // send sessionMember update to the other clients in the session + + QHash::iterator itr; + QHash clients = m_sessionData[sessionName]->getSessionParticipants(); + for (itr = clients.begin(); itr != clients.end(); itr++) + { + if(itr.key() != userName) + emit sendSessionMemberUpdate(itr.key(), sessionName, UPDATE_SESSION_JOIN_END, userName); + } +} + +void CollaborationServer::receivedSessionJoinRequest(QString userName, QString sessionName, QString password) +{ + QHostAddress userAddress(m_protocolHandler->getUserMapping(userName)); + // first, check if the session with given name exists + if(!m_sessionList.contains(sessionName)) { + // no such session + qWarning() << "receivedSessionJoinRequest error: session" << sessionName << "does not exist!"; + // TODO send error reply to client + return; + } + + if(m_sessionData[sessionName]->addSessionParticipant(userName, password, userAddress.toIPv4Address())) { + // user successfully joined the session + QHash participants = m_sessionData[sessionName]->getSessionParticipants(); + // send session member update to all other clients + QHashIterator i(participants); + while (i.hasNext()) { + i.next(); + if(i.key() != userName) + emit sendSessionMemberUpdate(i.key(), sessionName, UPDATE_SESSION_JOIN_BEGIN, userName); + } + // send join response to the newly joined client + emit sendSessionJoinResponse(userName, sessionName, 1, participants); + return; + } else { + // there was a problem with the user joining the session + emit sendSessionJoinResponse(userName, sessionName, 0, QHash()); + qWarning() << "receivedSessionJoinRequest error: user" << userName << "could not join session" << sessionName; + // TODO send error message to client + return; + } +} + +void CollaborationServer::receivedSessionLeaveRequest(QString userName, QString sessionName) +{ + //Remove the user from the list of that session + QHash *userList = &(m_sessionData[sessionName]->getSessionParticipants()); + userList->remove(userName); + + //Warn each user in the aforementioned session that a user left + QHash::iterator iter; + for (iter = userList->begin(); iter != userList->end(); iter++) + { + emit sendSessionMemberUpdate(iter.key(), sessionName, UPDATE_SESSION_LEAVE, userName); + } + //TODO Probably to be removed, no need for acknowledgement + emit sendSessionLeaveResponse(userName, sessionName, 1); +} + +void CollaborationServer::receivedSessionListRequest(QString userName) +{ + qWarning() << userName << "wants list of sessions, sending" << m_sessionList; + emit sendSessionListResponse(userName, m_sessionList); +} + +void CollaborationServer::receivedUpdateDrawingServer(QString userName, QString sessionName, QByteArray picData) +{ + qWarning() << "received update drawing" << userName << sessionName << picData.size(); + // first, check if the session with given name exists + if(!m_sessionList.contains(sessionName)) { + // no such session + qWarning() << "receivedUpdateDrawingServer error: session" << sessionName << "does not exist!"; + // TODO send error reply to client + return; + } + + // now check if this user has joined this session + if(!m_sessionData[sessionName]->getSessionParticipants().contains(userName)) { + // user is not a member of this sesion + qWarning() << "receivedUpdateDrawingServer error: user" << userName << "is not a member of" << sessionName; + return; + } + + // everything OK + QPicture tmpPic; + tmpPic.setData(picData.constData(), picData.length()); + // send data to session + m_sessionData[sessionName]->addDrawingStep(tmpPic); +} + +void CollaborationServer::receivedWritePermissionRequest(QString userName) +{ + // TODO +} + +void CollaborationServer::receivedSessionCreateRequest(QString userName, QString sessionName, QString password) +{ + //Check if a session with the same name exists + if (m_sessionList.contains(sessionName)) + { + //The session already exists, respond negative + emit sendSessionCreateResponse(userName, sessionName, 0, ""); + return; + } + + //Create session with the given name + m_sessionList.append(sessionName); + m_sessionData[sessionName] = new CollaborationSession(); + m_sessionData[sessionName]->setSessionName(sessionName); + m_sessionData[sessionName]->setSessionPassword(password); + qWarning() << "The password arrived at the server : " << password; + + //Session creation is successful, send a positive response + emit sendSessionCreateResponse(userName, sessionName, 1, password); +} + + +// sets the ProtocolHandler for this CollaborationServer +void CollaborationServer::setProtocolHandler(ProtocolHandler * newProtocolHandler) +{ + if(!newProtocolHandler) { + qWarning() << "Cannot set a null protocol handler for CollaborationServer!"; + return; + } + + if(m_protocolHandler) { + // disconnect all signals and slots from previous protocol handler + disconnect(this); + disconnect(m_protocolHandler); + } + // connect signals and slots for new protocol handler + // signals from server to the protocol handler slots + connect(this, SIGNAL(sendLoginResponse(QString,char,QString)), newProtocolHandler, SLOT(sendLoginResponse(QString,char,QString))); + connect(this, SIGNAL(sendPictureResponse(QString,QString,QByteArray)), newProtocolHandler, SLOT(sendPictureResponse(QString,QString,QByteArray))); + connect(this, SIGNAL(sendSessionJoinResponse(QString,QString,char,QHash)), newProtocolHandler, SLOT(sendSessionJoinResponse(QString,QString,char,QHash))); + connect(this, SIGNAL(sendSessionLeaveResponse(QString,QString,char)), newProtocolHandler, SLOT(sendSessionLeaveResponse(QString,QString,char))); + connect(this, SIGNAL(sendSessionListResponse(QString,QStringList)), newProtocolHandler, SLOT(sendSessionListResponse(QString,QStringList))); + connect(this, SIGNAL(sendSessionMemberUpdate(QString,QString,char,QString)), newProtocolHandler, SLOT(sendSessionMemberUpdate(QString,QString,char,QString))); + connect(this, SIGNAL(sendWritePermissionStatus(QString,QChar)),newProtocolHandler, SLOT(sendWritePermissionStatus(QString,QChar))); + connect(this, SIGNAL(sendSessionCreateResponse(QString,QString,QChar,QString)), newProtocolHandler, SLOT(sendSessionCreateResponse(QString,QString,QChar,QString))); + + // signals from protocol handler to server slots + connect(newProtocolHandler, SIGNAL(receivedLoginRequest(QString)), this, SLOT(receivedLoginRequest(QString))); + connect(newProtocolHandler, SIGNAL(receivedLogoutRequest(QString)), this, SLOT(receivedLogoutRequest(QString))); + connect(newProtocolHandler, SIGNAL(receivedPictureRequest(QString,QString)), this, SLOT(receivedPictureRequest(QString,QString))); + connect(newProtocolHandler, SIGNAL(receivedSessionJoinRequest(QString,QString,QString)), this, SLOT(receivedSessionJoinRequest(QString,QString,QString))); + connect(newProtocolHandler, SIGNAL(receivedSessionLeaveRequest(QString,QString)), this, SLOT(receivedSessionLeaveRequest(QString,QString))); + connect(newProtocolHandler, SIGNAL(receivedSessionListRequest(QString)), this, SLOT(receivedSessionListRequest(QString))); + connect(newProtocolHandler, SIGNAL(receivedUpdateDrawingServer(QString,QString,QByteArray)), this, SLOT(receivedUpdateDrawingServer(QString,QString,QByteArray))); + connect(newProtocolHandler, SIGNAL(receivedWritePermissionRequest(QString)), this, SLOT(receivedWritePermissionRequest(QString))); + connect(newProtocolHandler, SIGNAL(receivedSessionCreateRequest(QString,QString,QString)), this, SLOT(receivedSessionCreateRequest(QString,QString,QString))); + + // set protocol handler user name + newProtocolHandler->setUserName(m_serverUserName); + + m_protocolHandler = newProtocolHandler; +} + +ProtocolHandler * CollaborationServer::getProtocolHandler() +{ + return m_protocolHandler; +} + +// broadcast the server's address(es) over UDP at certain intervals +// so that clients in the network can auto-discover the server +void CollaborationServer::serviceBroadcastTimeout() +{ + QByteArray broadcastPackage; + QDataStream packageStream(&broadcastPackage, QIODevice::ReadWrite); + packageStream << QString("WTCOLSRV"); + packageStream << m_serverUserName; + QNetworkInterface interface; + QList IpList = interface.allAddresses(); + + for (int i = 0; i < IpList.size(); i++) + if (IpList.at(i) != QHostAddress("127.0.0.1") && IpList.at(i).protocol() == QAbstractSocket::IPv4Protocol) { // local loopback isn't useful for others + packageStream << IpList.at(i); + } + + serviceBroadcastSocket.writeDatagram(broadcastPackage, QHostAddress::Broadcast, SERVICE_BROADCAST_PORT); +} + +void CollaborationServer::setServerUserName(QString newUserName) +{ + if(m_protocolHandler) + m_protocolHandler->setUserName(newUserName); + + m_serverUserName = newUserName; +} + +QString CollaborationServer::getServerUserName() +{ + return m_serverUserName; +} diff --git a/cbc/server/collaborationserver.h b/cbc/server/collaborationserver.h new file mode 100644 index 0000000..30f3aee --- /dev/null +++ b/cbc/server/collaborationserver.h @@ -0,0 +1,63 @@ +#ifndef COLLABORATIONSERVER_H +#define COLLABORATIONSERVER_H + +#include +#include +#include + +#include "protocolhandler.h" +#include "collaborationsession.h" + +//TODO move into a common header file with client +#define COLLABORATION_SERVER_NAME "$SERVER$" +#define SERVICE_BROADCAST_PERIOD_MS 1000 +#define SERVICE_BROADCAST_PORT 45455 + +class CollaborationServer : public QObject +{ + Q_OBJECT +public: + explicit CollaborationServer(QObject *parent = 0); + + void setProtocolHandler(ProtocolHandler * newProtocolHandler); + ProtocolHandler * getProtocolHandler(); + + void setServerUserName(QString newUserName); + QString getServerUserName(); + +protected: + QString m_serverUserName; + QList m_userList; + QList m_sessionList; + ProtocolHandler * m_protocolHandler; + QUdpSocket serviceBroadcastSocket; + QTimer serviceBroadcastTimer; + QHash m_sessionData; + + +signals: + void sendLoginResponse(QString destUserName, char result, QString infoMsg); + void sendPictureResponse(QString destUserName, QString sessionName, QByteArray picData); + void sendSessionCreateResponse(QString destUserName, QString sessionName, QChar result, QString password); + void sendSessionJoinResponse(QString destUserName, QString sessionName, char result, QHash users); + void sendSessionLeaveResponse(QString destUserName, QString sessionName, char result); + void sendSessionListResponse(QString destUserName, QStringList sessionList); + void sendSessionMemberUpdate(QString destUserName, QString sessionName, char updateType, QString users); + void sendWritePermissionStatus(QString destUserName, QChar status); + +public slots: + void receivedLoginRequest(QString userName); + void receivedLogoutRequest(QString userName); + void receivedPictureRequest(QString userName, QString sessionName); + void receivedSessionCreateRequest(QString userName, QString sessionName, QString password); + void receivedSessionJoinRequest(QString userName, QString sessionName, QString password); + void receivedSessionLeaveRequest(QString userName, QString sessionName); + void receivedSessionListRequest(QString userName); + void receivedUpdateDrawingServer(QString userName, QString sessionName, QByteArray picData); + void receivedWritePermissionRequest(QString userName); + +private slots: + void serviceBroadcastTimeout(); +}; + +#endif // COLLABORATIONSERVER_H diff --git a/cbc/server/main.cpp b/cbc/server/main.cpp new file mode 100644 index 0000000..3101532 --- /dev/null +++ b/cbc/server/main.cpp @@ -0,0 +1,20 @@ +#include +#include "messagetransceiver.h" +#include "protocolhandler.h" +#include "collaborationserver.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + MessageTransceiver m; + ProtocolHandler p; + CollaborationServer cs; + + p.setMessageTransceiver(&m); + cs.setProtocolHandler(&p); + + m.run(); + + return a.exec(); +} diff --git a/iwbc_demo/iwbc_demo.pro b/iwbc_demo/iwbc_demo.pro index 8c3833f..2848394 100644 --- a/iwbc_demo/iwbc_demo.pro +++ b/iwbc_demo/iwbc_demo.pro @@ -43,8 +43,7 @@ SOURCES += main.cpp\ videounderlay.cpp \ videocontrolpanel.cpp \ webpagedisplaywidget.cpp \ - webcontrolpanel.cpp \ - qmlmenulayer.cpp + webcontrolpanel.cpp HEADERS += mainwindow.h \ contentdisplay.h \ @@ -76,8 +75,7 @@ HEADERS += mainwindow.h \ videounderlay.h \ videocontrolpanel.h \ webpagedisplaywidget.h \ - webcontrolpanel.h \ - qmlmenulayer.h + webcontrolpanel.h FORMS += mainwindow.ui \ diff --git a/iwbc_demo/mainwindow.cpp b/iwbc_demo/mainwindow.cpp index 6b269f6..6012218 100644 --- a/iwbc_demo/mainwindow.cpp +++ b/iwbc_demo/mainwindow.cpp @@ -38,11 +38,11 @@ MainWindow::MainWindow(QWidget *parent) : groupBoxForPresentation = new QWidget(this); - qmlMenu = new QmlMenuLayer(this); + //qmlMenu = new QMLMenuLayer(this); QStackedLayout *layout = new QStackedLayout(); - layout->addWidget(qmlMenu); + //layout->addWidget(qmlMenu); layout->addWidget(display); layout->addWidget(draw); @@ -51,7 +51,7 @@ MainWindow::MainWindow(QWidget *parent) : layout->setStackingMode(QStackedLayout::StackAll); layout->setAlignment(display, Qt::AlignHCenter); layout->setAlignment(draw, Qt::AlignHCenter); - layout->setAlignment(qmlMenu, Qt::AlignHCenter); + //layout->setAlignment(qmlMenu, Qt::AlignHCenter); groupBoxForPresentation->setLayout(layout); @@ -308,7 +308,7 @@ void MainWindow::openContent() if(widgetStack->width() < 800) widgetStack->resize(800, widgetStack->height()); draw->raise(); - qmlMenu->raise(); + //qmlMenu->raise(); } else if(selectedContent.endsWith("mp4") || selectedContent.endsWith("avi") || selectedContent.endsWith("flv")) { diff --git a/iwbc_demo/mainwindow.h b/iwbc_demo/mainwindow.h index fbce350..6228ebb 100644 --- a/iwbc_demo/mainwindow.h +++ b/iwbc_demo/mainwindow.h @@ -20,7 +20,6 @@ #include "webpagedisplaywidget.h" #include "webcontrolpanel.h" -#include "qmlmenulayer.h" namespace Ui { class MainWindow; @@ -36,7 +35,7 @@ class MainWindow : public QMainWindow ~MainWindow(); private: - QmlMenuLayer *qmlMenu; + Ui::MainWindow *ui; PresentationDisplayWidget *display;