diff --git a/Polyhedron/demo/Polyhedron/CMakeLists.txt b/Polyhedron/demo/Polyhedron/CMakeLists.txt index 9e00808a516..32da38d5618 100644 --- a/Polyhedron/demo/Polyhedron/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/CMakeLists.txt @@ -402,6 +402,13 @@ add_executable ( CGAL_PMP PMP.cpp ) add_dependencies(CGAL_PMP PMP) target_link_libraries( CGAL_PMP PRIVATE polyhedron_demo ) add_to_cached_list( CGAL_EXECUTABLE_TARGETS CGAL_PMP ) + +#WS Server +if(TARGET Qt5::WebSockets) + add_executable (WS_server Server_ws.cpp) + target_link_libraries(WS_server PUBLIC Qt5::WebSockets) + message(STATUS "Qt5WebSockets was found. Using WebSockets is therefore possible.") +endif() # # Exporting # diff --git a/Polyhedron/demo/Polyhedron/MainWindow.cpp b/Polyhedron/demo/Polyhedron/MainWindow.cpp index bd370cd7827..ee2274122fe 100644 --- a/Polyhedron/demo/Polyhedron/MainWindow.cpp +++ b/Polyhedron/demo/Polyhedron/MainWindow.cpp @@ -77,12 +77,6 @@ #include "Color_map.h" -#ifdef CGAL_USE_WEBSOCKETS -#include -#include -#include -#endif - using namespace CGAL::Three; QScriptValue myScene_itemToScriptValue(QScriptEngine *engine, @@ -2940,25 +2934,12 @@ void MainWindow::on_actionSa_ve_Scene_as_Script_triggered() bool do_upload = false; #ifdef CGAL_USE_SSH QString user = settings.value("ssh_user", QString()).toString(); - QString pass; if(!user.isEmpty()) { QMessageBox::StandardButton doyou = QMessageBox::question(this, tr("Upload ?"), tr("Do you wish to upload the scene" " using the SSH preferences ?")); - bool ok; do_upload = (doyou == QMessageBox::Yes); - if(do_upload) - { - pass = QInputDialog::getText(this, "SSH Password", - "Enter ssh key password:", - QLineEdit::Password, - tr(""), - &ok); - if(!ok) - return; - pass = pass.trimmed(); - } } #endif @@ -3109,12 +3090,31 @@ void MainWindow::on_actionSa_ve_Scene_as_Script_triggered() path.prepend("Polyhedron_demo_"); try{ ssh_session session; - bool res = establish_ssh_session(session, - user.toStdString().c_str(), - server.toStdString().c_str(), - pk.toStdString().c_str(), - privK.toStdString().c_str(), - pass.toStdString().c_str()); + bool res = establish_ssh_session_from_agent(session, + user.toStdString().c_str(), + server.toStdString().c_str(), + pk.toStdString().c_str()); + + if(!res) + { + bool ok; + QString pass; + pass = QInputDialog::getText(this, "SSH Password", + "Enter ssh key password:", + QLineEdit::Password, + tr(""), + &ok); + if(!ok) + return; + pass = pass.trimmed(); + res = establish_ssh_session(session, + user.toStdString().c_str(), + server.toStdString().c_str(), + pk.toStdString().c_str(), + privK.toStdString().c_str(), + pass.toStdString().c_str()); + } + if(!res) { QMessageBox::warning(this, @@ -3590,25 +3590,13 @@ void MainWindow::on_actionLoad_a_Scene_from_a_Script_File_triggered() #ifdef CGAL_USE_SSH QString user = settings.value("ssh_user", QString()).toString(); - QString pass; + if(!user.isEmpty()) { QMessageBox::StandardButton doyou = QMessageBox::question(this, tr("Download ?"), tr("Do you wish to download the scene" " using the SSH preferences ?")); - bool ok; do_download= (doyou == QMessageBox::Yes); - if(do_download) - { - pass = QInputDialog::getText(this, "SSH Password", - "Enter ssh key password:", - QLineEdit::Password, - tr(""), - &ok); - if(!ok) - return; - pass = pass.trimmed(); - } } #endif @@ -3623,22 +3611,30 @@ void MainWindow::on_actionLoad_a_Scene_from_a_Script_File_triggered() server = server.trimmed(); pk = pk.trimmed(); privK=privK.trimmed(); - QString path; - path = QInputDialog::getText(this, - "", - tr("Enter the name of the scene file.")); - if(path.isEmpty()) - return; - if(!path.contains("Polyhedron_demo_")) - path.prepend("Polyhedron_demo_"); + try{ ssh_session session; - bool res = establish_ssh_session(session, - user.toStdString().c_str(), - server.toStdString().c_str(), - pk.toStdString().c_str(), - privK.toStdString().c_str(), - pass.toStdString().c_str()); + bool res = establish_ssh_session_from_agent(session, + user.toStdString().c_str(), + server.toStdString().c_str(), + pk.toStdString().c_str()); + if(!res){ + bool ok; + QString pass= QInputDialog::getText(this, "SSH Password", + "Enter ssh key password:", + QLineEdit::Password, + tr(""), + &ok); + if(!ok) + return; + pass = pass.trimmed(); + res = establish_ssh_session(session, + user.toStdString().c_str(), + server.toStdString().c_str(), + pk.toStdString().c_str(), + privK.toStdString().c_str(), + pass.toStdString().c_str()); + } if(!res) { QMessageBox::warning(this, @@ -3646,7 +3642,22 @@ void MainWindow::on_actionLoad_a_Scene_from_a_Script_File_triggered() "The SSH session could not be started."); return; } + QStringList names; + if(!CGAL::ssh_internal::explore_the_galaxy(session, names)) + { + QMessageBox::warning(this, + "Error", + "Could not find remote directory."); + } + QString path; + path = QInputDialog::getItem(this, + "Choose a file", + tr("Choose the scene file."), + names); filename = QString("%1/load_scene.js").arg(QDir::tempPath()); + if(path.isEmpty()) + return; + path.prepend("Polyhedron_demo_"); path = tr("/tmp/%2").arg(path); res = pull_file(session,path.toStdString().c_str(), filename.toStdString().c_str()); if(!res) @@ -3681,90 +3692,3 @@ void MainWindow::on_actionLoad_a_Scene_from_a_Script_File_triggered() tmp_file.remove(); } } - -#ifdef CGAL_USE_WEBSOCKETS -void MainWindow::on_action_Start_a_Session_triggered() -{ - QAction * action= findChild("action_Start_a_Session"); - static EchoServer *server =nullptr; - if(action->isChecked()){ - server = new EchoServer(1234); - QObject::connect(server, &EchoServer::closed, server,&EchoServer::deleteLater); - } - else - { - server->deleteLater(); - } -} - -EchoServer::EchoServer(quint16 port) : - QObject(CGAL::Three::Three::mainWindow()), - m_pWebSocketServer(new QWebSocketServer(QStringLiteral("Echo Server"), - QWebSocketServer::NonSecureMode, this)) -{ - if (m_pWebSocketServer->listen(QHostAddress::Any, port)) { - connect(m_pWebSocketServer, &QWebSocketServer::newConnection, - this, &EchoServer::onNewConnection); - connect(m_pWebSocketServer, &QWebSocketServer::closed, this, &EchoServer::closed); - } - QHostAddress local_host("0.0.0.0"); - - //to avoid printing 127.0.0.1. Not realy sure it won't ever print the external ipv4 though. - const QHostAddress &localhost = QHostAddress(QHostAddress::LocalHost); - for (const QHostAddress &address: QNetworkInterface::allAddresses()) { - if (address.protocol() == QAbstractSocket::IPv4Protocol && address != localhost) - { - local_host= address; - break; - } - } - QMessageBox mb(QMessageBox::NoIcon, "WS Server", - tr("WebSockets Server started.\nEnter the following address in\nyour Network Preferences to be able to join it :\n" - "ws://%1:%2").arg(local_host.toString()).arg(port), QMessageBox::Ok, CGAL::Three::Three::mainWindow()); - mb.setTextInteractionFlags(Qt::TextSelectableByMouse); - mb.exec(); -} - -EchoServer::~EchoServer() -{ - m_pWebSocketServer->close(); - qDeleteAll(m_clients.begin(), m_clients.end()); -} - -void EchoServer::onNewConnection() -{ - QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection(); - - connect(pSocket, &QWebSocket::textMessageReceived, this, &EchoServer::processTextMessage); - connect(pSocket, &QWebSocket::binaryMessageReceived, this, &EchoServer::processBinaryMessage); - connect(pSocket, &QWebSocket::disconnected, this, &EchoServer::socketDisconnected); - - m_clients << pSocket; -} - -void EchoServer::processTextMessage(QString message) -{ - QWebSocket *pClient = qobject_cast(sender()); - for(auto *client : m_clients) { - if(client != pClient) - client->sendTextMessage(message); - } -} - -void EchoServer::processBinaryMessage(QByteArray message) -{ - QWebSocket *pClient = qobject_cast(sender()); - if (pClient) { - pClient->sendBinaryMessage(message); - } -} - -void EchoServer::socketDisconnected() -{ - QWebSocket *pClient = qobject_cast(sender()); - if (pClient) { - m_clients.removeAll(pClient); - pClient->deleteLater(); - } -} -#endif diff --git a/Polyhedron/demo/Polyhedron/MainWindow.h b/Polyhedron/demo/Polyhedron/MainWindow.h index 4e544c08378..8f681a3b3c8 100644 --- a/Polyhedron/demo/Polyhedron/MainWindow.h +++ b/Polyhedron/demo/Polyhedron/MainWindow.h @@ -354,10 +354,6 @@ protected Q_SLOTS: void save(QString filename, QList& to_save); //!Calls the function saveSnapShot of the viewer. void on_actionSaveSnapshot_triggered(); -#ifdef CGAL_USE_WEBSOCKETS - //!Starts a new WS server if none is already exist. Else, does nothing. - void on_action_Start_a_Session_triggered(); -#endif //!Opens a Dialog to choose a color and make it the background color. void setBackgroundColor(); //!Opens a Dialog to change the lighting settings @@ -501,30 +497,5 @@ protected: private: bool is_main; }; -#ifdef CGAL_USE_WEBSOCKETS -QT_FORWARD_DECLARE_CLASS(QWebSocketServer) -QT_FORWARD_DECLARE_CLASS(QWebSocket) -class EchoServer : public QObject -{ - Q_OBJECT -public: - explicit EchoServer(quint16 port); - ~EchoServer(); - - -Q_SIGNALS: - void closed(); - -private Q_SLOTS: - void onNewConnection(); - void processTextMessage(QString message); - void processBinaryMessage(QByteArray message); - void socketDisconnected(); - -private: - QWebSocketServer *m_pWebSocketServer; - QList m_clients; -}; -#endif #endif // ifndef MAINWINDOW_H diff --git a/Polyhedron/demo/Polyhedron/MainWindow.ui b/Polyhedron/demo/Polyhedron/MainWindow.ui index 6273a37bd16..a56c2c1afe8 100644 --- a/Polyhedron/demo/Polyhedron/MainWindow.ui +++ b/Polyhedron/demo/Polyhedron/MainWindow.ui @@ -97,7 +97,6 @@ - @@ -470,17 +469,6 @@ Load a Scene &from a Script File... - - - true - - - &Start a Session - - - Start a WebSocket Server to Share your Camera with Others on your Network. - - diff --git a/Polyhedron/demo/Polyhedron/Plugins/PCA/Basic_generator_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PCA/Basic_generator_plugin.cpp index 184770f685b..4215c75421b 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PCA/Basic_generator_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PCA/Basic_generator_plugin.cpp @@ -59,7 +59,7 @@ public : QMenu* menu = menuFile->findChild("menuGenerateObject"); if(!menu){ QAction* actionLoad = mw->findChild("actionLoadPlugin"); - menu = new QMenu(tr("Generate &Objet"), menuFile); + menu = new QMenu(tr("Generate &Object"), menuFile); menu->setObjectName("menuGenerateObject"); menuFile->insertMenu(actionLoad, menu); } diff --git a/Polyhedron/demo/Polyhedron/Plugins/Point_set/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/Point_set/CMakeLists.txt index d29ca1cf7b8..b5a7c159731 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Point_set/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/Plugins/Point_set/CMakeLists.txt @@ -59,7 +59,7 @@ if(EIGEN3_FOUND) CGAL_target_use_pointmatcher(register_point_sets_plugin) endif() else() - message(STATUS "NOTICE: OpenGR and libpointmatcher were not found. Registrationp plugin will not be available.") + message(STATUS "NOTICE: OpenGR and libpointmatcher were not found. Registration plugin will not be available.") endif() else(EIGEN3_FOUND) diff --git a/Polyhedron/demo/Polyhedron/Server_ws.cpp b/Polyhedron/demo/Polyhedron/Server_ws.cpp new file mode 100644 index 00000000000..82bea309a37 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Server_ws.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include "Server_ws.h" + +#include + + +EchoServer::EchoServer(quint16 port) : + QObject(), + m_pWebSocketServer(new QWebSocketServer(QStringLiteral("Echo Server"), + QWebSocketServer::NonSecureMode, this)) +{ + if (m_pWebSocketServer->listen(QHostAddress::Any, port)) { + connect(m_pWebSocketServer, &QWebSocketServer::newConnection, + this, &EchoServer::onNewConnection); + connect(m_pWebSocketServer, &QWebSocketServer::closed, this, &EchoServer::closed); + } + QHostAddress local_host("0.0.0.0"); + + //to avoid printing 127.0.0.1. Not realy sure it won't ever print the external ipv4 though. + const QHostAddress &localhost = QHostAddress(QHostAddress::LocalHost); + for (const QHostAddress &address: QNetworkInterface::allAddresses()) { + if (address.protocol() == QAbstractSocket::IPv4Protocol && address != localhost) + { + local_host= address; + break; + } + } + QMessageBox mb(QMessageBox::NoIcon, "WS Server", + tr("WebSockets Server started.\nEnter the following address in\nyour Network Preferences to be able to join it :\n" + "ws://%1:%2").arg(local_host.toString()).arg(port), QMessageBox::Ok); + mb.setTextInteractionFlags(Qt::TextSelectableByMouse); + mb.exec(); +} + +EchoServer::~EchoServer() +{ + m_pWebSocketServer->close(); + qDeleteAll(m_clients.begin(), m_clients.end()); +} + +void EchoServer::onNewConnection() +{ + QWebSocket *pSocket = m_pWebSocketServer->nextPendingConnection(); + + connect(pSocket, &QWebSocket::textMessageReceived, this, &EchoServer::processTextMessage); + connect(pSocket, &QWebSocket::binaryMessageReceived, this, &EchoServer::processBinaryMessage); + connect(pSocket, &QWebSocket::disconnected, this, &EchoServer::socketDisconnected); + + m_clients << pSocket; +} + +void EchoServer::processTextMessage(QString message) +{ + QWebSocket *pClient = qobject_cast(sender()); + for(auto *client : m_clients) { + if(client != pClient) + client->sendTextMessage(message); + } +} + +void EchoServer::processBinaryMessage(QByteArray message) +{ + QWebSocket *pClient = qobject_cast(sender()); + if (pClient) { + pClient->sendBinaryMessage(message); + } +} + +void EchoServer::socketDisconnected() +{ + QWebSocket *pClient = qobject_cast(sender()); + if (pClient) { + m_clients.removeAll(pClient); + pClient->deleteLater(); + } +} + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QCommandLineParser parser; + parser.setApplicationDescription("WS Server"); + parser.addHelpOption(); + QCommandLineOption portOption(QStringList() << "p" << "port", + QCoreApplication::translate("main", "Port for echoserver [default: 1234]."), + QCoreApplication::translate("main", "port"), QLatin1Literal("1234")); + parser.addOption(portOption); + parser.process(a); + int port = parser.value(portOption).toInt(); + EchoServer *server = new EchoServer(port); + QObject::connect(server, &EchoServer::closed, &a, &QCoreApplication::quit); + + return a.exec(); +} + diff --git a/Polyhedron/demo/Polyhedron/Server_ws.h b/Polyhedron/demo/Polyhedron/Server_ws.h new file mode 100644 index 00000000000..fe94f6b2d08 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Server_ws.h @@ -0,0 +1,30 @@ +#ifndef SERVER_WS_H +#define SERVER_WS_H + +#include +#include +#include + +class EchoServer : public QObject +{ + Q_OBJECT +public: + explicit EchoServer(quint16 port); + ~EchoServer(); + + +Q_SIGNALS: + void closed(); + +private Q_SLOTS: + void onNewConnection(); + void processTextMessage(QString message); + void processBinaryMessage(QByteArray message); + void socketDisconnected(); + +private: + QWebSocketServer *m_pWebSocketServer; + QList m_clients; +}; + +#endif // SERVER_WS_H diff --git a/Polyhedron/demo/Polyhedron/Use_ssh.cpp b/Polyhedron/demo/Polyhedron/Use_ssh.cpp index 8acc1801b8e..028909251c2 100644 --- a/Polyhedron/demo/Polyhedron/Use_ssh.cpp +++ b/Polyhedron/demo/Polyhedron/Use_ssh.cpp @@ -20,8 +20,10 @@ #include #include #include +#include #include +#include bool test_result(int res) { @@ -135,6 +137,79 @@ bool establish_ssh_session(ssh_session &session, return true; } + +bool establish_ssh_session_from_agent(ssh_session& session, + const char *user, + const char *server, + const char *pub_key_path) +{ + int port = 22; + + //Can use SSH_LOG_PROTOCOL here for verbose output + int verbosity = SSH_LOG_NOLOG; + int res; + //retry 4 times max each time the connection asks to be retried. + for(int k = 0; k < 4; ++k) + { + session = ssh_new(); + ssh_options_set( session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity ); + ssh_options_set( session, SSH_OPTIONS_PORT, &port ); + ssh_options_set( session, SSH_OPTIONS_USER, user ); + ssh_options_set( session, SSH_OPTIONS_HOST, server); + + ssh_connect(session); +#if LIBSSH_VERSION_MAJOR <1 && LIBSSH_VERSION_MINOR < 8 + if( ssh_is_server_known(session) != SSH_SERVER_KNOWN_OK ) +#else + if( ssh_session_is_known_server(session) != SSH_KNOWN_HOSTS_OK ) +#endif + { + if(QMessageBox::warning(CGAL::Three::Three::mainWindow(), QString("Unknown Server"), + QString ("The server you are trying to join is not known.\n" + "Do you wish to add it to the known servers list and continue?"), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) + { + return false; + } +#if LIBSSH_VERSION_MAJOR <1 && LIBSSH_VERSION_MINOR < 8 + if( ssh_write_knownhost(session) != SSH_OK ) +#else + if( ssh_session_update_known_hosts(session) != SSH_OK ) +#endif + { + std::cerr << "writeKnownHost failed" << std::endl; + return false; + } + else + { + ssh_connect(session); + } + } + ssh_key pubkey = ssh_key_new(); + ssh_pki_import_pubkey_file(pub_key_path, &pubkey); + res = ssh_userauth_try_publickey(session, NULL, pubkey); + if(res == SSH_AUTH_AGAIN) + ssh_disconnect(session); + else + break; + } + + + if(!test_result(res)) + { + ssh_disconnect(session); + return false; + } + + res = ssh_userauth_agent(session, user); + if(!test_result(res)) + { + ssh_disconnect(session); + return false; + } + return true; +} + void close_connection(ssh_session &session) { ssh_disconnect(session); @@ -282,5 +357,62 @@ bool pull_file(ssh_session &session, return true; } -}} +bool explore_the_galaxy(ssh_session &session, + QStringList& files) +{ + ssh_channel channel; + int rc; + channel = ssh_channel_new(session); + if (channel == NULL) return false; + rc = ssh_channel_open_session(channel); + if (rc != SSH_OK) + { + ssh_channel_free(channel); + return rc; + } + rc = ssh_channel_request_exec(channel, "ls /tmp"); + if (rc != SSH_OK) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return rc; + } + + char buffer[256]; + int nbytes; + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + while (nbytes > 0) + { + + std::string sbuf(buffer, nbytes); + if(sbuf.find("Polyhedron_demo_") != std::string::npos) + { + std::istringstream iss(sbuf); + std::string file; + while(iss >> file) + { + if(file.find("Polyhedron_demo_") != std::string::npos) + { + QString name(file.c_str()); + files.push_back(name.remove("Polyhedron_demo_")); + } + } + } + + nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0); + } + if (nbytes < 0) + { + ssh_channel_close(channel); + ssh_channel_free(channel); + return false; + } + ssh_channel_send_eof(channel); + ssh_channel_close(channel); + ssh_channel_free(channel); + return true; +} + +}// end of ssh_internal +}// end of CGAL #endif diff --git a/Polyhedron/demo/Polyhedron/Viewer.cpp b/Polyhedron/demo/Polyhedron/Viewer.cpp index ecadeba815a..57ed3f9d84f 100644 --- a/Polyhedron/demo/Polyhedron/Viewer.cpp +++ b/Polyhedron/demo/Polyhedron/Viewer.cpp @@ -1690,6 +1690,12 @@ void Viewer::setTotalPass(int p) void Viewer::messageLogged(QOpenGLDebugMessage msg) { + //filter out useless warning + // From those two links, we decided we didn't care for this warning: + // https://community.khronos.org/t/vertex-shader-in-program-2-is-being-recompiled-based-on-gl-state/76019 + // https://stackoverflow.com/questions/12004396/opengl-debug-context-performance-warning + if(msg.message().contains("is being recompiled")) + return; QString error; // Format based on severity @@ -2017,7 +2023,15 @@ void Viewer::onTextMessageSocketReceived(QString message) if(session != d->session){ return; } - moveCameraToCoordinates(position, 0.05f); + QStringList sl = position.split(" "); + if(sl.size() != 7) + return; + + CGAL::qglviewer::Vec pos(sl[0].toDouble(),sl[1].toDouble(),sl[2].toDouble()); + CGAL::qglviewer::Quaternion q(sl[3].toDouble(),sl[4].toDouble(), + sl[5].toDouble(),sl[6].toDouble()); + camera()->frame()->setPositionAndOrientation(pos, q); + update(); } #endif #include "Viewer.moc" diff --git a/Polyhedron/demo/Polyhedron/include/CGAL/Use_ssh.h b/Polyhedron/demo/Polyhedron/include/CGAL/Use_ssh.h index 2cdca27452b..5d24a129302 100644 --- a/Polyhedron/demo/Polyhedron/include/CGAL/Use_ssh.h +++ b/Polyhedron/demo/Polyhedron/include/CGAL/Use_ssh.h @@ -1,6 +1,10 @@ #ifndef USE_SSH_H #define USE_SSH_H #include +#include + +class QStringList; + namespace CGAL{ namespace ssh_internal{ //should be used inside a try/catch(ssh::SshException e) @@ -12,6 +16,12 @@ bool establish_ssh_session(ssh_session& session, const char *pub_key_path, const char *priv_key_path, const char *priv_key_password); + +bool establish_ssh_session_from_agent(ssh_session& session, + const char *user, + const char *server, + const char *pub_key_path); + void close_connection(ssh_session& session); bool push_file(ssh_session& session, @@ -21,5 +31,8 @@ bool pull_file(ssh_session &session, const char *from_path, const char *to_path); +bool explore_the_galaxy(ssh_session &session, + QStringList &files); + }} #endif