1 #include <cassert>
2 #include <errno.h>
3 #include <dirent.h>
4 #include <cstdio>
5 #include <cstdlib>
6 #include <unistd.h>
7 #include <sqlite3.h>
8 #include <glib.h>
9 #include <cstring>
10 #include <QObject>
11 #include <QString>
12 #include <QSettings>
13 #include <QProcess>
14 #include <QDesktopServices>
15 #include <QHostInfo>
16 #include <jansson.h>
17 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
18 #include <QUrlQuery>
19 #endif
20
21 #include "utils/utils-mac.h"
22 #include "utils/utils-win.h"
23
24 #if defined(Q_OS_MAC)
25 #include <sys/sysctl.h>
26 #elif defined(Q_OS_WIN32)
27 #include <windows.h>
28 #include <psapi.h>
29 #endif
30
31 #include <QMap>
32 #include <QVariant>
33 #include <QDebug>
34 #include <QDateTime>
35 #include <QCryptographicHash>
36 #include <QSslCipher>
37 #include <QSslCertificate>
38
39 #include "seafile-applet.h"
40 #include "rpc/rpc-client.h"
41
42 #include "utils/utils.h"
43
44 namespace {
45
46 const char *kSeafileClientBrand = "Seafile";
47 #if defined(Q_OS_WIN32)
48 const char *kCcnetConfDir = "ccnet";
49 #else
50 const char *kCcnetConfDir = ".ccnet";
51 #endif
52
53 #ifdef Q_OS_LINUX
54 /// \brief call xdg-mime to find out the mime filetype X11 recognizes it as
55 /// xdg-mime's usage:
56 /// xdg-mime query filetype <filename>
57 /// stdout: mime-type
getMimeTypeFromXdgUtils(const QString & filepath,QString * mime)58 bool getMimeTypeFromXdgUtils(const QString &filepath, QString *mime)
59 {
60 QProcess subprocess;
61 QStringList args("query");
62 args.push_back("filetype");
63 args.push_back(filepath);
64 subprocess.start(QLatin1String("xdg-mime"), args);
65 subprocess.waitForFinished(-1);
66 if (subprocess.exitCode())
67 return false;
68 *mime = subprocess.readAllStandardOutput();
69 *mime = mime->trimmed();
70 if (mime->isEmpty())
71 return false;
72 return true;
73 }
74
75 /// \brief call xdg-mime to find out the application X11 opens with by mime filetype
76 /// xdg-mime's usage:
77 /// xdg-mime query default <filename>
78 /// stdout: application
getOpenApplicationFromXdgUtils(const QString & mime,QString * application)79 bool getOpenApplicationFromXdgUtils(const QString &mime, QString *application)
80 {
81 QProcess subprocess;
82 QStringList args("query");
83 args.push_back("default");
84 args.push_back(mime);
85 subprocess.start(QLatin1String("xdg-mime"), args);
86 subprocess.waitForFinished(-1);
87 if (subprocess.exitCode())
88 return false;
89 *application = subprocess.readAllStandardOutput();
90 *application = application->trimmed();
91 if (application->isEmpty())
92 return false;
93 return true;
94 }
95 #endif
96
97 } // namespace
98
99
defaultCcnetDir()100 QString defaultCcnetDir() {
101 const char *env = g_getenv("CCNET_CONF_DIR");
102 if (env) {
103 return QString::fromUtf8(env);
104 } else {
105 return QDir::home().filePath(kCcnetConfDir);
106 }
107 }
108
defaultDownloadDir()109 QString defaultDownloadDir() {
110 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
111 static QStringList list = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation);
112 if (!list.empty())
113 return list.front();
114 #endif
115 // qt4 don't have QStandardPaths, use glib's as fallback
116 return QString::fromUtf8(g_get_user_special_dir(G_USER_DIRECTORY_DOWNLOAD));
117 }
118
openInNativeExtension(const QString & path)119 bool openInNativeExtension(const QString &path) {
120 #if defined(Q_OS_WIN32)
121 //call ShellExecute internally
122 return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
123 #elif defined(Q_OS_MAC)
124 // mac's open program, it will fork to open the file in a subprocess
125 // so we will wait for it to check whether it succeeds or not
126 QProcess subprocess;
127 subprocess.start(QLatin1String("open"), QStringList(path));
128 subprocess.waitForFinished(-1);
129 return subprocess.exitCode() == 0;
130 #elif defined(Q_OS_LINUX)
131 // unlike mac's open program, xdg-open won't fork a new subprocess to open
132 // the file will block until the application returns, so we won't wait for it
133 // and we need another approach to check if it works
134
135 // find out if the file can be opened by xdg-open, xdg-mime
136 // usually they are installed in xdg-utils installed by default
137 QString mime_type;
138 if (!getMimeTypeFromXdgUtils(path, &mime_type))
139 return false;
140 // don't open this type of files from xdg-mime
141 if (mime_type == "application/octet-stream")
142 return false;
143 // in fact we need to filter out files like application/x-executable
144 // but it is not necessary since getMimeTypeFromXdg will return false for
145 // it!
146 QString application;
147 if (!getOpenApplicationFromXdgUtils(mime_type, &application))
148 return false;
149
150 return QProcess::startDetached(QLatin1String("xdg-open"),
151 QStringList(path));
152 #else
153 return false;
154 #endif
155 }
156
showInGraphicalShell(const QString & path)157 bool showInGraphicalShell(const QString& path) {
158 #if defined(Q_OS_WIN32)
159 QStringList params;
160 if (!QFileInfo(path).isDir())
161 params << QLatin1String("/select,");
162 params << QDir::toNativeSeparators(path);
163 return QProcess::startDetached(QLatin1String("explorer.exe"), params);
164 #elif defined(Q_OS_MAC)
165 QStringList scriptArgs;
166 scriptArgs << QLatin1String("-e")
167 << QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
168 .arg(path);
169 QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
170 scriptArgs.clear();
171 scriptArgs << QLatin1String("-e")
172 << QLatin1String("tell application \"Finder\" to activate");
173 QProcess::execute("/usr/bin/osascript", scriptArgs);
174 return true;
175 #else
176 return QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(path).absolutePath()));
177 #endif
178 }
179
180 typedef bool (*SqliteRowFunc) (sqlite3_stmt *stmt, void *data);
181
182 sqlite3_stmt *
sqlite_query_prepare(sqlite3 * db,const char * sql)183 sqlite_query_prepare (sqlite3 *db, const char *sql)
184 {
185 sqlite3_stmt *stmt;
186 int result;
187
188 result = sqlite3_prepare_v2 (db, sql, -1, &stmt, NULL);
189
190 if (result != SQLITE_OK) {
191 const gchar *str = sqlite3_errmsg (db);
192
193 g_warning ("Couldn't prepare query, error:%d->'%s'\n\t%s\n",
194 result, str ? str : "no error given", sql);
195
196 return NULL;
197 }
198
199 return stmt;
200 }
201
sqlite_query_exec(sqlite3 * db,const char * sql)202 int sqlite_query_exec (sqlite3 *db, const char *sql)
203 {
204 char *errmsg = NULL;
205 int result;
206
207 result = sqlite3_exec (db, sql, NULL, NULL, &errmsg);
208
209 if (result != SQLITE_OK) {
210 if (errmsg != NULL) {
211 g_warning ("SQL error: %d - %s\n:\t%s\n", result, errmsg, sql);
212 sqlite3_free (errmsg);
213 }
214 return -1;
215 }
216
217 return 0;
218 }
219
sqlite_foreach_selected_row(sqlite3 * db,const char * sql,SqliteRowFunc callback,void * data)220 int sqlite_foreach_selected_row (sqlite3 *db, const char *sql,
221 SqliteRowFunc callback, void *data)
222 {
223 sqlite3_stmt *stmt;
224 int result;
225 int n_rows = 0;
226
227 stmt = sqlite_query_prepare (db, sql);
228 if (!stmt) {
229 return -1;
230 }
231
232 while (1) {
233 result = sqlite3_step (stmt);
234 if (result != SQLITE_ROW)
235 break;
236 n_rows++;
237 if (!callback (stmt, data))
238 break;
239 }
240
241 if (result == SQLITE_ERROR) {
242 const gchar *s = sqlite3_errmsg (db);
243
244 g_warning ("Couldn't execute query, error: %d->'%s'\n",
245 result, s ? s : "no error given");
246 sqlite3_finalize (stmt);
247 return -1;
248 }
249
250 sqlite3_finalize (stmt);
251 return n_rows;
252 }
253
checkdir_with_mkdir(const char * dir)254 int checkdir_with_mkdir (const char *dir)
255 {
256 #if defined(Q_OS_WIN32)
257 int ret;
258 char *path = g_strdup(dir);
259 char *p = (char *)path + strlen(path) - 1;
260 while (*p == '\\' || *p == '/') *p-- = '\0';
261 ret = g_mkdir_with_parents(path, 0755);
262 g_free (path);
263 return ret;
264 #else
265 return g_mkdir_with_parents(dir, 0755);
266 #endif
267 }
268
269
270 #if defined(Q_OS_WIN32)
271 static LONG
get_win_run_key(HKEY * pKey)272 get_win_run_key (HKEY *pKey)
273 {
274 const char *key_run = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
275 LONG result = RegOpenKeyEx(
276 /* We don't use HKEY_LOCAL_MACHINE here because that requires
277 * seaf-daemon to run with admin privilege. */
278 HKEY_CURRENT_USER,
279 key_run,
280 0L,KEY_WRITE | KEY_READ,
281 pKey);
282 if (result != ERROR_SUCCESS) {
283 qWarning("Failed to open Registry key %s\n", key_run);
284 }
285
286 return result;
287 }
288
289 static int
add_to_auto_start(const wchar_t * appname_w,const wchar_t * path_w)290 add_to_auto_start (const wchar_t *appname_w, const wchar_t *path_w)
291 {
292 HKEY hKey;
293 LONG result = get_win_run_key(&hKey);
294 if (result != ERROR_SUCCESS) {
295 return -1;
296 }
297
298 DWORD n = sizeof(wchar_t) * (wcslen(path_w) + 1);
299
300 result = RegSetValueExW (hKey, appname_w,
301 0, REG_SZ, (const BYTE *)path_w, n);
302
303 RegCloseKey(hKey);
304 if (result != ERROR_SUCCESS) {
305 qWarning("Failed to create auto start value\n");
306 return -1;
307 }
308
309 return 0;
310 }
311
312 static int
delete_from_auto_start(const wchar_t * appname)313 delete_from_auto_start(const wchar_t *appname)
314 {
315 HKEY hKey;
316 LONG result = get_win_run_key(&hKey);
317 if (result != ERROR_SUCCESS) {
318 return -1;
319 }
320
321 result = RegDeleteValueW (hKey, appname);
322 RegCloseKey(hKey);
323 if (result != ERROR_SUCCESS) {
324 qWarning("Failed to remove auto start value");
325 return -1;
326 }
327
328 return 0;
329 }
330
331 int
get_seafile_auto_start()332 get_seafile_auto_start()
333 {
334 HKEY hKey;
335 LONG result = get_win_run_key(&hKey);
336 if (result != ERROR_SUCCESS) {
337 return -1;
338 }
339
340 char buf[MAX_PATH] = {0};
341 DWORD len = sizeof(buf);
342 result = RegQueryValueExW (hKey, /* Key */
343 getBrand().toStdWString().c_str(), /* value */
344 NULL, /* reserved */
345 NULL, /* output type */
346 (LPBYTE)buf, /* output data */
347 &len); /* output length */
348
349 RegCloseKey(hKey);
350 if (result != ERROR_SUCCESS) {
351 /* seafile applet auto start no set */
352 return 0;
353 }
354
355 return 1;
356 }
357
358 int
set_seafile_auto_start(bool on)359 set_seafile_auto_start(bool on)
360 {
361 int result = 0;
362 if (on) {
363 /* turn on auto start */
364 wchar_t applet_path[MAX_PATH];
365 if (GetModuleFileNameW (NULL, applet_path, MAX_PATH) == 0) {
366 return -1;
367 }
368
369 result = add_to_auto_start (getBrand().toStdWString().c_str(), applet_path);
370
371 } else {
372 /* turn off auto start */
373 result = delete_from_auto_start(getBrand().toStdWString().c_str());
374 }
375 return result;
376 }
377
378 #elif defined(Q_OS_MAC)
379 int
get_seafile_auto_start()380 get_seafile_auto_start()
381 {
382 return utils::mac::get_auto_start();
383 }
384
385 int
set_seafile_auto_start(bool on)386 set_seafile_auto_start(bool on)
387 {
388 bool was_on = utils::mac::get_auto_start();
389 if (on != was_on)
390 utils::mac::set_auto_start(on);
391 return on;
392 }
393 #else
394 int
get_seafile_auto_start()395 get_seafile_auto_start()
396 {
397 return 0;
398 }
399
400 int
set_seafile_auto_start(bool)401 set_seafile_auto_start(bool /* on */)
402 {
403 return 0;
404 }
405
406 #endif
407
408 int
set_seafile_dock_icon_style(bool hidden)409 set_seafile_dock_icon_style(bool hidden)
410 {
411 #if defined(Q_OS_MAC)
412 utils::mac::setDockIconStyle(hidden);
413 #endif
414 return 0;
415 }
416
parse_key_value_pairs(char * string,KeyValueFunc func,void * data)417 bool parse_key_value_pairs (char *string, KeyValueFunc func, void *data)
418 {
419 char *line = string, *next, *space;
420 char *key, *value;
421
422 while (*line) {
423 /* handle empty line */
424 if (*line == '\n') {
425 ++line;
426 continue;
427 }
428
429 for (next = line; *next != '\n' && *next; ++next) ;
430 *next = '\0';
431
432 for (space = line; space < next && *space != ' '; ++space) ;
433 if (*space != ' ') {
434 return false;
435 }
436 *space = '\0';
437 key = line;
438 value = space + 1;
439
440 if (func(data, key, value) == FALSE)
441 return false;
442
443 line = next + 1;
444 }
445 return true;
446 }
447
getBrand()448 QString getBrand()
449 {
450 return QString::fromUtf8(kSeafileClientBrand);
451 }
452
453 static
listFromJSON(json_t * array)454 QList<QVariant> listFromJSON(json_t *array)
455 {
456 QList<QVariant> ret;
457 size_t array_size = json_array_size(array);
458 json_t *value;
459
460 for(size_t index = 0; index < array_size &&
461 (value = json_array_get(array, index)); ++index) {
462 /* block of code that uses index and value */
463 QVariant v;
464 if (json_is_object(value)) {
465 v = mapFromJSON(value, NULL);
466 } else if (json_is_array(value)) {
467 v = listFromJSON(value);
468 } else if (json_is_string(value)) {
469 v = QString::fromUtf8(json_string_value(value));
470 } else if (json_is_integer(value)) {
471 v = json_integer_value(value);
472 } else if (json_is_real(value)) {
473 v = json_real_value(value);
474 } else if (json_is_boolean(value)) {
475 v = json_is_true(value);
476 }
477 if (v.isValid()) {
478 ret.push_back(v);
479 }
480 }
481 return ret;
482 }
483
mapFromJSON(json_t * json,json_error_t * error)484 QMap<QString, QVariant> mapFromJSON(json_t *json, json_error_t *error)
485 {
486 QMap<QString, QVariant> dict;
487 void *member;
488 const char *key;
489 json_t *value;
490
491 for (member = json_object_iter(json); member; member = json_object_iter_next(json, member)) {
492 key = json_object_iter_key(member);
493 value = json_object_iter_value(member);
494
495 QString k = QString::fromUtf8(key);
496 QVariant v;
497
498 // json_is_object(const json_t *json)
499 // json_is_array(const json_t *json)
500 // json_is_string(const json_t *json)
501 // json_is_integer(const json_t *json)
502 // json_is_real(const json_t *json)
503 // json_is_true(const json_t *json)
504 // json_is_false(const json_t *json)
505 // json_is_null(const json_t *json)
506 if (json_is_object(value)) {
507 v = mapFromJSON(value, NULL);
508 } else if (json_is_array(value)) {
509 v = listFromJSON(value);
510 } else if (json_is_string(value)) {
511 v = QString::fromUtf8(json_string_value(value));
512 } else if (json_is_integer(value)) {
513 v = json_integer_value(value);
514 } else if (json_is_real(value)) {
515 v = json_real_value(value);
516 } else if (json_is_boolean(value)) {
517 v = json_is_true(value);
518 }
519
520 if (v.isValid()) {
521 dict[k] = v;
522 }
523 }
524 return dict;
525 }
526
mapToJson(QMap<QString,QVariant> map)527 QString mapToJson(QMap<QString, QVariant> map)
528 {
529 json_t *object = NULL;
530 char *info = NULL;
531 object = json_object();
532
533 Q_FOREACH (const QString &k, map.keys()) {
534 QVariant v = map.value(k);
535 switch (v.type()) {
536 case QVariant::String:
537 json_object_set_new(object, toCStr(k), json_string(toCStr(v.toString())));
538 break;
539 case QVariant::Int:
540 json_object_set_new(object, toCStr(k), json_integer(v.toInt()));
541 break;
542 // TODO: support other types
543 default:
544 continue;
545 }
546 }
547
548 info = json_dumps(object, 0);
549 QString ret = QString::fromUtf8(info);
550 json_decref (object);
551 free (info);
552 return ret;
553 }
554
translateCommitTime(qint64 timestamp,bool hours_and_minutes)555 QString translateCommitTime(qint64 timestamp, bool hours_and_minutes) {
556 timestamp *= 1000; // use milli seconds
557 qint64 now = QDateTime::currentMSecsSinceEpoch();
558 if (now <= timestamp) {
559 return QObject::tr("Just now");
560 }
561
562 qint64 delta = (now - timestamp) / 1000;
563
564 qint64 secondsPerDay = 24 * 60 * 60;
565
566 qint64 days = delta / secondsPerDay;
567 qint64 seconds = delta % secondsPerDay;
568
569 QDateTime dt = QDateTime::fromMSecsSinceEpoch(timestamp);
570
571 if (hours_and_minutes) {
572 return dt.toString("yyyy-MM-dd HH:mm");
573 }
574
575 if (days >= 14) {
576 return dt.toString("yyyy-MM-dd");
577
578 } else if (days > 0) {
579 return days == 1 ? QObject::tr("1 day ago") : QObject::tr("%1 days ago").arg(days);
580
581 } else if (seconds >= 60 * 60) {
582 qint64 hours = seconds / 3600;
583 return hours == 1 ? QObject::tr("1 hour ago") : QObject::tr("%1 hours ago").arg(hours);
584
585 } else if (seconds >= 60) {
586 qint64 minutes = seconds / 60;
587 return minutes == 1 ? QObject::tr("1 minute ago") : QObject::tr("%1 minutes ago").arg(minutes);
588
589 } else if (seconds > 0) {
590 // return seconds == 1 ? QObject::tr("1 second ago") : QObject::tr("%1 seconds ago").arg(seconds);
591 return QObject::tr("Just now");
592
593 } else {
594 return QObject::tr("Just now");
595 }
596 }
597
readableFileSize(qint64 size)598 QString readableFileSize(qint64 size)
599 {
600 QString str;
601 double value = (double)size;
602 int precision = 1;
603
604 if (value < 1000) {
605 str = "B";
606 precision = 0;
607 } else if (value >= 1000 && value < 1000*1000) {
608 value = value / 1000;
609 str = "KB";
610 precision = 0;
611 } else if (value >= 1000*1000 && value < 1000*1000*1000) {
612 value = value / 1000 / 1000;
613 str = "MB";
614 } else if (value >= 1000*1000*1000) {
615 value = value / 1000 / 1000 / 1000;
616 str = "GB";
617 }
618
619 return QString::number(value, 'f', precision) + str;
620 }
621
readableFileSizeV2(qint64 size)622 QString readableFileSizeV2(qint64 size)
623 {
624 return readableFileSize(size);
625 }
626
627
md5(const QString & s)628 QString md5(const QString& s)
629 {
630 return QCryptographicHash::hash(s.toUtf8(), QCryptographicHash::Md5).toHex();
631 }
632
urlJoin(const QUrl & head,const QString & tail)633 QUrl urlJoin(const QUrl& head, const QString& tail)
634 {
635 QString a = head.toString();
636 QString b = tail;
637
638 if (!a.endsWith("/")) {
639 a += "/";
640 }
641 while (b.startsWith("/")) {
642 b = b.mid(1);
643 }
644 return QUrl(a + b);
645 }
646
removeDirRecursively(const QString & path)647 void removeDirRecursively(const QString &path)
648 {
649 QFileInfo file_info(path);
650 if (file_info.isDir()) {
651 QDir dir(path);
652 QStringList file_list = dir.entryList();
653 for (int i = 0; i < file_list.count(); ++i) {
654 removeDirRecursively(file_list.at(i));
655 }
656 removeDirRecursively(path);
657 } else {
658 QFile::remove(path);
659 }
660 }
661
dumpHexPresentation(const QByteArray & bytes)662 QString dumpHexPresentation(const QByteArray &bytes)
663 {
664 if (bytes.size() < 2)
665 return QString(bytes).toUpper();
666 QString output((char)bytes[0]);
667 output += (char)bytes[1];
668 for (int i = 2 ; i != bytes.size() ; i++) {
669 if (i % 2 == 0)
670 output += ':';
671 output += (char)bytes[i];
672 }
673 return output.toUpper();
674 }
675
dumpCipher(const QSslCipher & cipher)676 QString dumpCipher(const QSslCipher &cipher)
677 {
678 QString s = "\n";
679 s += "Authentication: " + cipher.authenticationMethod() + "\n";
680 s += "Encryption: " + cipher.encryptionMethod() + "\n";
681 s += "Key Exchange: " + cipher.keyExchangeMethod() + "\n";
682 s += "Cipher Name: " + cipher.name() + "\n";
683 s += "Protocol: " + cipher.protocolString() + "\n";
684 s += "Supported Bits: " + QString(cipher.supportedBits()) + "\n";
685 s += "Used Bits: " + QString(cipher.usedBits()) + "\n";
686 return s;
687 }
688
dumpCertificate(const QSslCertificate & cert)689 QString dumpCertificate(const QSslCertificate &cert)
690 {
691 if (cert.isNull())
692 return "\n-\n";
693
694 QString s = "\n";
695 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
696 s += cert.toText();
697 #else
698 QString s_none = QObject::tr("<Not Part of Certificate>");
699 #define CERTIFICATE_STR(x) ( ((x) == "" ) ? s_none : (x) )
700
701 s += "Certificate:\n";
702 s += "\nIssued To:\n";
703 s += "CommonName(CN): " + CERTIFICATE_STR(cert.subjectInfo(QSslCertificate::CommonName)) + "\n";
704 s += "Organization(O): " + CERTIFICATE_STR(cert.subjectInfo(QSslCertificate::Organization)) + "\n";
705 s += "OrganizationalUnitName(OU): " + CERTIFICATE_STR(cert.subjectInfo(QSslCertificate::OrganizationalUnitName)) + "\n";
706 s += "Serial Number: " + dumpHexPresentation(cert.serialNumber()) + "\n";
707
708 s += "\nIssued By:\n";
709 s += "CommonName(CN): " + CERTIFICATE_STR(cert.issuerInfo(QSslCertificate::CommonName)) + "\n";
710 s += "Organization(O): " + CERTIFICATE_STR(cert.issuerInfo(QSslCertificate::Organization)) + "\n";
711 s += "OrganizationalUnitName(OU): " + CERTIFICATE_STR(cert.issuerInfo(QSslCertificate::OrganizationalUnitName)) + "\n";
712
713 s += "\nPeriod Of Validity\n";
714 s += "Begins On: " + cert.effectiveDate().toString() + "\n";
715 s += "Expires On: " + cert.expiryDate().toString() + "\n";
716 s += "IsValid: " + (cert.isValid() ? QString("Yes") : QString("No")) + "\n";
717
718 s += "\nFingerprints\n";
719 s += "SHA1 Fingerprint:\n" + dumpCertificateFingerprint(cert, QCryptographicHash::Sha1) + "\n";
720 s += "MD5 Fingerprint:\n" + dumpCertificateFingerprint(cert, QCryptographicHash::Md5) + "\n";
721 #endif
722
723 s += "\n\n";
724 s += cert.toPem();
725
726 return s;
727 }
728
dumpCertificateFingerprint(const QSslCertificate & cert,const QCryptographicHash::Algorithm & algorithm)729 QString dumpCertificateFingerprint(const QSslCertificate &cert, const QCryptographicHash::Algorithm &algorithm)
730 {
731 if(cert.isNull())
732 return "";
733 return dumpHexPresentation(cert.digest(algorithm).toHex());
734 }
735
dumpSslErrors(const QList<QSslError> & errors)736 QString dumpSslErrors(const QList<QSslError> &errors)
737 {
738 QString s;
739 foreach (const QSslError &error, errors) {
740 s += error.errorString() + "\n";
741 }
742 return s;
743 }
744
msleep(int mseconds)745 void msleep(int mseconds)
746 {
747 #ifdef Q_OS_WIN32
748 ::Sleep(mseconds);
749 #else
750 struct timespec ts;
751 ts.tv_sec = mseconds / 1000;
752 ts.tv_nsec = mseconds % 1000 * 1000 * 1000;
753
754 int r;
755 do {
756 r = ::nanosleep(&ts, &ts);
757 } while (r == -1 && errno == EINTR);
758 #endif
759 }
760
includeQueryParams(const QUrl & url,const QHash<QString,QString> & params)761 QUrl includeQueryParams(const QUrl& url,
762 const QHash<QString, QString>& params)
763 {
764 QUrl u(url);
765 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
766 QUrlQuery query;
767 Q_FOREACH (const QString& key, params.keys()) {
768 QString value = params[key];
769 query.addQueryItem(QUrl::toPercentEncoding(key),
770 QUrl::toPercentEncoding(value));
771 }
772 u.setQuery(query);
773 #else
774 Q_FOREACH (const QString& key, params.keys()) {
775 QString value = params[key];
776 u.addEncodedQueryItem(QUrl::toPercentEncoding(key),
777 QUrl::toPercentEncoding(value));
778 }
779 #endif
780 return u;
781 }
782
buildFormData(const QHash<QString,QString> & params)783 QByteArray buildFormData(const QHash<QString, QString>& params)
784 {
785 #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
786 QUrlQuery query;
787 Q_FOREACH (const QString& key, params.keys()) {
788 QString value = params[key];
789 query.addQueryItem(QUrl::toPercentEncoding(key),
790 QUrl::toPercentEncoding(value));
791
792 }
793 return query.query(QUrl::FullyEncoded).toUtf8();
794 #else
795 QUrl u;
796 Q_FOREACH (const QString& key, params.keys()) {
797 QString value = params[key];
798 u.addEncodedQueryItem(QUrl::toPercentEncoding(key),
799 QUrl::toPercentEncoding(value));
800 }
801 return u.encodedQuery();
802 #endif
803 }
804
digitalCompare(const QString & left,const QString & right)805 int digitalCompare(const QString &left, const QString &right)
806 {
807 int ret = 0;
808 if (left.compare(right, Qt::CaseInsensitive) == 0)
809 return ret;
810 if (left.size() == 0)
811 return -1;
812 if (right.size() == 0)
813 return 1;
814
815 QString left_sub = left;
816 QString right_sub = right;
817 const uint min_size = left.size() < right.size()
818 ? left.size() : right.size();
819 uint i;
820 for (i = 0; i != min_size; i++) {
821 if (left[i].isDigit() && right[i].isDigit())
822 break;
823 if (left[i] != right[i])
824 return left.compare(right, Qt::CaseInsensitive);
825 }
826 left_sub = left_sub.right(left_sub.size() - i);
827 right_sub = right_sub.right(right_sub.size() - i);
828
829 const QRegExp left_digit_pattern("(\\d+)*");
830 const QRegExp right_digit_pattern("(\\d+)*");
831 const int left_pos = left_digit_pattern.indexIn(left_sub);
832 const int right_pos = right_digit_pattern.indexIn(right_sub);
833 if (left_pos == 0 && right_pos == 0) {
834 quint64 left_digit = left_digit_pattern.cap(1).toUInt();
835 quint64 right_digit = right_digit_pattern.cap(1).toUInt();
836 if (left_digit == right_digit) {
837 left_sub = left_sub.right(left_sub.size() -
838 left_digit_pattern.cap(1).size());
839 right_sub = right_sub.right(right_sub.size() -
840 right_digit_pattern.cap(1).size());
841 return digitalCompare(left_sub, right_sub);
842 }
843 return left_digit - right_digit;
844 }
845 return left.compare(right, Qt::CaseInsensitive);
846 }
847
shouldUseFramelessWindow()848 bool shouldUseFramelessWindow()
849 {
850 static int _shouldUseFramelessWindow = -1;
851
852 if (_shouldUseFramelessWindow < 0) {
853 _shouldUseFramelessWindow = 1;
854 #if defined(Q_OS_MAC)
855 _shouldUseFramelessWindow = 0;
856 #elif defined(Q_OS_WIN32)
857 if (utils::win::isWindows10OrHigher()) {
858 _shouldUseFramelessWindow = 0;
859 }
860 #endif
861 }
862
863 return _shouldUseFramelessWindow > 0;
864 }
865