1 #include <QtGlobal>
2
3 #include <QtWidgets>
4 #include <QApplication>
5 #include <QDesktopServices>
6 #include <QFile>
7 #include <QTextStream>
8 #include <QDir>
9 #include <QCoreApplication>
10 #include <QMessageBox>
11 #include <QTimer>
12 #include <QHostInfo>
13
14 #include <errno.h>
15 #include <glib.h>
16
17 #include "utils/utils.h"
18 #include "utils/file-utils.h"
19 #include "utils/log.h"
20 #include "account-mgr.h"
21 #include "configurator.h"
22 #include "daemon-mgr.h"
23 #include "message-poller.h"
24 #include "settings-mgr.h"
25 #include "certs-mgr.h"
26 #include "rpc/rpc-client.h"
27 #include "ui/main-window.h"
28 #include "ui/tray-icon.h"
29 #include "ui/settings-dialog.h"
30 #include "ui/init-vdrive-dialog.h"
31 #include "ui/login-dialog.h"
32 #include "open-local-helper.h"
33 #include "avatar-service.h"
34 #include "filebrowser/thumbnail-service.h"
35 #include "filebrowser/data-cache.h"
36 #include "filebrowser/auto-update-mgr.h"
37 #include "filebrowser/data-mgr.h"
38 #include "rpc/local-repo.h"
39 #include "rpc/rpc-server.h"
40 #include "network-mgr.h"
41 #include "server-status-service.h"
42 #include "account-info-service.h"
43 #include "customization-service.h"
44
45 #if defined(Q_OS_WIN32)
46 #include "ext-handler.h"
47 #include "utils/registry.h"
48 #elif defined(HAVE_FINDER_SYNC_SUPPORT)
49 #include "finder-sync/finder-sync-listener.h"
50 #endif
51
52 #ifdef HAVE_SPARKLE_SUPPORT
53 #include "auto-update-service.h"
54 #endif
55
56
57 #if defined(Q_OS_MAC)
58 #include "utils/utils-mac.h"
59 #endif
60
61 #include "seafile-applet.h"
62
63 namespace {
64 enum DEBUG_LEVEL {
65 DEBUG = 0,
66 WARNING
67 };
68
69 // -DQT_NO_DEBUG is used with cmake and qmake if it is a release build
70 // if it is debug build, use DEBUG level as default
71 #if !defined(QT_NO_DEBUG) || !defined(NDEBUG)
72 DEBUG_LEVEL seafile_client_debug_level = DEBUG;
73 #else
74 // if it is release build, use WARNING level as default
75 DEBUG_LEVEL seafile_client_debug_level = WARNING;
76 #endif
77
myLogHandlerDebug(QtMsgType type,const QMessageLogContext & context,const QString & msg)78 void myLogHandlerDebug(QtMsgType type, const QMessageLogContext &context, const QString &msg)
79 {
80 QByteArray localMsg = msg.toLocal8Bit();
81 switch (type) {
82 // Note: By default, this information (QMessageLogContext) is recorded only in debug builds.
83 // You can overwrite this explicitly by defining QT_MESSAGELOGCONTEXT or QT_NO_MESSAGELOGCONTEXT.
84 // from http://doc.qt.io/qt-5/qmessagelogcontext.html
85 #ifdef QT_MESSAGELOGCONTEXT
86 case QtDebugMsg:
87 g_debug("%s (%s:%u)\n", localMsg.constData(), context.file, context.line);
88 break;
89 case QtWarningMsg:
90 g_warning("%s (%s:%u)\n", localMsg.constData(), context.file, context.line);
91 break;
92 case QtCriticalMsg:
93 g_critical("%s (%s:%u)\n", localMsg.constData(), context.file, context.line);
94 break;
95 case QtFatalMsg:
96 g_critical("%s (%s:%u)\n", localMsg.constData(), context.file, context.line);
97 abort();
98 #else // QT_MESSAGELOGCONTEXT
99 case QtDebugMsg:
100 g_debug("%s\n", localMsg.constData());
101 break;
102 case QtWarningMsg:
103 g_warning("%s\n", localMsg.constData());
104 break;
105 case QtCriticalMsg:
106 g_critical("%s\n", localMsg.constData());
107 break;
108 case QtFatalMsg:
109 g_critical("%s\n", localMsg.constData());
110 abort();
111 #endif // QT_MESSAGELOGCONTEXT
112 default:
113 break;
114 }
115 }
myLogHandler(QtMsgType type,const QMessageLogContext & context,const QString & msg)116 void myLogHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
117 {
118 QByteArray localMsg = msg.toLocal8Bit();
119 switch (type) {
120 #ifdef QT_MESSAGELOGCONTEXT
121 case QtWarningMsg:
122 g_warning("%s (%s:%u)\n", localMsg.constData(), context.file, context.line);
123 break;
124 case QtCriticalMsg:
125 g_critical("%s (%s:%u)\n", localMsg.constData(), context.file, context.line);
126 break;
127 case QtFatalMsg:
128 g_critical("%s (%s:%u)\n", localMsg.constData(), context.file, context.line);
129 abort();
130 #else // QT_MESSAGELOGCONTEXT
131 case QtWarningMsg:
132 g_warning("%s\n", localMsg.constData());
133 break;
134 case QtCriticalMsg:
135 g_critical("%s\n", localMsg.constData());
136 break;
137 case QtFatalMsg:
138 g_critical("%s\n", localMsg.constData());
139 abort();
140 #endif // QT_MESSAGELOGCONTEXT
141 default:
142 break;
143 }
144 }
145
146 #ifdef Q_OS_MAC
writeCABundleForCurl()147 void writeCABundleForCurl()
148 {
149 QString ca_bundle_path = QDir(seafApplet->configurator()->seafileDir()).filePath("ca-bundle.pem");
150 QFile bundle(ca_bundle_path);
151 if (bundle.exists()) {
152 bundle.remove();
153 }
154 bundle.open(QIODevice::WriteOnly);
155 const std::vector<QByteArray> certs = utils::mac::getSystemCaCertificates();
156 for (size_t i = 0; i < certs.size(); i++) {
157 QList<QSslCertificate> list = QSslCertificate::fromData(certs[i], QSsl::Der);
158 foreach (const QSslCertificate& cert, list) {
159 bundle.write(cert.toPem());
160 }
161 }
162 }
163 #endif
164
165
166 const char *const kPreconfigureUsername = "PreconfigureUsername";
167 const char *const kPreconfigureUserToken = "PreconfigureUserToken";
168 const char *const kPreconfigureServerAddr = "PreconfigureServerAddr";
169 const char *const kPreconfigureComputerName = "PreconfigureComputerName";
170 const char* const kPreConfiguretionBlockSize = "PreconfigureBlockSize";
171 const char* const kHideConfigurationWizard = "HideConfigurationWizard";
172 #if defined(Q_OS_WIN32)
173 const char *const kSeafileConfigureFileName = "seafile.ini";
174 const char *const kSeafileConfigurePath = "SOFTWARE\\Seafile";
175 const int kIntervalBeforeShowInitVirtualDialog = 3000;
176 #else
177 const char *const kSeafileConfigureFileName = ".seafilerc";
178 #endif
179 const char *const kSeafilePreconfigureGroupName = "preconfigure";
180
181 const int kIntervalForUpdateRepoProperty = 1000;
182
183 const char *kRepoServerUrlProperty = "server-url";
184
185 } // namespace
186
187
188 SeafileApplet *seafApplet;
189
SeafileApplet()190 SeafileApplet::SeafileApplet()
191 : configurator_(new Configurator),
192 account_mgr_(new AccountManager),
193 daemon_mgr_(new DaemonManager),
194 main_win_(NULL),
195 rpc_client_(new SeafileRpcClient),
196 message_poller_(new MessagePoller),
197 settings_dialog_(new SettingsDialog),
198 settings_mgr_(new SettingsManager),
199 certs_mgr_(new CertsManager),
200 data_mgr_(new DataManager),
201 started_(false),
202 in_exit_(false),
203 is_pro_(false),
204 about_to_quit_(false)
205 {
206 tray_icon_ = new SeafileTrayIcon(this);
207 connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(onAboutToQuit()));
208 }
209
~SeafileApplet()210 SeafileApplet::~SeafileApplet()
211 {
212 NetworkStatusDetector::instance()->stop();
213
214 #ifdef HAVE_FINDER_SYNC_SUPPORT
215 finderSyncListenerStop();
216 #endif
217 delete tray_icon_;
218 delete certs_mgr_;
219 delete settings_dialog_;
220 delete message_poller_;
221 delete rpc_client_;
222 delete account_mgr_;
223 // seafile-applet exit will inform seaf-daemon to clean sync token,
224 // so the class object deamon_mgr daemon_mgr_ dealloc after account_mgr_.
225
226 delete daemon_mgr_;
227 delete configurator_;
228 delete data_mgr_;
229 if (main_win_)
230 delete main_win_;
231 #if defined(Q_OS_WIN32)
232 SeafileExtensionHandler::instance()->stop();
233
234 #ifdef HAVE_SPARKLE_SUPPORT
235 AutoUpdateService::instance()->stop();
236 #endif
237
238 #endif
239 }
240
start()241 void SeafileApplet::start()
242 {
243 refreshQss();
244
245 configurator_->checkInit();
246
247 initLog();
248
249 qDebug("client id is %s", toCStr(getUniqueClientId()));
250 account_mgr_->start();
251
252 certs_mgr_->start();
253
254 data_mgr_->start();
255
256 #if defined(Q_OS_WIN32)
257 QString crash_rpt_path = QDir(configurator_->ccnetDir()).filePath("logs/seafile-crash-report.txt");
258 if (!g_setenv ("CRASH_RPT_PATH", toCStr(crash_rpt_path), FALSE))
259 qWarning("Failed to set CRASH_RPT_PATH env variable.\n");
260 #endif
261
262 #if defined(Q_OS_MAC)
263 writeCABundleForCurl();
264 #endif
265
266 // Load system proxy information. This must be done before we start
267 // seaf-daemon.
268 settings_mgr_->writeSystemProxyInfo(
269 account_mgr_->currentAccount().serverUrl,
270 QDir(configurator_->seafileDir()).filePath("system-proxy.txt"));
271
272 FileCache::instance()->start();
273
274 //
275 // start daemons
276 //
277 daemon_mgr_->startSeafileDaemon();
278
279 connect(daemon_mgr_, SIGNAL(daemonStarted()),
280 this, SLOT(onDaemonStarted()));
281 connect(daemon_mgr_, SIGNAL(daemonRestarted()),
282 this, SLOT(onDaemonRestarted()));
283 }
284
onDaemonStarted()285 void SeafileApplet::onDaemonStarted()
286 {
287 //
288 // start daemon-related services
289 //
290 rpc_client_->connectDaemon();
291
292 // Sleep 500 millseconds to wait seafile registering services
293
294 msleep(500);
295
296 //
297 // load proxy settings (important)
298 //
299 settings_mgr_->loadSettings();
300
301 //
302 // start network-related services
303 //
304 NetworkStatusDetector::instance()->start();
305 AutoUpdateManager::instance()->start();
306
307 AvatarService::instance()->start();
308 ThumbnailService::instance()->start();
309
310 ServerStatusService::instance()->start();
311 CustomizationService::instance()->start();
312 AccountInfoService::instance()->start();
313 SeafileAppletRpcServer::instance()->start();
314
315 account_mgr_->updateServerInfoForAllAccounts();
316
317 //
318 // start ui part
319 //
320 main_win_ = new MainWindow;
321
322 #if defined(Q_OS_MAC)
323 seafApplet->settingsManager()->setHideDockIcon(seafApplet->settingsManager()->hideDockIcon());
324 #endif
325
326 #ifdef XCODE_APP
327 if (configurator_->firstUse()) {
328 settings_mgr_->setAutoStart(true);
329 }
330 #endif
331
332 if (configurator_->firstUse() || account_mgr_->accounts().size() == 0) {
333 do {
334 QString username = readPreconfigureExpandedString(kPreconfigureUsername);
335 QString token = readPreconfigureExpandedString(kPreconfigureUserToken);
336 QString url = readPreconfigureExpandedString(kPreconfigureServerAddr);
337 QString computer_name = readPreconfigureExpandedString(kPreconfigureComputerName, settingsManager()->getComputerName());
338 if (!computer_name.isEmpty())
339 settingsManager()->setComputerName(computer_name);
340 if (!username.isEmpty() && !token.isEmpty() && !url.isEmpty()) {
341 Account account(url, username, token);
342 account_mgr_->setCurrentAccount(account);
343 break;
344 }
345
346 if (readPreconfigureEntry(kHideConfigurationWizard).toInt())
347 break;
348 LoginDialog login_dialog;
349 login_dialog.exec();
350 } while (0);
351 } else if (!account_mgr_->accounts().empty()) {
352 const Account &account = account_mgr_->accounts()[0];
353 account_mgr_->removeNonautoLoginSyncTokens();
354 account_mgr_->validateAndUseAccount(account);
355 }
356
357 started_ = true;
358
359 if (configurator_->firstUse() || !settings_mgr_->hideMainWindowWhenStarted()) {
360 main_win_->showWindow();
361 }
362
363 tray_icon_->start();
364 tray_icon_->setState(SeafileTrayIcon::STATE_DAEMON_UP);
365 message_poller_->start();
366
367
368 #if defined(Q_OS_WIN32)
369 QTimer::singleShot(kIntervalBeforeShowInitVirtualDialog, this, SLOT(checkInitVDrive()));
370 configurator_->installCustomUrlHandler();
371 #endif
372
373 QString value;
374 if (seafApplet->rpcClient()->seafileGetConfig("client_name", &value) < 0 || value.isEmpty()) {
375 // We do this because clients before 6.0 don't set the "client_name" option.
376 seafApplet->rpcClient()->seafileSetConfig(
377 "client_name", settings_mgr_->getComputerName());
378 }
379
380 // Set the device id to the daemon so it can use it when generating commits.
381 // The "client_name" is not set here, but updated each time we call
382 // switch_account rpc.
383 if (rpc_client_->seafileGetConfig("client_id", &value) < 0 ||
384 value.isEmpty() || value != getUniqueClientId()) {
385 rpc_client_->seafileSetConfig("client_id", getUniqueClientId());
386 }
387
388 // pre-configure option to set the size of sync block.
389 QString block = readPreconfigureExpandedString(kPreConfiguretionBlockSize);
390 if (!block.isEmpty()) {
391 int block_size = block.toInt();
392 if (rpc_client_->seafileSetConfigInt("block_size", block_size) < 0) {
393 qDebug("setting sync block_size error");
394 }
395 }
396 QTimer::singleShot(kIntervalForUpdateRepoProperty,
397 this, SLOT(updateReposPropertyForHttpSync()));
398
399 //
400 // start finder/explorer extension handler
401 //
402 #if defined(Q_OS_WIN32)
403 SeafileExtensionHandler::instance()->start();
404 #elif defined(HAVE_FINDER_SYNC_SUPPORT)
405 finderSyncListenerStart();
406 #endif
407
408 #ifdef HAVE_SPARKLE_SUPPORT
409 if (AutoUpdateService::instance()->shouldSupportAutoUpdate()) {
410 AutoUpdateService::instance()->start();
411 }
412 #endif
413 }
414
checkInitVDrive()415 void SeafileApplet::checkInitVDrive()
416 {
417 if (configurator_->firstUse() && account_mgr_->hasAccount()) {
418 const Account account = account_mgr_->currentAccount();
419 InitVirtualDriveDialog *dialog = new InitVirtualDriveDialog(account);
420 // Move the dialog to the left of the main window
421 int x = main_win_->pos().x() - dialog->rect().width() - 30;
422 int y = (QApplication::desktop()->screenGeometry().center() - dialog->rect().center()).y();
423 dialog->move(qMax(0, x), y);
424 dialog->show();
425 dialog->raise();
426 dialog->activateWindow();
427 }
428 }
429
onAboutToQuit()430 void SeafileApplet::onAboutToQuit()
431 {
432 about_to_quit_ = true;
433 tray_icon_->hide();
434 if (main_win_) {
435 main_win_->writeSettings();
436 }
437 }
438 // stop the main event loop and return to the main function
errorAndExit(const QString & error)439 void SeafileApplet::errorAndExit(const QString& error)
440 {
441 if (in_exit_ || QCoreApplication::closingDown()) {
442 return;
443 }
444
445 in_exit_ = true;
446
447 warningBox(error);
448 // stop eventloop before exit and return to the main function
449 QCoreApplication::exit(1);
450 }
451
restartApp()452 void SeafileApplet::restartApp()
453 {
454 if (in_exit_ || QCoreApplication::closingDown()) {
455 return;
456 }
457
458 in_exit_ = true;
459
460 QStringList args = QApplication::arguments();
461
462 args.removeFirst();
463
464 // append delay argument
465 bool found = false;
466 Q_FOREACH(const QString& arg, args)
467 {
468 if (arg == "--delay" || arg == "-D") {
469 found = true;
470 break;
471 }
472 }
473
474 if (!found)
475 args.push_back("--delay");
476
477 QProcess::startDetached(QApplication::applicationFilePath(), args);
478 QCoreApplication::quit();
479 }
480
initLog()481 void SeafileApplet::initLog()
482 {
483 if (applet_log_init(toCStr(configurator_->ccnetDir())) < 0) {
484 errorAndExit(tr("Failed to initialize log: %s").arg(g_strerror(errno)));
485 } else {
486 // give a change to override DEBUG_LEVEL by environment
487 QString debug_level = qgetenv("SEAFILE_CLIENT_DEBUG");
488 if (!debug_level.isEmpty() && debug_level != "false" &&
489 debug_level != "0")
490 seafile_client_debug_level = DEBUG;
491
492 if (seafile_client_debug_level == DEBUG)
493 qInstallMessageHandler(myLogHandlerDebug);
494 else
495 qInstallMessageHandler(myLogHandler);
496 }
497 }
498
loadQss(const QString & path)499 bool SeafileApplet::loadQss(const QString& path)
500 {
501 QFile file(path);
502 if (!QFileInfo(file).exists()) {
503 return false;
504 }
505 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
506 return false;
507 }
508
509 QTextStream input(&file);
510 style_ += "\n";
511 style_ += input.readAll();
512 qApp->setStyleSheet(style_);
513
514 return true;
515 }
516
refreshQss()517 void SeafileApplet::refreshQss()
518 {
519 style_.clear();
520 loadQss("qt.css") || loadQss(":/qt.css");
521
522 #if defined(Q_OS_WIN32)
523 loadQss("qt-win.css") || loadQss(":/qt-win.css");
524 #elif defined(Q_OS_LINUX)
525 loadQss("qt-linux.css") || loadQss(":/qt-linux.css");
526 #else
527 loadQss("qt-mac.css") || loadQss(":/qt-mac.css");
528 #endif
529 }
530
warningBox(const QString & msg,QWidget * parent)531 void SeafileApplet::warningBox(const QString& msg, QWidget *parent)
532 {
533 QMessageBox box(parent ? parent : main_win_);
534 box.setText(msg);
535 box.setWindowTitle(getBrand());
536 box.setIcon(QMessageBox::Warning);
537 box.addButton(tr("OK"), QMessageBox::YesRole);
538 box.exec();
539
540 if (!parent && main_win_) {
541 main_win_->showWindow();
542 }
543 qWarning("%s", msg.toUtf8().data());
544 }
545
messageBox(const QString & msg,QWidget * parent)546 void SeafileApplet::messageBox(const QString& msg, QWidget *parent)
547 {
548 QMessageBox box(parent ? parent : main_win_);
549 box.setText(msg);
550 box.setWindowTitle(getBrand());
551 box.setIcon(QMessageBox::Information);
552 box.addButton(tr("OK"), QMessageBox::YesRole);
553 if (!parent) {
554 main_win_->showWindow();
555 }
556 box.exec();
557 qDebug("%s", msg.toUtf8().data());
558 }
559
yesOrNoBox(const QString & msg,QWidget * parent,bool default_val)560 bool SeafileApplet::yesOrNoBox(const QString& msg, QWidget *parent, bool default_val)
561 {
562 QMessageBox box(parent ? parent : main_win_);
563 box.setText(msg);
564 box.setWindowTitle(getBrand());
565 box.setIcon(QMessageBox::Question);
566 QPushButton *yes_btn = box.addButton(tr("Yes"), QMessageBox::YesRole);
567 QPushButton *no_btn = box.addButton(tr("No"), QMessageBox::NoRole);
568 box.setDefaultButton(default_val ? yes_btn: no_btn);
569 box.exec();
570
571 return box.clickedButton() == yes_btn;
572 }
573
yesOrCancelBox(const QString & msg,QWidget * parent,bool default_yes)574 bool SeafileApplet::yesOrCancelBox(const QString& msg, QWidget *parent, bool default_yes)
575 {
576 QMessageBox box(parent ? parent : main_win_);
577 box.setText(msg);
578 box.setWindowTitle(getBrand());
579 box.setIcon(QMessageBox::Question);
580 QPushButton *yes_btn = box.addButton(tr("Yes"), QMessageBox::YesRole);
581 QPushButton *cancel_btn = box.addButton(tr("Cancel"), QMessageBox::RejectRole);
582 box.setDefaultButton(default_yes ? yes_btn: cancel_btn);
583 box.exec();
584
585 return box.clickedButton() == yes_btn;
586 }
587
588
589 QMessageBox::StandardButton
yesNoCancelBox(const QString & msg,QWidget * parent,QMessageBox::StandardButton default_btn)590 SeafileApplet::yesNoCancelBox(const QString& msg, QWidget *parent, QMessageBox::StandardButton default_btn)
591 {
592 QMessageBox box(parent ? parent : main_win_);
593 box.setText(msg);
594 box.setWindowTitle(getBrand());
595 box.setIcon(QMessageBox::Question);
596 QPushButton *yes_btn = box.addButton(tr("Yes"), QMessageBox::YesRole);
597 QPushButton *no_btn = box.addButton(tr("No"), QMessageBox::NoRole);
598 box.addButton(tr("Cancel"), QMessageBox::RejectRole);
599 box.setDefaultButton(default_btn);
600 box.exec();
601
602 QAbstractButton *btn = box.clickedButton();
603 if (btn == yes_btn) {
604 return QMessageBox::Yes;
605 } else if (btn == no_btn) {
606 return QMessageBox::No;
607 }
608
609 return QMessageBox::Cancel;
610 }
611
detailedYesOrNoBox(const QString & msg,const QString & detailed_text,QWidget * parent,bool default_val)612 bool SeafileApplet::detailedYesOrNoBox(const QString& msg, const QString& detailed_text, QWidget *parent, bool default_val)
613 {
614 QMessageBox msgBox(QMessageBox::Question,
615 getBrand(),
616 msg,
617 QMessageBox::Yes | QMessageBox::No,
618 parent != 0 ? parent : main_win_);
619 msgBox.setDetailedText(detailed_text);
620 msgBox.setButtonText(QMessageBox::Yes, tr("Yes"));
621 msgBox.setButtonText(QMessageBox::No, tr("No"));
622 // Turns out the layout box in the QMessageBox is a grid
623 // You can force the resize using a spacer this way:
624 QSpacerItem* horizontalSpacer = new QSpacerItem(400, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
625 QGridLayout* layout = (QGridLayout*)msgBox.layout();
626 layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());
627 msgBox.setDefaultButton(default_val ? QMessageBox::Yes : QMessageBox::No);
628 return msgBox.exec() == QMessageBox::Yes;
629 }
630
631 /**
632 * For each repo, add the "server-url" property (inferred from account url),
633 * which would be used for http sync.
634 */
updateReposPropertyForHttpSync()635 void SeafileApplet::updateReposPropertyForHttpSync()
636 {
637 std::vector<LocalRepo> repos;
638 if (rpc_client_->listLocalRepos(&repos) < 0) {
639 QTimer::singleShot(kIntervalForUpdateRepoProperty,
640 this, SLOT(updateReposPropertyForHttpSync()));
641 return;
642 }
643
644 const std::vector<Account>& accounts = account_mgr_->accounts();
645 for (size_t i = 0; i < repos.size(); i++) {
646 const LocalRepo& repo = repos[i];
647 QString repo_server_url;
648 QString server_url;
649 if (rpc_client_->getRepoProperty(repo.id, kRepoServerUrlProperty, &repo_server_url) < 0) {
650 continue;
651 }
652 if (!repo_server_url.isEmpty()) {
653 continue;
654 }
655 if (rpc_client_->getRepoProperty(repo.id, kRepoServerUrlProperty, &server_url) < 0) {
656 continue;
657 }
658
659 QString server_host = QUrl(server_url).host();
660 for (size_t i = 0; i < accounts.size(); i++) {
661 const Account& account = accounts[i];
662 if (account.serverUrl.host() == server_host) {
663 QUrl url(account.serverUrl);
664 url.setPath("/");
665 rpc_client_->setRepoProperty(repo.id, kRepoServerUrlProperty, url.toString());
666 break;
667 }
668 }
669 }
670 }
671
readPreconfigureEntry(const QString & key,const QVariant & default_value)672 QVariant SeafileApplet::readPreconfigureEntry(const QString& key, const QVariant& default_value)
673 {
674 #ifdef Q_OS_WIN32
675 QVariant v = RegElement::getPreconfigureValue(key);
676 if (!v.isNull()) {
677 return v;
678 }
679 #endif
680 QString configure_file = QDir::home().filePath(kSeafileConfigureFileName);
681 if (!QFileInfo(configure_file).exists())
682 return default_value;
683 QSettings setting(configure_file, QSettings::IniFormat);
684 setting.beginGroup(kSeafilePreconfigureGroupName);
685 QVariant value = setting.value(key, default_value);
686 setting.endGroup();
687 return value;
688 }
689
readPreconfigureExpandedString(const QString & key,const QString & default_value)690 QString SeafileApplet::readPreconfigureExpandedString(const QString& key, const QString& default_value)
691 {
692 QVariant retval = readPreconfigureEntry(key, default_value);
693 if (retval.isNull() || retval.type() != QVariant::String)
694 return QString();
695 return expandVars(retval.toString());
696 }
697
698
getText(QWidget * parent,const QString & title,const QString & label,QLineEdit::EchoMode mode,const QString & text,bool * ok,Qt::WindowFlags flags,Qt::InputMethodHints inputMethodHints)699 QString SeafileApplet::getText(QWidget *parent,
700 const QString &title,
701 const QString &label,
702 QLineEdit::EchoMode mode,
703 const QString &text,
704 bool *ok,
705 Qt::WindowFlags flags,
706 Qt::InputMethodHints inputMethodHints)
707 {
708 QInputDialog tmp_dialog;
709 // Get rid of the help button
710 if (flags == 0) {
711 flags = tmp_dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint;
712 }
713
714 return QInputDialog::getText(parent != nullptr ? parent : main_win_,
715 title,
716 label,
717 mode,
718 text,
719 ok,
720 flags,
721 inputMethodHints);
722 }
723
getUniqueClientId()724 QString SeafileApplet::getUniqueClientId()
725 {
726 static QString id;
727 if (!id.isEmpty()) {
728 return id;
729 }
730
731 // Id file path is `~/Seafile/.seafile-data/id`
732 QFile id_file(QDir(seafApplet->configurator()->seafileDir())
733 .absoluteFilePath("id"));
734 if (!id_file.exists()) {
735 qWarning("id file not found, creating it");
736 // First migrate existing id from ccnet.conf General.ID
737 QString ccnet_conf_file = QDir(configurator_->ccnetDir()).absoluteFilePath("ccnet.conf");
738 QString ccnet_id;
739 if (QFile(ccnet_conf_file).exists()) {
740 QSettings ccnet_conf(ccnet_conf_file, QSettings::IniFormat);
741 ccnet_id = ccnet_conf.value("ID").toString();
742 if (ccnet_id.isEmpty()) {
743 ccnet_conf.beginGroup("General");
744 ccnet_id = ccnet_conf.value("ID", "").toString();
745 }
746 }
747
748 if (!ccnet_id.isEmpty()) {
749 id = ccnet_id;
750 qWarning("use existing ccnet id %s", toCStr(id));
751 } else {
752 srand(time(NULL));
753 while (id.length() < 40) {
754 int r = rand() % 0xff;
755 id += QString("%1").arg(r, 0, 16);
756 }
757 id = id.mid(0, 40);
758 qWarning("generated new device id %s", toCStr(id));
759 }
760
761 if (!id_file.open(QIODevice::WriteOnly)) {
762 errorAndExit(tr("failed to save client id"));
763 return "";
764 }
765
766 id_file.write(id.toUtf8().data());
767 return id;
768 }
769
770 if (!id_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
771 errorAndExit(tr("failed to access %1").arg(id_file.fileName()));
772 return "";
773 }
774
775 QTextStream input(&id_file);
776 input.setCodec("UTF-8");
777
778 if (input.atEnd()) {
779 errorAndExit(tr("incorrect client id"));
780 return "";
781 }
782
783 id = input.readLine().trimmed();
784 if (id.length() != 40) {
785 errorAndExit(tr("failed to read %1").arg(id_file.fileName()));
786 return "";
787 }
788
789 qWarning("read id from id file");
790 return id;
791 }
792
onDaemonRestarted()793 void SeafileApplet::onDaemonRestarted()
794 {
795 qDebug("reviving rpc client when daemon is restarted");
796 if (rpc_client_) {
797 delete rpc_client_;
798 }
799
800 rpc_client_ = new SeafileRpcClient();
801 rpc_client_->tryConnectDaemon();
802 }
803