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