1 /*
2 Drawpile - a collaborative drawing program.
3
4 Copyright (C) 2017 Calle Laakkonen
5
6 Drawpile is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Drawpile is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Drawpile. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "mainwindow.h"
21 #include "localserver.h"
22 #include "remoteserver.h"
23 #include "multiserver.h"
24 #include "database.h"
25 #include "trayicon.h"
26 #include "singleinstance.h"
27 #include "authdialog.h"
28
29 #include <QApplication>
30 #include <QCommandLineParser>
31 #include <QThread>
32 #include <QMessageBox>
33 #include <QMutex>
34 #include <QFileInfo>
35 #include <QStandardPaths>
36 #include <QDir>
37 #include <QSystemTrayIcon>
38 #include <QSettings>
39 #include <QMenu>
40
41 namespace server {
42 namespace gui {
43
startServer()44 bool startServer()
45 {
46 QString errorMessage;
47 MultiServer *server=nullptr;
48
49 // A database is always used with the GUI server
50 QDir dbDir = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
51 dbDir.mkpath(".");
52 QString dbFile = QFileInfo(dbDir, "guiserver.db").absoluteFilePath();
53
54 // The server must be initialized in its own thread
55 QMutex mutex;
56 auto threadInitFunc = [dbFile, &errorMessage, &server, &mutex]() {
57 Database *db = new Database;
58 if(!db->openFile(dbFile)) {
59 errorMessage = QApplication::tr("Couldn't open database");
60 mutex.unlock();
61 return;
62 }
63
64 server = new MultiServer(db);
65 db->setParent(server);
66
67 mutex.unlock();
68 };
69
70 // Start a new thread for the server
71 qDebug() << "Starting server thread";
72 QThread *serverThread = new QThread;
73 auto initConnetion = serverThread->connect(serverThread, &QThread::started, threadInitFunc);
74 mutex.lock();
75 serverThread->start();
76
77 // Wait for initialization to complete
78 mutex.lock();
79 mutex.unlock();
80 QObject::disconnect(initConnetion);
81 qDebug() << "Server thread initialized";
82
83 if(!errorMessage.isEmpty()) {
84 QMessageBox::critical(nullptr, QApplication::tr("Drawpile Server"), errorMessage);
85 return false;
86 }
87
88 Q_ASSERT(server);
89
90 // The local server connector to use
91 LocalServer *localServer = new LocalServer(server);
92
93 QObject::connect(localServer, &LocalServer::serverError, [](const QString &error) {
94 QMessageBox::warning(nullptr, QApplication::tr("Drawpile Server"), error);
95 });
96
97 // Create the system tray menu
98 if(QSettings().value("ui/trayicon", true).toBool()) {
99 TrayIcon::showTrayIcon();
100 }
101
102 QObject::connect(server, &MultiServer::userCountChanged, TrayIcon::instance(), &TrayIcon::setNumber);
103 QObject::connect(localServer, &LocalServer::serverStateChanged, TrayIcon::instance(), &TrayIcon::setServerOn);
104
105 // Open the main window
106 MainWindow::setDefaultInstanceServer(localServer);
107 MainWindow::showDefaultInstance();
108
109 // Quit when last window is closed, but not if the tray icon is visible
110 qApp->setQuitOnLastWindowClosed(false);
111 #ifndef Q_OS_MAC
112 QObject::connect(qApp, &QApplication::lastWindowClosed, []() {
113 if(!TrayIcon::isTrayIconVisible())
114 qApp->quit();
115 });
116 #endif
117
118 return true;
119 }
120
startRemote(const QString & address)121 bool startRemote(const QString &address)
122 {
123 QUrl url(address);
124 if(!url.isValid()) {
125 QMessageBox::critical(nullptr, QApplication::tr("Drawpile Server"), QApplication::tr("Invalid URL"));
126 return false;
127 }
128
129 AuthDialog::init();
130
131 RemoteServer *remote = new RemoteServer(url);
132
133 MainWindow *win = new MainWindow(remote);
134 remote->setParent(win);
135
136 win->show();
137 return true;
138 }
139
start()140 bool start() {
141 // Set up command line arguments
142 QCommandLineParser parser;
143
144 parser.setApplicationDescription("Standalone server for Drawpile (graphical mode)");
145 parser.addHelpOption();
146
147 // --gui (this is just for the help text)
148 QCommandLineOption guiOption(QStringList() << "gui", "Run the graphical version.");
149 parser.addOption(guiOption);
150
151 // remote <address>
152 QCommandLineOption remoteOption(QStringList() << "remote", "Remote admin mode", "address");
153 parser.addOption(remoteOption);
154
155 // Parse
156 parser.process(*QCoreApplication::instance());
157
158 if(parser.isSet(remoteOption)) {
159 // Remote admin mode
160 return startRemote(parser.value(remoteOption));
161
162 } else {
163 // Normal server mode
164 SingleInstance *guard = new SingleInstance;
165 if(!guard->tryStart()) {
166 qWarning("Another instance is already running");
167 return false;
168 }
169 QObject::connect(qApp, &QApplication::aboutToQuit, guard, &SingleInstance::deleteLater);
170
171 return startServer();
172 }
173 }
174
175 }
176 }
177