1 /*
2     Copyright © 2013 by Maxim Biro <nurupo.contributions@gmail.com>
3     Copyright © 2014-2019 by The qTox Project Contributors
4 
5     This file is part of qTox, a Qt-based graphical interface for Tox.
6 
7     qTox is libre software: you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation, either version 3 of the License, or
10     (at your option) any later version.
11 
12     qTox is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with qTox.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "settings.h"
22 #include "src/core/core.h"
23 #include "src/core/corefile.h"
24 #include "src/nexus.h"
25 #include "src/persistence/profile.h"
26 #include "src/persistence/profilelocker.h"
27 #include "src/persistence/settingsserializer.h"
28 #include "src/persistence/smileypack.h"
29 #include "src/widget/gui.h"
30 #include "src/widget/style.h"
31 #ifdef QTOX_PLATFORM_EXT
32 #include "src/platform/autorun.h"
33 #endif
34 #include "src/ipc.h"
35 
36 #include "src/util/compatiblerecursivemutex.h"
37 
38 #include <QApplication>
39 #include <QCryptographicHash>
40 #include <QDebug>
41 #include <QDir>
42 #include <QFile>
43 #include <QFont>
44 #include <QList>
45 #include <QMutexLocker>
46 #include <QNetworkProxy>
47 #include <QStandardPaths>
48 #include <QStyleFactory>
49 #include <QThread>
50 #include <QtCore/QCommandLineParser>
51 
52 /**
53  * @var QHash<QString, QByteArray> Settings::widgetSettings
54  * @brief Assume all widgets have unique names
55  * @warning Don't use it to save every single thing you want to save, use it
56  * for some general purpose widgets, such as MainWindows or Splitters,
57  * which have widget->saveX() and widget->loadX() methods.
58  */
59 
60 const QString Settings::globalSettingsFile = "qtox.ini";
61 Settings* Settings::settings{nullptr};
62 CompatibleRecursiveMutex Settings::bigLock;
63 QThread* Settings::settingsThread{nullptr};
64 
Settings()65 Settings::Settings()
66     : loaded(false)
67     , useCustomDhtList{false}
68     , makeToxPortable{false}
69     , currentProfileId(0)
70 {
71     settingsThread = new QThread();
72     settingsThread->setObjectName("qTox Settings");
73     settingsThread->start(QThread::LowPriority);
74     moveToThread(settingsThread);
75     loadGlobal();
76 }
77 
~Settings()78 Settings::~Settings()
79 {
80     sync();
81     settingsThread->exit(0);
82     settingsThread->wait();
83     delete settingsThread;
84 }
85 
86 /**
87  * @brief Returns the singleton instance.
88  */
getInstance()89 Settings& Settings::getInstance()
90 {
91     if (!settings)
92         settings = new Settings();
93 
94     return *settings;
95 }
96 
destroyInstance()97 void Settings::destroyInstance()
98 {
99     delete settings;
100     settings = nullptr;
101 }
102 
loadGlobal()103 void Settings::loadGlobal()
104 {
105     QMutexLocker locker{&bigLock};
106 
107     if (loaded)
108         return;
109 
110     createSettingsDir();
111 
112     makeToxPortable = Settings::isToxPortable();
113 
114     QDir dir(getSettingsDirPath());
115     QString filePath = dir.filePath(globalSettingsFile);
116 
117     // If no settings file exist -- use the default one
118     if (!QFile(filePath).exists()) {
119         qDebug() << "No settings file found, using defaults";
120         filePath = ":/conf/" + globalSettingsFile;
121     }
122 
123     qDebug() << "Loading settings from " + filePath;
124 
125     QSettings s(filePath, QSettings::IniFormat);
126     s.setIniCodec("UTF-8");
127 
128     s.beginGroup("Login");
129     {
130         autoLogin = s.value("autoLogin", false).toBool();
131     }
132     s.endGroup();
133 
134     s.beginGroup("General");
135     {
136         translation = s.value("translation", "en").toString();
137         showSystemTray = s.value("showSystemTray", true).toBool();
138         autostartInTray = s.value("autostartInTray", false).toBool();
139         closeToTray = s.value("closeToTray", false).toBool();
140         if (currentProfile.isEmpty()) {
141             currentProfile = s.value("currentProfile", "").toString();
142             currentProfileId = makeProfileId(currentProfile);
143         }
144         autoAwayTime = s.value("autoAwayTime", 10).toInt();
145         checkUpdates = s.value("checkUpdates", true).toBool();
146         // note: notifySound and busySound UI elements are now under UI settings
147         // page, but kept under General in settings file to be backwards compatible
148         notifySound = s.value("notifySound", true).toBool();
149         notifyHide = s.value("notifyHide", false).toBool();
150         busySound = s.value("busySound", false).toBool();
151         autoSaveEnabled = s.value("autoSaveEnabled", false).toBool();
152         globalAutoAcceptDir = s.value("globalAutoAcceptDir",
153                                       QStandardPaths::locate(QStandardPaths::HomeLocation, QString(),
154                                                              QStandardPaths::LocateDirectory))
155                                   .toString();
156         autoAcceptMaxSize =
157             static_cast<size_t>(s.value("autoAcceptMaxSize", 20 << 20 /*20 MB*/).toLongLong());
158         stylePreference = static_cast<StyleType>(s.value("stylePreference", 1).toInt());
159     }
160     s.endGroup();
161 
162     s.beginGroup("Advanced");
163     {
164         makeToxPortable = s.value("makeToxPortable", false).toBool();
165         enableIPv6 = s.value("enableIPv6", true).toBool();
166         forceTCP = s.value("forceTCP", false).toBool();
167         enableLanDiscovery = s.value("enableLanDiscovery", true).toBool();
168     }
169     s.endGroup();
170 
171     s.beginGroup("Widgets");
172     {
173         QList<QString> objectNames = s.childKeys();
174         for (const QString& name : objectNames)
175             widgetSettings[name] = s.value(name).toByteArray();
176     }
177     s.endGroup();
178 
179     s.beginGroup("GUI");
180     {
181         showWindow = s.value("showWindow", true).toBool();
182         notify = s.value("notify", true).toBool();
183         desktopNotify = s.value("desktopNotify", true).toBool();
184         groupAlwaysNotify = s.value("groupAlwaysNotify", true).toBool();
185         groupchatPosition = s.value("groupchatPosition", true).toBool();
186         separateWindow = s.value("separateWindow", false).toBool();
187         dontGroupWindows = s.value("dontGroupWindows", false).toBool();
188         showIdenticons = s.value("showIdenticons", true).toBool();
189 
190         const QString DEFAULT_SMILEYS = ":/smileys/emojione/emoticons.xml";
191         smileyPack = s.value("smileyPack", DEFAULT_SMILEYS).toString();
192         if (!QFile::exists(smileyPack)) {
193             smileyPack = DEFAULT_SMILEYS;
194         }
195 
196         emojiFontPointSize = s.value("emojiFontPointSize", 24).toInt();
197         firstColumnHandlePos = s.value("firstColumnHandlePos", 50).toInt();
198         secondColumnHandlePosFromRight = s.value("secondColumnHandlePosFromRight", 50).toInt();
199         timestampFormat = s.value("timestampFormat", "hh:mm:ss").toString();
200         dateFormat = s.value("dateFormat", "yyyy-MM-dd").toString();
201         minimizeOnClose = s.value("minimizeOnClose", false).toBool();
202         minimizeToTray = s.value("minimizeToTray", false).toBool();
203         lightTrayIcon = s.value("lightTrayIcon", false).toBool();
204         useEmoticons = s.value("useEmoticons", true).toBool();
205         statusChangeNotificationEnabled = s.value("statusChangeNotificationEnabled", false).toBool();
206         spellCheckingEnabled = s.value("spellCheckingEnabled", true).toBool();
207         themeColor = s.value("themeColor", 0).toInt();
208         style = s.value("style", "").toString();
209         if (style == "") // Default to Fusion if available, otherwise no style
210         {
211             if (QStyleFactory::keys().contains("Fusion"))
212                 style = "Fusion";
213             else
214                 style = "None";
215         }
216         nameColors = s.value("nameColors", false).toBool();
217     }
218     s.endGroup();
219 
220     s.beginGroup("Chat");
221     {
222         chatMessageFont = s.value("chatMessageFont", Style::getFont(Style::Big)).value<QFont>();
223     }
224     s.endGroup();
225 
226     s.beginGroup("State");
227     {
228         windowGeometry = s.value("windowGeometry", QByteArray()).toByteArray();
229         windowState = s.value("windowState", QByteArray()).toByteArray();
230         splitterState = s.value("splitterState", QByteArray()).toByteArray();
231         dialogGeometry = s.value("dialogGeometry", QByteArray()).toByteArray();
232         dialogSplitterState = s.value("dialogSplitterState", QByteArray()).toByteArray();
233         dialogSettingsGeometry = s.value("dialogSettingsGeometry", QByteArray()).toByteArray();
234     }
235     s.endGroup();
236 
237     s.beginGroup("Audio");
238     {
239         inDev = s.value("inDev", "").toString();
240         audioInDevEnabled = s.value("audioInDevEnabled", true).toBool();
241         outDev = s.value("outDev", "").toString();
242         audioOutDevEnabled = s.value("audioOutDevEnabled", true).toBool();
243         audioInGainDecibel = s.value("inGain", 0).toReal();
244         audioThreshold = s.value("audioThreshold", 0).toReal();
245         outVolume = s.value("outVolume", 100).toInt();
246         enableTestSound = s.value("enableTestSound", true).toBool();
247         audioBitrate = s.value("audioBitrate", 64).toInt();
248     }
249     s.endGroup();
250 
251     s.beginGroup("Video");
252     {
253         videoDev = s.value("videoDev", "").toString();
254         camVideoRes = s.value("camVideoRes", QRect()).toRect();
255         screenRegion = s.value("screenRegion", QRect()).toRect();
256         screenGrabbed = s.value("screenGrabbed", false).toBool();
257         camVideoFPS = static_cast<quint16>(s.value("camVideoFPS", 0).toUInt());
258     }
259     s.endGroup();
260 
261     loaded = true;
262 }
263 
isToxPortable()264 bool Settings::isToxPortable()
265 {
266     QString localSettingsPath = qApp->applicationDirPath() + QDir::separator() + globalSettingsFile;
267     if (!QFile(localSettingsPath).exists()) {
268         return false;
269     }
270     QSettings ps(localSettingsPath, QSettings::IniFormat);
271     ps.setIniCodec("UTF-8");
272     ps.beginGroup("Advanced");
273     bool result = ps.value("makeToxPortable", false).toBool();
274     ps.endGroup();
275     return result;
276 }
277 
updateProfileData(Profile * profile,const QCommandLineParser * parser)278 void Settings::updateProfileData(Profile* profile, const QCommandLineParser* parser)
279 {
280     QMutexLocker locker{&bigLock};
281 
282     if (profile == nullptr) {
283         qWarning() << QString("Could not load new settings (profile change to nullptr)");
284         return;
285     }
286     setCurrentProfile(profile->getName());
287     saveGlobal();
288     loadPersonal(profile->getName(), profile->getPasskey());
289     if (parser) {
290         applyCommandLineOptions(*parser);
291     }
292 }
293 
294 /**
295  * Verifies that commandline proxy settings are at least reasonable. Does not verify provided IP
296  * or hostname addresses are valid. Code duplication with Settings::applyCommandLineOptions, which
297  * also verifies arguments, should be removed in a future refactor.
298  * @param parser QCommandLineParser instance
299  */
verifyProxySettings(const QCommandLineParser & parser)300 bool Settings::verifyProxySettings(const QCommandLineParser& parser)
301 {
302     QString IPv6SettingString = parser.value("I").toLower();
303     QString LANSettingString = parser.value("L").toLower();
304     QString UDPSettingString = parser.value("U").toLower();
305     QString proxySettingString = parser.value("proxy").toLower();
306     QStringList proxySettingStrings = proxySettingString.split(":");
307 
308     const QString SOCKS5 = QStringLiteral("socks5");
309     const QString HTTP = QStringLiteral("http");
310     const QString NONE = QStringLiteral("none");
311     const QString ON = QStringLiteral("on");
312     const QString OFF = QStringLiteral("off");
313 
314     // Check for incompatible settings
315     bool activeProxyType = false;
316 
317     if (parser.isSet("P")) {
318         activeProxyType = proxySettingStrings[0] == SOCKS5 || proxySettingStrings[0] == HTTP;
319     }
320 
321     if (parser.isSet("I")) {
322         if (!(IPv6SettingString == ON || IPv6SettingString == OFF)) {
323             qCritical() << "Unable to parse IPv6 setting.";
324             return false;
325         }
326     }
327 
328     if (parser.isSet("U")) {
329         if (!(UDPSettingString == ON || UDPSettingString == OFF)) {
330             qCritical() << "Unable to parse UDP setting.";
331             return false;
332         }
333     }
334 
335     if (parser.isSet("L")) {
336         if (!(LANSettingString == ON || LANSettingString == OFF)) {
337             qCritical() << "Unable to parse LAN setting.";
338             return false;
339         }
340     }
341     if (activeProxyType && UDPSettingString == ON) {
342         qCritical() << "Cannot set UDP on with proxy.";
343         return false;
344     }
345 
346     if (activeProxyType && LANSettingString == ON) {
347         qCritical() << "Cannot set LAN discovery on with proxy.";
348         return false;
349     }
350 
351     if (LANSettingString == ON && UDPSettingString == OFF) {
352         qCritical() << "Incompatible UDP/LAN settings.";
353         return false;
354     }
355 
356     if (parser.isSet("P")) {
357         if (proxySettingStrings[0] == NONE) {
358             // slightly lazy check here, accepting 'NONE[:.*]' is fine since no other
359             // arguments will be investigated when proxy settings are applied.
360             return true;
361         }
362         // Since the first argument isn't 'none', verify format of remaining arguments
363         if (proxySettingStrings.size() != 3) {
364             qCritical() << "Invalid number of proxy arguments.";
365             return false;
366         }
367 
368         if (!(proxySettingStrings[0] == SOCKS5 || proxySettingStrings[0] == HTTP)) {
369             qCritical() << "Unable to parse proxy type.";
370             return false;
371         }
372 
373         // TODO(Kriby): Sanity check IPv4/IPv6 addresses/hostnames?
374 
375         int portNumber = proxySettingStrings[2].toInt();
376         if (!(portNumber > 0 && portNumber < 65536)) {
377             qCritical() << "Invalid port number range.";
378         }
379     }
380     return true;
381 }
382 
383 /**
384  * Applies command line options on top of loaded settings. Fails without changes if attempting to
385  * apply contradicting settings.
386  * @param parser QCommandLineParser instance
387  * @return Success indicator (success = true)
388  */
applyCommandLineOptions(const QCommandLineParser & parser)389 bool Settings::applyCommandLineOptions(const QCommandLineParser& parser)
390 {
391     if (!verifyProxySettings(parser)) {
392         return false;
393     };
394 
395     QString IPv6Setting = parser.value("I").toUpper();
396     QString LANSetting = parser.value("L").toUpper();
397     QString UDPSetting = parser.value("U").toUpper();
398     QString proxySettingString = parser.value("proxy").toUpper();
399     QStringList proxySettings = proxySettingString.split(":");
400 
401     const QString SOCKS5 = QStringLiteral("SOCKS5");
402     const QString HTTP = QStringLiteral("HTTP");
403     const QString NONE = QStringLiteral("NONE");
404     const QString ON = QStringLiteral("ON");
405     const QString OFF = QStringLiteral("OFF");
406 
407 
408     if (parser.isSet("I")) {
409         enableIPv6 = IPv6Setting == ON;
410         qDebug() << QString("Setting IPv6 %1.").arg(IPv6Setting);
411     }
412 
413     if (parser.isSet("P")) {
414         qDebug() << QString("Setting proxy type to %1.").arg(proxySettings[0]);
415 
416         quint16 portNumber = 0;
417         QString address = "";
418 
419         if (proxySettings[0] == NONE) {
420             proxyType = ICoreSettings::ProxyType::ptNone;
421         } else {
422             if (proxySettings[0] == SOCKS5) {
423                 proxyType = ICoreSettings::ProxyType::ptSOCKS5;
424             } else if (proxySettings[0] == HTTP) {
425                 proxyType = ICoreSettings::ProxyType::ptHTTP;
426             } else {
427                 qCritical() << "Failed to set valid proxy type";
428                 assert(false); // verifyProxySettings should've made this impossible
429             }
430 
431             forceTCP = true;
432             enableLanDiscovery = false;
433 
434             address = proxySettings[1];
435             portNumber = static_cast<quint16>(proxySettings[2].toInt());
436         }
437 
438 
439         proxyAddr = address;
440         qDebug() << QString("Setting proxy address to %1.").arg(address);
441         proxyPort = portNumber;
442         qDebug() << QString("Setting port number to %1.").arg(portNumber);
443     }
444 
445     if (parser.isSet("U")) {
446         bool shouldForceTCP = UDPSetting == OFF;
447         if (!shouldForceTCP && proxyType != ICoreSettings::ProxyType::ptNone) {
448             qDebug() << "Cannot use UDP with proxy; disable proxy explicitly with '-P none'.";
449         } else {
450             forceTCP = shouldForceTCP;
451             qDebug() << QString("Setting UDP %1.").arg(UDPSetting);
452         }
453 
454         // LANSetting == ON is caught by verifyProxySettings, the OFF check removes needless debug
455         if (shouldForceTCP && !(LANSetting == OFF) && enableLanDiscovery) {
456             qDebug() << "Cannot perform LAN discovery without UDP; disabling LAN discovery.";
457             enableLanDiscovery = false;
458         }
459     }
460 
461     if (parser.isSet("L")) {
462         bool shouldEnableLAN = LANSetting == ON;
463 
464         if (shouldEnableLAN && proxyType != ICoreSettings::ProxyType::ptNone) {
465             qDebug()
466                 << "Cannot use LAN discovery with proxy; disable proxy explicitly with '-P none'.";
467         } else if (shouldEnableLAN && forceTCP) {
468             qDebug() << "Cannot use LAN discovery without UDP; enable UDP explicitly with '-U on'.";
469         } else {
470             enableLanDiscovery = shouldEnableLAN;
471             qDebug() << QString("Setting LAN Discovery %1.").arg(LANSetting);
472         }
473     }
474     return true;
475 }
476 
loadPersonal(QString profileName,const ToxEncrypt * passKey)477 void Settings::loadPersonal(QString profileName, const ToxEncrypt* passKey)
478 {
479     QMutexLocker locker{&bigLock};
480 
481     QDir dir(getSettingsDirPath());
482     QString filePath = dir.filePath(globalSettingsFile);
483 
484     // load from a profile specific friend data list if possible
485     QString tmp = dir.filePath(profileName + ".ini");
486     if (QFile(tmp).exists()) // otherwise, filePath remains the global file
487         filePath = tmp;
488 
489     qDebug() << "Loading personal settings from" << filePath;
490 
491     SettingsSerializer ps(filePath, passKey);
492     ps.load();
493     friendLst.clear();
494 
495     ps.beginGroup("Privacy");
496     {
497         typingNotification = ps.value("typingNotification", true).toBool();
498         enableLogging = ps.value("enableLogging", true).toBool();
499         blackList = ps.value("blackList").toString().split('\n');
500     }
501     ps.endGroup();
502 
503     ps.beginGroup("Friends");
504     {
505         int size = ps.beginReadArray("Friend");
506         friendLst.reserve(size);
507         for (int i = 0; i < size; i++) {
508             ps.setArrayIndex(i);
509             friendProp fp{ps.value("addr").toString()};
510             fp.alias = ps.value("alias").toString();
511             fp.note = ps.value("note").toString();
512             fp.autoAcceptDir = ps.value("autoAcceptDir").toString();
513 
514             if (fp.autoAcceptDir == "")
515                 fp.autoAcceptDir = ps.value("autoAccept").toString();
516 
517             fp.autoAcceptCall =
518                 Settings::AutoAcceptCallFlags(QFlag(ps.value("autoAcceptCall", 0).toInt()));
519             fp.autoGroupInvite = ps.value("autoGroupInvite").toBool();
520             fp.circleID = ps.value("circle", -1).toInt();
521 
522             if (getEnableLogging())
523                 fp.activity = ps.value("activity", QDateTime()).toDateTime();
524             friendLst.insert(ToxId(fp.addr).getPublicKey().getByteArray(), fp);
525         }
526         ps.endArray();
527     }
528     ps.endGroup();
529 
530     ps.beginGroup("Requests");
531     {
532         int size = ps.beginReadArray("Request");
533         friendRequests.clear();
534         friendRequests.reserve(size);
535         for (int i = 0; i < size; i++) {
536             ps.setArrayIndex(i);
537             Request request;
538             request.address = ps.value("addr").toString();
539             request.message = ps.value("message").toString();
540             request.read = ps.value("read").toBool();
541             friendRequests.push_back(request);
542         }
543         ps.endArray();
544     }
545     ps.endGroup();
546 
547     ps.beginGroup("GUI");
548     {
549         compactLayout = ps.value("compactLayout", true).toBool();
550         sortingMode = static_cast<FriendListSortingMode>(
551             ps.value("friendSortingMethod", static_cast<int>(FriendListSortingMode::Name)).toInt());
552     }
553     ps.endGroup();
554 
555     ps.beginGroup("Proxy");
556     {
557         proxyType = static_cast<ProxyType>(ps.value("proxyType", 0 /* ProxyType::None */).toInt());
558         proxyType = fixInvalidProxyType(proxyType);
559         proxyAddr = ps.value("proxyAddr", proxyAddr).toString();
560         proxyPort = static_cast<quint16>(ps.value("proxyPort", proxyPort).toUInt());
561     }
562     ps.endGroup();
563 
564     ps.beginGroup("Circles");
565     {
566         int size = ps.beginReadArray("Circle");
567         circleLst.clear();
568         circleLst.reserve(size);
569         for (int i = 0; i < size; i++) {
570             ps.setArrayIndex(i);
571             circleProp cp;
572             cp.name = ps.value("name").toString();
573             cp.expanded = ps.value("expanded", true).toBool();
574             circleLst.push_back(cp);
575         }
576         ps.endArray();
577     }
578     ps.endGroup();
579 }
580 
resetToDefault()581 void Settings::resetToDefault()
582 {
583     // To stop saving
584     loaded = false;
585 
586     // Remove file with profile settings
587     QDir dir(getSettingsDirPath());
588     Profile* profile = Nexus::getProfile();
589     QString localPath = dir.filePath(profile->getName() + ".ini");
590     QFile local(localPath);
591     if (local.exists())
592         local.remove();
593 }
594 
595 /**
596  * @brief Asynchronous, saves the global settings.
597  */
saveGlobal()598 void Settings::saveGlobal()
599 {
600     if (QThread::currentThread() != settingsThread)
601         return (void)QMetaObject::invokeMethod(&getInstance(), "saveGlobal");
602 
603     QMutexLocker locker{&bigLock};
604     if (!loaded)
605         return;
606 
607     QString path = getSettingsDirPath() + globalSettingsFile;
608     qDebug() << "Saving global settings at " + path;
609 
610     QSettings s(path, QSettings::IniFormat);
611     s.setIniCodec("UTF-8");
612 
613     s.clear();
614 
615     s.beginGroup("Login");
616     {
617         s.setValue("autoLogin", autoLogin);
618     }
619     s.endGroup();
620 
621     s.beginGroup("General");
622     {
623         s.setValue("translation", translation);
624         s.setValue("showSystemTray", showSystemTray);
625         s.setValue("autostartInTray", autostartInTray);
626         s.setValue("closeToTray", closeToTray);
627         s.setValue("currentProfile", currentProfile);
628         s.setValue("autoAwayTime", autoAwayTime);
629         s.setValue("checkUpdates", checkUpdates);
630         s.setValue("notifySound", notifySound);
631         s.setValue("notifyHide", notifyHide);
632         s.setValue("busySound", busySound);
633         s.setValue("autoSaveEnabled", autoSaveEnabled);
634         s.setValue("autoAcceptMaxSize", static_cast<qlonglong>(autoAcceptMaxSize));
635         s.setValue("globalAutoAcceptDir", globalAutoAcceptDir);
636         s.setValue("stylePreference", static_cast<int>(stylePreference));
637     }
638     s.endGroup();
639 
640     s.beginGroup("Advanced");
641     {
642         s.setValue("makeToxPortable", makeToxPortable);
643         s.setValue("enableIPv6", enableIPv6);
644         s.setValue("forceTCP", forceTCP);
645         s.setValue("enableLanDiscovery", enableLanDiscovery);
646         s.setValue("dbSyncType", static_cast<int>(dbSyncType));
647     }
648     s.endGroup();
649 
650     s.beginGroup("Widgets");
651     {
652         const QList<QString> widgetNames = widgetSettings.keys();
653         for (const QString& name : widgetNames)
654             s.setValue(name, widgetSettings.value(name));
655     }
656     s.endGroup();
657 
658     s.beginGroup("GUI");
659     {
660         s.setValue("showWindow", showWindow);
661         s.setValue("notify", notify);
662         s.setValue("desktopNotify", desktopNotify);
663         s.setValue("groupAlwaysNotify", groupAlwaysNotify);
664         s.setValue("separateWindow", separateWindow);
665         s.setValue("dontGroupWindows", dontGroupWindows);
666         s.setValue("groupchatPosition", groupchatPosition);
667         s.setValue("showIdenticons", showIdenticons);
668 
669         s.setValue("smileyPack", smileyPack);
670         s.setValue("emojiFontPointSize", emojiFontPointSize);
671         s.setValue("firstColumnHandlePos", firstColumnHandlePos);
672         s.setValue("secondColumnHandlePosFromRight", secondColumnHandlePosFromRight);
673         s.setValue("timestampFormat", timestampFormat);
674         s.setValue("dateFormat", dateFormat);
675         s.setValue("minimizeOnClose", minimizeOnClose);
676         s.setValue("minimizeToTray", minimizeToTray);
677         s.setValue("lightTrayIcon", lightTrayIcon);
678         s.setValue("useEmoticons", useEmoticons);
679         s.setValue("themeColor", themeColor);
680         s.setValue("style", style);
681         s.setValue("nameColors", nameColors);
682         s.setValue("statusChangeNotificationEnabled", statusChangeNotificationEnabled);
683         s.setValue("spellCheckingEnabled", spellCheckingEnabled);
684     }
685     s.endGroup();
686 
687     s.beginGroup("Chat");
688     {
689         s.setValue("chatMessageFont", chatMessageFont);
690     }
691     s.endGroup();
692 
693     s.beginGroup("State");
694     {
695         s.setValue("windowGeometry", windowGeometry);
696         s.setValue("windowState", windowState);
697         s.setValue("splitterState", splitterState);
698         s.setValue("dialogGeometry", dialogGeometry);
699         s.setValue("dialogSplitterState", dialogSplitterState);
700         s.setValue("dialogSettingsGeometry", dialogSettingsGeometry);
701     }
702     s.endGroup();
703 
704     s.beginGroup("Audio");
705     {
706         s.setValue("inDev", inDev);
707         s.setValue("audioInDevEnabled", audioInDevEnabled);
708         s.setValue("outDev", outDev);
709         s.setValue("audioOutDevEnabled", audioOutDevEnabled);
710         s.setValue("inGain", audioInGainDecibel);
711         s.setValue("audioThreshold", audioThreshold);
712         s.setValue("outVolume", outVolume);
713         s.setValue("enableTestSound", enableTestSound);
714         s.setValue("audioBitrate", audioBitrate);
715     }
716     s.endGroup();
717 
718     s.beginGroup("Video");
719     {
720         s.setValue("videoDev", videoDev);
721         s.setValue("camVideoRes", camVideoRes);
722         s.setValue("camVideoFPS", camVideoFPS);
723         s.setValue("screenRegion", screenRegion);
724         s.setValue("screenGrabbed", screenGrabbed);
725     }
726     s.endGroup();
727 }
728 
729 /**
730  * @brief Asynchronous, saves the current profile.
731  */
savePersonal()732 void Settings::savePersonal()
733 {
734     savePersonal(Nexus::getProfile());
735 }
736 
737 /**
738  * @brief Asynchronous, saves the profile.
739  * @param profile Profile to save.
740  */
savePersonal(Profile * profile)741 void Settings::savePersonal(Profile* profile)
742 {
743     if (!profile) {
744         qDebug() << "Could not save personal settings because there is no active profile";
745         return;
746     }
747     if (QThread::currentThread() != settingsThread)
748         return (void)QMetaObject::invokeMethod(&getInstance(), "savePersonal",
749                                                Q_ARG(Profile*, profile));
750     savePersonal(profile->getName(), profile->getPasskey());
751 }
752 
savePersonal(QString profileName,const ToxEncrypt * passkey)753 void Settings::savePersonal(QString profileName, const ToxEncrypt* passkey)
754 {
755     QMutexLocker locker{&bigLock};
756     if (!loaded)
757         return;
758 
759     QString path = getSettingsDirPath() + profileName + ".ini";
760 
761     qDebug() << "Saving personal settings at " << path;
762 
763     SettingsSerializer ps(path, passkey);
764     ps.beginGroup("Friends");
765     {
766         ps.beginWriteArray("Friend", friendLst.size());
767         int index = 0;
768         for (auto& frnd : friendLst) {
769             ps.setArrayIndex(index);
770             ps.setValue("addr", frnd.addr);
771             ps.setValue("alias", frnd.alias);
772             ps.setValue("note", frnd.note);
773             ps.setValue("autoAcceptDir", frnd.autoAcceptDir);
774             ps.setValue("autoAcceptCall", static_cast<int>(frnd.autoAcceptCall));
775             ps.setValue("autoGroupInvite", frnd.autoGroupInvite);
776             ps.setValue("circle", frnd.circleID);
777 
778             if (getEnableLogging())
779                 ps.setValue("activity", frnd.activity);
780 
781             ++index;
782         }
783         ps.endArray();
784     }
785     ps.endGroup();
786 
787     ps.beginGroup("Requests");
788     {
789         ps.beginWriteArray("Request", friendRequests.size());
790         int index = 0;
791         for (auto& request : friendRequests) {
792             ps.setArrayIndex(index);
793             ps.setValue("addr", request.address);
794             ps.setValue("message", request.message);
795             ps.setValue("read", request.read);
796 
797             ++index;
798         }
799         ps.endArray();
800     }
801     ps.endGroup();
802 
803     ps.beginGroup("GUI");
804     {
805         ps.setValue("compactLayout", compactLayout);
806         ps.setValue("friendSortingMethod", static_cast<int>(sortingMode));
807     }
808     ps.endGroup();
809 
810     ps.beginGroup("Proxy");
811     {
812         ps.setValue("proxyType", static_cast<int>(proxyType));
813         ps.setValue("proxyAddr", proxyAddr);
814         ps.setValue("proxyPort", proxyPort);
815     }
816     ps.endGroup();
817 
818     ps.beginGroup("Circles");
819     {
820         ps.beginWriteArray("Circle", circleLst.size());
821         int index = 0;
822         for (auto& circle : circleLst) {
823             ps.setArrayIndex(index);
824             ps.setValue("name", circle.name);
825             ps.setValue("expanded", circle.expanded);
826             ++index;
827         }
828         ps.endArray();
829     }
830     ps.endGroup();
831 
832     ps.beginGroup("Privacy");
833     {
834         ps.setValue("typingNotification", typingNotification);
835         ps.setValue("enableLogging", enableLogging);
836         ps.setValue("blackList", blackList.join('\n'));
837     }
838     ps.endGroup();
839     ps.save();
840 }
841 
makeProfileId(const QString & profile)842 uint32_t Settings::makeProfileId(const QString& profile)
843 {
844     QByteArray data = QCryptographicHash::hash(profile.toUtf8(), QCryptographicHash::Md5);
845     const uint32_t* dwords = reinterpret_cast<const uint32_t*>(data.constData());
846     return dwords[0] ^ dwords[1] ^ dwords[2] ^ dwords[3];
847 }
848 
849 /**
850  * @brief Get path to directory, where the settings files are stored.
851  * @return Path to settings directory, ends with a directory separator.
852  */
getSettingsDirPath() const853 QString Settings::getSettingsDirPath() const
854 {
855     QMutexLocker locker{&bigLock};
856     if (makeToxPortable)
857         return qApp->applicationDirPath() + QDir::separator();
858 
859 // workaround for https://bugreports.qt-project.org/browse/QTBUG-38845
860 #ifdef Q_OS_WIN
861     return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
862                            + QDir::separator() + "AppData" + QDir::separator() + "Roaming"
863                            + QDir::separator() + "tox")
864            + QDir::separator();
865 #elif defined(Q_OS_OSX)
866     return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
867                            + QDir::separator() + "Library" + QDir::separator()
868                            + "Application Support" + QDir::separator() + "Tox")
869            + QDir::separator();
870 #else
871     return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)
872                            + QDir::separator() + "tox")
873            + QDir::separator();
874 #endif
875 }
876 
877 /**
878  * @brief Get path to directory, where the application data are stored.
879  * @return Path to application data, ends with a directory separator.
880  */
getAppDataDirPath() const881 QString Settings::getAppDataDirPath() const
882 {
883     QMutexLocker locker{&bigLock};
884     if (makeToxPortable)
885         return qApp->applicationDirPath() + QDir::separator();
886 
887 // workaround for https://bugreports.qt-project.org/browse/QTBUG-38845
888 #ifdef Q_OS_WIN
889     return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
890                            + QDir::separator() + "AppData" + QDir::separator() + "Roaming"
891                            + QDir::separator() + "tox")
892            + QDir::separator();
893 #elif defined(Q_OS_OSX)
894     return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
895                            + QDir::separator() + "Library" + QDir::separator()
896                            + "Application Support" + QDir::separator() + "Tox")
897            + QDir::separator();
898 #else
899     /*
900      * TODO: Change QStandardPaths::DataLocation to AppDataLocation when upgrate Qt to 5.4+
901      * For now we need support Qt 5.3, so we use deprecated DataLocation
902      * BTW, it's not a big deal since for linux AppDataLocation and DataLocation are equal
903      */
904     return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation))
905            + QDir::separator();
906 #endif
907 }
908 
909 /**
910  * @brief Get path to directory, where the application cache are stored.
911  * @return Path to application cache, ends with a directory separator.
912  */
getAppCacheDirPath() const913 QString Settings::getAppCacheDirPath() const
914 {
915     QMutexLocker locker{&bigLock};
916     if (makeToxPortable)
917         return qApp->applicationDirPath() + QDir::separator();
918 
919 // workaround for https://bugreports.qt-project.org/browse/QTBUG-38845
920 #ifdef Q_OS_WIN
921     return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
922                            + QDir::separator() + "AppData" + QDir::separator() + "Roaming"
923                            + QDir::separator() + "tox")
924            + QDir::separator();
925 #elif defined(Q_OS_OSX)
926     return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
927                            + QDir::separator() + "Library" + QDir::separator()
928                            + "Application Support" + QDir::separator() + "Tox")
929            + QDir::separator();
930 #else
931     return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::CacheLocation))
932            + QDir::separator();
933 #endif
934 }
935 
getEnableTestSound() const936 bool Settings::getEnableTestSound() const
937 {
938     QMutexLocker locker{&bigLock};
939     return enableTestSound;
940 }
941 
setEnableTestSound(bool newValue)942 void Settings::setEnableTestSound(bool newValue)
943 {
944     QMutexLocker locker{&bigLock};
945 
946     if (newValue != enableTestSound) {
947         enableTestSound = newValue;
948         emit enableTestSoundChanged(enableTestSound);
949     }
950 }
951 
getEnableIPv6() const952 bool Settings::getEnableIPv6() const
953 {
954     QMutexLocker locker{&bigLock};
955     return enableIPv6;
956 }
957 
setEnableIPv6(bool enabled)958 void Settings::setEnableIPv6(bool enabled)
959 {
960     QMutexLocker locker{&bigLock};
961 
962     if (enabled != enableIPv6) {
963         enableIPv6 = enabled;
964         emit enableIPv6Changed(enableIPv6);
965     }
966 }
967 
getMakeToxPortable() const968 bool Settings::getMakeToxPortable() const
969 {
970     QMutexLocker locker{&bigLock};
971     return makeToxPortable;
972 }
973 
setMakeToxPortable(bool newValue)974 void Settings::setMakeToxPortable(bool newValue)
975 {
976     QMutexLocker locker{&bigLock};
977 
978     if (newValue != makeToxPortable) {
979         QFile(getSettingsDirPath() + globalSettingsFile).remove();
980         makeToxPortable = newValue;
981         saveGlobal();
982 
983         emit makeToxPortableChanged(makeToxPortable);
984     }
985 }
986 
getAutorun() const987 bool Settings::getAutorun() const
988 {
989     QMutexLocker locker{&bigLock};
990 
991 #ifdef QTOX_PLATFORM_EXT
992     return Platform::getAutorun();
993 #else
994     return false;
995 #endif
996 }
997 
setAutorun(bool newValue)998 void Settings::setAutorun(bool newValue)
999 {
1000 #ifdef QTOX_PLATFORM_EXT
1001     QMutexLocker locker{&bigLock};
1002 
1003     bool autorun = Platform::getAutorun();
1004 
1005     if (newValue != autorun) {
1006         Platform::setAutorun(newValue);
1007         emit autorunChanged(autorun);
1008     }
1009 #else
1010     Q_UNUSED(newValue);
1011 #endif
1012 }
1013 
getAutostartInTray() const1014 bool Settings::getAutostartInTray() const
1015 {
1016     QMutexLocker locker{&bigLock};
1017     return autostartInTray;
1018 }
1019 
getStyle() const1020 QString Settings::getStyle() const
1021 {
1022     QMutexLocker locker{&bigLock};
1023     return style;
1024 }
1025 
setStyle(const QString & newStyle)1026 void Settings::setStyle(const QString& newStyle)
1027 {
1028     QMutexLocker locker{&bigLock};
1029 
1030     if (newStyle != style) {
1031         style = newStyle;
1032         emit styleChanged(style);
1033     }
1034 }
1035 
getShowSystemTray() const1036 bool Settings::getShowSystemTray() const
1037 {
1038     QMutexLocker locker{&bigLock};
1039     return showSystemTray;
1040 }
1041 
setShowSystemTray(bool newValue)1042 void Settings::setShowSystemTray(bool newValue)
1043 {
1044     QMutexLocker locker{&bigLock};
1045 
1046     if (newValue != showSystemTray) {
1047         showSystemTray = newValue;
1048         emit showSystemTrayChanged(newValue);
1049     }
1050 }
1051 
setUseEmoticons(bool newValue)1052 void Settings::setUseEmoticons(bool newValue)
1053 {
1054     QMutexLocker locker{&bigLock};
1055 
1056     if (newValue != useEmoticons) {
1057         useEmoticons = newValue;
1058         emit useEmoticonsChanged(useEmoticons);
1059     }
1060 }
1061 
getUseEmoticons() const1062 bool Settings::getUseEmoticons() const
1063 {
1064     QMutexLocker locker{&bigLock};
1065     return useEmoticons;
1066 }
1067 
setAutoSaveEnabled(bool newValue)1068 void Settings::setAutoSaveEnabled(bool newValue)
1069 {
1070     QMutexLocker locker{&bigLock};
1071 
1072     if (newValue != autoSaveEnabled) {
1073         autoSaveEnabled = newValue;
1074         emit autoSaveEnabledChanged(autoSaveEnabled);
1075     }
1076 }
1077 
getAutoSaveEnabled() const1078 bool Settings::getAutoSaveEnabled() const
1079 {
1080     QMutexLocker locker{&bigLock};
1081     return autoSaveEnabled;
1082 }
1083 
setAutostartInTray(bool newValue)1084 void Settings::setAutostartInTray(bool newValue)
1085 {
1086     QMutexLocker locker{&bigLock};
1087 
1088     if (newValue != autostartInTray) {
1089         autostartInTray = newValue;
1090         emit autostartInTrayChanged(autostartInTray);
1091     }
1092 }
1093 
getCloseToTray() const1094 bool Settings::getCloseToTray() const
1095 {
1096     QMutexLocker locker{&bigLock};
1097     return closeToTray;
1098 }
1099 
setCloseToTray(bool newValue)1100 void Settings::setCloseToTray(bool newValue)
1101 {
1102     QMutexLocker locker{&bigLock};
1103 
1104     if (newValue != closeToTray) {
1105         closeToTray = newValue;
1106         emit closeToTrayChanged(newValue);
1107     }
1108 }
1109 
getMinimizeToTray() const1110 bool Settings::getMinimizeToTray() const
1111 {
1112     QMutexLocker locker{&bigLock};
1113     return minimizeToTray;
1114 }
1115 
setMinimizeToTray(bool newValue)1116 void Settings::setMinimizeToTray(bool newValue)
1117 {
1118     QMutexLocker locker{&bigLock};
1119 
1120     if (newValue != minimizeToTray) {
1121         minimizeToTray = newValue;
1122         emit minimizeToTrayChanged(minimizeToTray);
1123     }
1124 }
1125 
getLightTrayIcon() const1126 bool Settings::getLightTrayIcon() const
1127 {
1128     QMutexLocker locker{&bigLock};
1129     return lightTrayIcon;
1130 }
1131 
setLightTrayIcon(bool newValue)1132 void Settings::setLightTrayIcon(bool newValue)
1133 {
1134     QMutexLocker locker{&bigLock};
1135 
1136     if (newValue != lightTrayIcon) {
1137         lightTrayIcon = newValue;
1138         emit lightTrayIconChanged(lightTrayIcon);
1139     }
1140 }
1141 
getStatusChangeNotificationEnabled() const1142 bool Settings::getStatusChangeNotificationEnabled() const
1143 {
1144     QMutexLocker locker{&bigLock};
1145     return statusChangeNotificationEnabled;
1146 }
1147 
setStatusChangeNotificationEnabled(bool newValue)1148 void Settings::setStatusChangeNotificationEnabled(bool newValue)
1149 {
1150     QMutexLocker locker{&bigLock};
1151 
1152     if (newValue != statusChangeNotificationEnabled) {
1153         statusChangeNotificationEnabled = newValue;
1154         emit statusChangeNotificationEnabledChanged(statusChangeNotificationEnabled);
1155     }
1156 }
1157 
getSpellCheckingEnabled() const1158 bool Settings::getSpellCheckingEnabled() const
1159 {
1160     const QMutexLocker locker{&bigLock};
1161     return spellCheckingEnabled;
1162 }
1163 
setSpellCheckingEnabled(bool newValue)1164 void Settings::setSpellCheckingEnabled(bool newValue)
1165 {
1166     QMutexLocker locker{&bigLock};
1167 
1168     if (newValue != spellCheckingEnabled) {
1169         spellCheckingEnabled = newValue;
1170         emit statusChangeNotificationEnabledChanged(statusChangeNotificationEnabled);
1171     }
1172 }
1173 
getNotifySound() const1174 bool Settings::getNotifySound() const
1175 {
1176     QMutexLocker locker{&bigLock};
1177     return notifySound;
1178 }
1179 
setNotifySound(bool newValue)1180 void Settings::setNotifySound(bool newValue)
1181 {
1182     QMutexLocker locker{&bigLock};
1183 
1184     if (newValue != notifySound) {
1185         notifySound = newValue;
1186         emit notifySoundChanged(notifySound);
1187     }
1188 }
1189 
getNotifyHide() const1190 bool Settings::getNotifyHide() const
1191 {
1192     QMutexLocker locker{&bigLock};
1193     return notifyHide;
1194 }
1195 
setNotifyHide(bool newValue)1196 void Settings::setNotifyHide(bool newValue)
1197 {
1198     QMutexLocker locker{&bigLock};
1199 
1200     if (newValue != notifyHide) {
1201         notifyHide = newValue;
1202         emit notifyHideChanged(notifyHide);
1203     }
1204 }
1205 
getBusySound() const1206 bool Settings::getBusySound() const
1207 {
1208     QMutexLocker locker{&bigLock};
1209     return busySound;
1210 }
1211 
setBusySound(bool newValue)1212 void Settings::setBusySound(bool newValue)
1213 {
1214     QMutexLocker locker{&bigLock};
1215 
1216     if (newValue != busySound) {
1217         busySound = newValue;
1218         emit busySoundChanged(busySound);
1219     }
1220 }
1221 
getGroupAlwaysNotify() const1222 bool Settings::getGroupAlwaysNotify() const
1223 {
1224     QMutexLocker locker{&bigLock};
1225     return groupAlwaysNotify;
1226 }
1227 
setGroupAlwaysNotify(bool newValue)1228 void Settings::setGroupAlwaysNotify(bool newValue)
1229 {
1230     QMutexLocker locker{&bigLock};
1231 
1232     if (newValue != groupAlwaysNotify) {
1233         groupAlwaysNotify = newValue;
1234         emit groupAlwaysNotifyChanged(groupAlwaysNotify);
1235     }
1236 }
1237 
getTranslation() const1238 QString Settings::getTranslation() const
1239 {
1240     QMutexLocker locker{&bigLock};
1241     return translation;
1242 }
1243 
setTranslation(const QString & newValue)1244 void Settings::setTranslation(const QString& newValue)
1245 {
1246     QMutexLocker locker{&bigLock};
1247 
1248     if (newValue != translation) {
1249         translation = newValue;
1250         emit translationChanged(translation);
1251     }
1252 }
1253 
getForceTCP() const1254 bool Settings::getForceTCP() const
1255 {
1256     QMutexLocker locker{&bigLock};
1257     return forceTCP;
1258 }
1259 
setForceTCP(bool enabled)1260 void Settings::setForceTCP(bool enabled)
1261 {
1262     QMutexLocker locker{&bigLock};
1263 
1264     if (enabled != forceTCP) {
1265         forceTCP = enabled;
1266         emit forceTCPChanged(forceTCP);
1267     }
1268 }
1269 
getEnableLanDiscovery() const1270 bool Settings::getEnableLanDiscovery() const
1271 {
1272     QMutexLocker locker{&bigLock};
1273     return enableLanDiscovery;
1274 }
1275 
setEnableLanDiscovery(bool enabled)1276 void Settings::setEnableLanDiscovery(bool enabled)
1277 {
1278     QMutexLocker locker{&bigLock};
1279 
1280     if (enabled != enableLanDiscovery) {
1281         enableLanDiscovery = enabled;
1282         emit enableLanDiscoveryChanged(enableLanDiscovery);
1283     }
1284 }
1285 
getProxy() const1286 QNetworkProxy Settings::getProxy() const
1287 {
1288     QMutexLocker locker{&bigLock};
1289 
1290     QNetworkProxy proxy;
1291     switch (Settings::getProxyType()) {
1292     case ProxyType::ptNone:
1293         proxy.setType(QNetworkProxy::NoProxy);
1294         break;
1295     case ProxyType::ptSOCKS5:
1296         proxy.setType(QNetworkProxy::Socks5Proxy);
1297         break;
1298     case ProxyType::ptHTTP:
1299         proxy.setType(QNetworkProxy::HttpProxy);
1300         break;
1301     default:
1302         proxy.setType(QNetworkProxy::NoProxy);
1303         qWarning() << "Invalid Proxy type, setting to NoProxy";
1304         break;
1305     }
1306 
1307     proxy.setHostName(Settings::getProxyAddr());
1308     proxy.setPort(Settings::getProxyPort());
1309     return proxy;
1310 }
1311 
getProxyType() const1312 Settings::ProxyType Settings::getProxyType() const
1313 {
1314     QMutexLocker locker{&bigLock};
1315     return proxyType;
1316 }
1317 
setProxyType(ProxyType newValue)1318 void Settings::setProxyType(ProxyType newValue)
1319 {
1320     QMutexLocker locker{&bigLock};
1321 
1322     if (newValue != proxyType) {
1323         proxyType = newValue;
1324         emit proxyTypeChanged(proxyType);
1325     }
1326 }
1327 
getProxyAddr() const1328 QString Settings::getProxyAddr() const
1329 {
1330     QMutexLocker locker{&bigLock};
1331     return proxyAddr;
1332 }
1333 
setProxyAddr(const QString & address)1334 void Settings::setProxyAddr(const QString& address)
1335 {
1336     QMutexLocker locker{&bigLock};
1337 
1338     if (address != proxyAddr) {
1339         proxyAddr = address;
1340         emit proxyAddressChanged(proxyAddr);
1341     }
1342 }
1343 
getProxyPort() const1344 quint16 Settings::getProxyPort() const
1345 {
1346     QMutexLocker locker{&bigLock};
1347     return proxyPort;
1348 }
1349 
setProxyPort(quint16 port)1350 void Settings::setProxyPort(quint16 port)
1351 {
1352     QMutexLocker locker{&bigLock};
1353 
1354     if (port != proxyPort) {
1355         proxyPort = port;
1356         emit proxyPortChanged(proxyPort);
1357     }
1358 }
1359 
getCurrentProfile() const1360 QString Settings::getCurrentProfile() const
1361 {
1362     QMutexLocker locker{&bigLock};
1363     return currentProfile;
1364 }
1365 
getCurrentProfileId() const1366 uint32_t Settings::getCurrentProfileId() const
1367 {
1368     QMutexLocker locker{&bigLock};
1369     return currentProfileId;
1370 }
1371 
setCurrentProfile(const QString & profile)1372 void Settings::setCurrentProfile(const QString& profile)
1373 {
1374     QMutexLocker locker{&bigLock};
1375 
1376     if (profile != currentProfile) {
1377         currentProfile = profile;
1378         currentProfileId = makeProfileId(currentProfile);
1379 
1380         emit currentProfileIdChanged(currentProfileId);
1381     }
1382 }
1383 
getEnableLogging() const1384 bool Settings::getEnableLogging() const
1385 {
1386     QMutexLocker locker{&bigLock};
1387     return enableLogging;
1388 }
1389 
setEnableLogging(bool newValue)1390 void Settings::setEnableLogging(bool newValue)
1391 {
1392     QMutexLocker locker{&bigLock};
1393 
1394     if (newValue != enableLogging) {
1395         enableLogging = newValue;
1396         emit enableLoggingChanged(enableLogging);
1397     }
1398 }
1399 
getAutoAwayTime() const1400 int Settings::getAutoAwayTime() const
1401 {
1402     QMutexLocker locker{&bigLock};
1403     return autoAwayTime;
1404 }
1405 
1406 /**
1407  * @brief Sets how long the user may stay idle, before online status is set to "away".
1408  * @param[in] newValue  the user idle duration in minutes
1409  * @note Values < 0 default to 10 minutes.
1410  */
setAutoAwayTime(int newValue)1411 void Settings::setAutoAwayTime(int newValue)
1412 {
1413     QMutexLocker locker{&bigLock};
1414 
1415     if (newValue < 0)
1416         newValue = 10;
1417 
1418     if (newValue != autoAwayTime) {
1419         autoAwayTime = newValue;
1420         emit autoAwayTimeChanged(autoAwayTime);
1421     }
1422 }
1423 
getAutoAcceptDir(const ToxPk & id) const1424 QString Settings::getAutoAcceptDir(const ToxPk& id) const
1425 {
1426     QMutexLocker locker{&bigLock};
1427 
1428     auto it = friendLst.find(id.getByteArray());
1429     if (it != friendLst.end())
1430         return it->autoAcceptDir;
1431 
1432     return QString();
1433 }
1434 
setAutoAcceptDir(const ToxPk & id,const QString & dir)1435 void Settings::setAutoAcceptDir(const ToxPk& id, const QString& dir)
1436 {
1437     QMutexLocker locker{&bigLock};
1438 
1439     auto& frnd = getOrInsertFriendPropRef(id);
1440 
1441     if (frnd.autoAcceptDir != dir) {
1442         frnd.autoAcceptDir = dir;
1443         emit autoAcceptDirChanged(id, dir);
1444     }
1445 }
1446 
getAutoAcceptCall(const ToxPk & id) const1447 Settings::AutoAcceptCallFlags Settings::getAutoAcceptCall(const ToxPk& id) const
1448 {
1449     QMutexLocker locker{&bigLock};
1450 
1451     auto it = friendLst.find(id.getByteArray());
1452     if (it != friendLst.end())
1453         return it->autoAcceptCall;
1454 
1455     return Settings::AutoAcceptCallFlags();
1456 }
1457 
setAutoAcceptCall(const ToxPk & id,AutoAcceptCallFlags accept)1458 void Settings::setAutoAcceptCall(const ToxPk& id, AutoAcceptCallFlags accept)
1459 {
1460     QMutexLocker locker{&bigLock};
1461 
1462     auto& frnd = getOrInsertFriendPropRef(id);
1463 
1464     if (frnd.autoAcceptCall != accept) {
1465         frnd.autoAcceptCall = accept;
1466         emit autoAcceptCallChanged(id, accept);
1467     }
1468 }
1469 
getAutoGroupInvite(const ToxPk & id) const1470 bool Settings::getAutoGroupInvite(const ToxPk& id) const
1471 {
1472     QMutexLocker locker{&bigLock};
1473 
1474     auto it = friendLst.find(id.getByteArray());
1475     if (it != friendLst.end()) {
1476         return it->autoGroupInvite;
1477     }
1478 
1479     return false;
1480 }
1481 
setAutoGroupInvite(const ToxPk & id,bool accept)1482 void Settings::setAutoGroupInvite(const ToxPk& id, bool accept)
1483 {
1484     QMutexLocker locker{&bigLock};
1485 
1486     auto& frnd = getOrInsertFriendPropRef(id);
1487 
1488     if (frnd.autoGroupInvite != accept) {
1489         frnd.autoGroupInvite = accept;
1490         emit autoGroupInviteChanged(id, accept);
1491     }
1492 }
1493 
getContactNote(const ToxPk & id) const1494 QString Settings::getContactNote(const ToxPk& id) const
1495 {
1496     QMutexLocker locker{&bigLock};
1497 
1498     auto it = friendLst.find(id.getByteArray());
1499     if (it != friendLst.end())
1500         return it->note;
1501 
1502     return QString();
1503 }
1504 
setContactNote(const ToxPk & id,const QString & note)1505 void Settings::setContactNote(const ToxPk& id, const QString& note)
1506 {
1507     QMutexLocker locker{&bigLock};
1508 
1509     auto& frnd = getOrInsertFriendPropRef(id);
1510 
1511     if (frnd.note != note) {
1512         frnd.note = note;
1513         emit contactNoteChanged(id, note);
1514     }
1515 }
1516 
getGlobalAutoAcceptDir() const1517 QString Settings::getGlobalAutoAcceptDir() const
1518 {
1519     QMutexLocker locker{&bigLock};
1520     return globalAutoAcceptDir;
1521 }
1522 
setGlobalAutoAcceptDir(const QString & newValue)1523 void Settings::setGlobalAutoAcceptDir(const QString& newValue)
1524 {
1525     QMutexLocker locker{&bigLock};
1526 
1527     if (newValue != globalAutoAcceptDir) {
1528         globalAutoAcceptDir = newValue;
1529         emit globalAutoAcceptDirChanged(globalAutoAcceptDir);
1530     }
1531 }
1532 
getMaxAutoAcceptSize() const1533 size_t Settings::getMaxAutoAcceptSize() const
1534 {
1535     QMutexLocker locker{&bigLock};
1536     return autoAcceptMaxSize;
1537 }
1538 
setMaxAutoAcceptSize(size_t size)1539 void Settings::setMaxAutoAcceptSize(size_t size)
1540 {
1541     QMutexLocker locker{&bigLock};
1542 
1543     if (size != autoAcceptMaxSize) {
1544         autoAcceptMaxSize = size;
1545         emit autoAcceptMaxSizeChanged(autoAcceptMaxSize);
1546     }
1547 }
1548 
getChatMessageFont() const1549 const QFont& Settings::getChatMessageFont() const
1550 {
1551     QMutexLocker locker(&bigLock);
1552     return chatMessageFont;
1553 }
1554 
setChatMessageFont(const QFont & font)1555 void Settings::setChatMessageFont(const QFont& font)
1556 {
1557     QMutexLocker locker(&bigLock);
1558 
1559     if (font != chatMessageFont) {
1560         chatMessageFont = font;
1561         emit chatMessageFontChanged(chatMessageFont);
1562     }
1563 }
1564 
setWidgetData(const QString & uniqueName,const QByteArray & data)1565 void Settings::setWidgetData(const QString& uniqueName, const QByteArray& data)
1566 {
1567     QMutexLocker locker{&bigLock};
1568 
1569     if (!widgetSettings.contains(uniqueName) || widgetSettings[uniqueName] != data) {
1570         widgetSettings[uniqueName] = data;
1571         emit widgetDataChanged(uniqueName);
1572     }
1573 }
1574 
getWidgetData(const QString & uniqueName) const1575 QByteArray Settings::getWidgetData(const QString& uniqueName) const
1576 {
1577     QMutexLocker locker{&bigLock};
1578     return widgetSettings.value(uniqueName);
1579 }
1580 
getSmileyPack() const1581 QString Settings::getSmileyPack() const
1582 {
1583     QMutexLocker locker{&bigLock};
1584     return smileyPack;
1585 }
1586 
setSmileyPack(const QString & value)1587 void Settings::setSmileyPack(const QString& value)
1588 {
1589     QMutexLocker locker{&bigLock};
1590 
1591     if (value != smileyPack) {
1592         smileyPack = value;
1593         emit smileyPackChanged(smileyPack);
1594     }
1595 }
1596 
getEmojiFontPointSize() const1597 int Settings::getEmojiFontPointSize() const
1598 {
1599     QMutexLocker locker{&bigLock};
1600     return emojiFontPointSize;
1601 }
1602 
setEmojiFontPointSize(int value)1603 void Settings::setEmojiFontPointSize(int value)
1604 {
1605     QMutexLocker locker{&bigLock};
1606 
1607     if (value != emojiFontPointSize) {
1608         emojiFontPointSize = value;
1609         emit emojiFontPointSizeChanged(emojiFontPointSize);
1610     }
1611 }
1612 
getTimestampFormat() const1613 const QString& Settings::getTimestampFormat() const
1614 {
1615     QMutexLocker locker{&bigLock};
1616     return timestampFormat;
1617 }
1618 
setTimestampFormat(const QString & format)1619 void Settings::setTimestampFormat(const QString& format)
1620 {
1621     QMutexLocker locker{&bigLock};
1622 
1623     if (format != timestampFormat) {
1624         timestampFormat = format;
1625         emit timestampFormatChanged(timestampFormat);
1626     }
1627 }
1628 
getDateFormat() const1629 const QString& Settings::getDateFormat() const
1630 {
1631     QMutexLocker locker{&bigLock};
1632     return dateFormat;
1633 }
1634 
setDateFormat(const QString & format)1635 void Settings::setDateFormat(const QString& format)
1636 {
1637     QMutexLocker locker{&bigLock};
1638 
1639     if (format != dateFormat) {
1640         dateFormat = format;
1641         emit dateFormatChanged(dateFormat);
1642     }
1643 }
1644 
getStylePreference() const1645 Settings::StyleType Settings::getStylePreference() const
1646 {
1647     QMutexLocker locker{&bigLock};
1648     return stylePreference;
1649 }
1650 
setStylePreference(StyleType newValue)1651 void Settings::setStylePreference(StyleType newValue)
1652 {
1653     QMutexLocker locker{&bigLock};
1654 
1655     if (newValue != stylePreference) {
1656         stylePreference = newValue;
1657         emit stylePreferenceChanged(stylePreference);
1658     }
1659 }
1660 
getWindowGeometry() const1661 QByteArray Settings::getWindowGeometry() const
1662 {
1663     QMutexLocker locker{&bigLock};
1664     return windowGeometry;
1665 }
1666 
setWindowGeometry(const QByteArray & value)1667 void Settings::setWindowGeometry(const QByteArray& value)
1668 {
1669     QMutexLocker locker{&bigLock};
1670 
1671     if (value != windowGeometry) {
1672         windowGeometry = value;
1673         emit windowGeometryChanged(windowGeometry);
1674     }
1675 }
1676 
getWindowState() const1677 QByteArray Settings::getWindowState() const
1678 {
1679     QMutexLocker locker{&bigLock};
1680     return windowState;
1681 }
1682 
setWindowState(const QByteArray & value)1683 void Settings::setWindowState(const QByteArray& value)
1684 {
1685     QMutexLocker locker{&bigLock};
1686 
1687     if (value != windowState) {
1688         windowState = value;
1689         emit windowStateChanged(windowState);
1690     }
1691 }
1692 
getCheckUpdates() const1693 bool Settings::getCheckUpdates() const
1694 {
1695     QMutexLocker locker{&bigLock};
1696     return checkUpdates;
1697 }
1698 
setCheckUpdates(bool newValue)1699 void Settings::setCheckUpdates(bool newValue)
1700 {
1701     QMutexLocker locker{&bigLock};
1702 
1703     if (newValue != checkUpdates) {
1704         checkUpdates = newValue;
1705         emit checkUpdatesChanged(checkUpdates);
1706     }
1707 }
1708 
getNotify() const1709 bool Settings::getNotify() const
1710 {
1711     QMutexLocker locker{&bigLock};
1712     return notify;
1713 }
1714 
setNotify(bool newValue)1715 void Settings::setNotify(bool newValue)
1716 {
1717     QMutexLocker locker{&bigLock};
1718     if (newValue != notify) {
1719         notify = newValue;
1720         emit notifyChanged(notify);
1721     }
1722 }
1723 
getShowWindow() const1724 bool Settings::getShowWindow() const
1725 {
1726     QMutexLocker locker{&bigLock};
1727     return showWindow;
1728 }
1729 
setShowWindow(bool newValue)1730 void Settings::setShowWindow(bool newValue)
1731 {
1732     QMutexLocker locker{&bigLock};
1733 
1734     if (newValue != showWindow) {
1735         showWindow = newValue;
1736         emit showWindowChanged(showWindow);
1737     }
1738 }
1739 
getDesktopNotify() const1740 bool Settings::getDesktopNotify() const
1741 {
1742     QMutexLocker locker{&bigLock};
1743     return desktopNotify;
1744 }
1745 
setDesktopNotify(bool enabled)1746 void Settings::setDesktopNotify(bool enabled)
1747 {
1748     QMutexLocker locker{&bigLock};
1749 
1750     if (enabled != desktopNotify) {
1751         desktopNotify = enabled;
1752         emit desktopNotifyChanged(desktopNotify);
1753     }
1754 }
1755 
getSplitterState() const1756 QByteArray Settings::getSplitterState() const
1757 {
1758     QMutexLocker locker{&bigLock};
1759     return splitterState;
1760 }
1761 
setSplitterState(const QByteArray & value)1762 void Settings::setSplitterState(const QByteArray& value)
1763 {
1764     QMutexLocker locker{&bigLock};
1765 
1766     if (value != splitterState) {
1767         splitterState = value;
1768         emit splitterStateChanged(splitterState);
1769     }
1770 }
1771 
getDialogGeometry() const1772 QByteArray Settings::getDialogGeometry() const
1773 {
1774     QMutexLocker locker{&bigLock};
1775     return dialogGeometry;
1776 }
1777 
setDialogGeometry(const QByteArray & value)1778 void Settings::setDialogGeometry(const QByteArray& value)
1779 {
1780     QMutexLocker locker{&bigLock};
1781 
1782     if (value != dialogGeometry) {
1783         dialogGeometry = value;
1784         emit dialogGeometryChanged(dialogGeometry);
1785     }
1786 }
1787 
getDialogSplitterState() const1788 QByteArray Settings::getDialogSplitterState() const
1789 {
1790     QMutexLocker locker{&bigLock};
1791     return dialogSplitterState;
1792 }
1793 
setDialogSplitterState(const QByteArray & value)1794 void Settings::setDialogSplitterState(const QByteArray& value)
1795 {
1796     QMutexLocker locker{&bigLock};
1797 
1798     if (value != dialogSplitterState) {
1799         dialogSplitterState = value;
1800         emit dialogSplitterStateChanged(dialogSplitterState);
1801     }
1802 }
1803 
getDialogSettingsGeometry() const1804 QByteArray Settings::getDialogSettingsGeometry() const
1805 {
1806     QMutexLocker locker{&bigLock};
1807     return dialogSettingsGeometry;
1808 }
1809 
setDialogSettingsGeometry(const QByteArray & value)1810 void Settings::setDialogSettingsGeometry(const QByteArray& value)
1811 {
1812     QMutexLocker locker{&bigLock};
1813 
1814     if (value != dialogSettingsGeometry) {
1815         dialogSettingsGeometry = value;
1816         emit dialogSettingsGeometryChanged(dialogSettingsGeometry);
1817     }
1818 }
1819 
getMinimizeOnClose() const1820 bool Settings::getMinimizeOnClose() const
1821 {
1822     QMutexLocker locker{&bigLock};
1823     return minimizeOnClose;
1824 }
1825 
setMinimizeOnClose(bool newValue)1826 void Settings::setMinimizeOnClose(bool newValue)
1827 {
1828     QMutexLocker locker{&bigLock};
1829 
1830     if (newValue != minimizeOnClose) {
1831         minimizeOnClose = newValue;
1832         emit minimizeOnCloseChanged(minimizeOnClose);
1833     }
1834 }
1835 
getTypingNotification() const1836 bool Settings::getTypingNotification() const
1837 {
1838     QMutexLocker locker{&bigLock};
1839     return typingNotification;
1840 }
1841 
setTypingNotification(bool enabled)1842 void Settings::setTypingNotification(bool enabled)
1843 {
1844     QMutexLocker locker{&bigLock};
1845 
1846     if (enabled != typingNotification) {
1847         typingNotification = enabled;
1848         emit typingNotificationChanged(typingNotification);
1849     }
1850 }
1851 
getBlackList() const1852 QStringList Settings::getBlackList() const
1853 {
1854     QMutexLocker locker{&bigLock};
1855     return blackList;
1856 }
1857 
setBlackList(const QStringList & blist)1858 void Settings::setBlackList(const QStringList& blist)
1859 {
1860     QMutexLocker locker{&bigLock};
1861 
1862     if (blist != blackList) {
1863         blackList = blist;
1864         emit blackListChanged(blackList);
1865     }
1866 }
1867 
getInDev() const1868 QString Settings::getInDev() const
1869 {
1870     QMutexLocker locker{&bigLock};
1871     return inDev;
1872 }
1873 
setInDev(const QString & deviceSpecifier)1874 void Settings::setInDev(const QString& deviceSpecifier)
1875 {
1876     QMutexLocker locker{&bigLock};
1877 
1878     if (deviceSpecifier != inDev) {
1879         inDev = deviceSpecifier;
1880         emit inDevChanged(inDev);
1881     }
1882 }
1883 
getAudioInDevEnabled() const1884 bool Settings::getAudioInDevEnabled() const
1885 {
1886     QMutexLocker locker(&bigLock);
1887     return audioInDevEnabled;
1888 }
1889 
setAudioInDevEnabled(bool enabled)1890 void Settings::setAudioInDevEnabled(bool enabled)
1891 {
1892     QMutexLocker locker(&bigLock);
1893 
1894     if (enabled != audioInDevEnabled) {
1895         audioInDevEnabled = enabled;
1896         emit audioInDevEnabledChanged(enabled);
1897     }
1898 }
1899 
getAudioInGainDecibel() const1900 qreal Settings::getAudioInGainDecibel() const
1901 {
1902     QMutexLocker locker{&bigLock};
1903     return audioInGainDecibel;
1904 }
1905 
setAudioInGainDecibel(qreal dB)1906 void Settings::setAudioInGainDecibel(qreal dB)
1907 {
1908     QMutexLocker locker{&bigLock};
1909 
1910     if (dB < audioInGainDecibel || dB > audioInGainDecibel) {
1911         audioInGainDecibel = dB;
1912         emit audioInGainDecibelChanged(audioInGainDecibel);
1913     }
1914 }
1915 
getAudioThreshold() const1916 qreal Settings::getAudioThreshold() const
1917 {
1918     QMutexLocker locker{&bigLock};
1919     return audioThreshold;
1920 }
1921 
setAudioThreshold(qreal percent)1922 void Settings::setAudioThreshold(qreal percent)
1923 {
1924     QMutexLocker locker{&bigLock};
1925 
1926     if (percent < audioThreshold || percent > audioThreshold) {
1927         audioThreshold = percent;
1928         emit audioThresholdChanged(audioThreshold);
1929     }
1930 }
1931 
getVideoDev() const1932 QString Settings::getVideoDev() const
1933 {
1934     QMutexLocker locker{&bigLock};
1935     return videoDev;
1936 }
1937 
setVideoDev(const QString & deviceSpecifier)1938 void Settings::setVideoDev(const QString& deviceSpecifier)
1939 {
1940     QMutexLocker locker{&bigLock};
1941 
1942     if (deviceSpecifier != videoDev) {
1943         videoDev = deviceSpecifier;
1944         emit videoDevChanged(videoDev);
1945     }
1946 }
1947 
getOutDev() const1948 QString Settings::getOutDev() const
1949 {
1950     QMutexLocker locker{&bigLock};
1951     return outDev;
1952 }
1953 
setOutDev(const QString & deviceSpecifier)1954 void Settings::setOutDev(const QString& deviceSpecifier)
1955 {
1956     QMutexLocker locker{&bigLock};
1957 
1958     if (deviceSpecifier != outDev) {
1959         outDev = deviceSpecifier;
1960         emit outDevChanged(outDev);
1961     }
1962 }
1963 
getAudioOutDevEnabled() const1964 bool Settings::getAudioOutDevEnabled() const
1965 {
1966     QMutexLocker locker(&bigLock);
1967     return audioOutDevEnabled;
1968 }
1969 
setAudioOutDevEnabled(bool enabled)1970 void Settings::setAudioOutDevEnabled(bool enabled)
1971 {
1972     QMutexLocker locker(&bigLock);
1973 
1974     if (enabled != audioOutDevEnabled) {
1975         audioOutDevEnabled = enabled;
1976         emit audioOutDevEnabledChanged(audioOutDevEnabled);
1977     }
1978 }
1979 
getOutVolume() const1980 int Settings::getOutVolume() const
1981 {
1982     QMutexLocker locker{&bigLock};
1983     return outVolume;
1984 }
1985 
setOutVolume(int volume)1986 void Settings::setOutVolume(int volume)
1987 {
1988     QMutexLocker locker{&bigLock};
1989 
1990     if (volume != outVolume) {
1991         outVolume = volume;
1992         emit outVolumeChanged(outVolume);
1993     }
1994 }
1995 
getAudioBitrate() const1996 int Settings::getAudioBitrate() const
1997 {
1998     const QMutexLocker locker{&bigLock};
1999     return audioBitrate;
2000 }
2001 
setAudioBitrate(int bitrate)2002 void Settings::setAudioBitrate(int bitrate)
2003 {
2004     const QMutexLocker locker{&bigLock};
2005 
2006     if (bitrate != audioBitrate) {
2007         audioBitrate = bitrate;
2008         emit audioBitrateChanged(audioBitrate);
2009     }
2010 }
2011 
getScreenRegion() const2012 QRect Settings::getScreenRegion() const
2013 {
2014     QMutexLocker locker(&bigLock);
2015     return screenRegion;
2016 }
2017 
setScreenRegion(const QRect & value)2018 void Settings::setScreenRegion(const QRect& value)
2019 {
2020     QMutexLocker locker{&bigLock};
2021 
2022     if (value != screenRegion) {
2023         screenRegion = value;
2024         emit screenRegionChanged(screenRegion);
2025     }
2026 }
2027 
getScreenGrabbed() const2028 bool Settings::getScreenGrabbed() const
2029 {
2030     QMutexLocker locker(&bigLock);
2031     return screenGrabbed;
2032 }
2033 
setScreenGrabbed(bool value)2034 void Settings::setScreenGrabbed(bool value)
2035 {
2036     QMutexLocker locker{&bigLock};
2037 
2038     if (value != screenGrabbed) {
2039         screenGrabbed = value;
2040         emit screenGrabbedChanged(screenGrabbed);
2041     }
2042 }
2043 
getCamVideoRes() const2044 QRect Settings::getCamVideoRes() const
2045 {
2046     QMutexLocker locker{&bigLock};
2047     return camVideoRes;
2048 }
2049 
setCamVideoRes(QRect newValue)2050 void Settings::setCamVideoRes(QRect newValue)
2051 {
2052     QMutexLocker locker{&bigLock};
2053 
2054     if (newValue != camVideoRes) {
2055         camVideoRes = newValue;
2056         emit camVideoResChanged(camVideoRes);
2057     }
2058 }
2059 
getCamVideoFPS() const2060 float Settings::getCamVideoFPS() const
2061 {
2062     QMutexLocker locker{&bigLock};
2063     return camVideoFPS;
2064 }
2065 
setCamVideoFPS(float newValue)2066 void Settings::setCamVideoFPS(float newValue)
2067 {
2068     QMutexLocker locker{&bigLock};
2069 
2070     if (newValue != camVideoFPS) {
2071         camVideoFPS = newValue;
2072         emit camVideoFPSChanged(camVideoFPS);
2073     }
2074 }
2075 
getFriendAddress(const QString & publicKey) const2076 QString Settings::getFriendAddress(const QString& publicKey) const
2077 {
2078     QMutexLocker locker{&bigLock};
2079     // TODO: using ToxId here is a hack
2080     QByteArray key = ToxId(publicKey).getPublicKey().getByteArray();
2081     auto it = friendLst.find(key);
2082     if (it != friendLst.end())
2083         return it->addr;
2084 
2085     return QString();
2086 }
2087 
updateFriendAddress(const QString & newAddr)2088 void Settings::updateFriendAddress(const QString& newAddr)
2089 {
2090     QMutexLocker locker{&bigLock};
2091     // TODO: using ToxId here is a hack
2092     auto key = ToxId(newAddr).getPublicKey();
2093     auto& frnd = getOrInsertFriendPropRef(key);
2094     frnd.addr = newAddr;
2095 }
2096 
getFriendAlias(const ToxPk & id) const2097 QString Settings::getFriendAlias(const ToxPk& id) const
2098 {
2099     QMutexLocker locker{&bigLock};
2100     auto it = friendLst.find(id.getByteArray());
2101     if (it != friendLst.end())
2102         return it->alias;
2103 
2104     return QString();
2105 }
2106 
setFriendAlias(const ToxPk & id,const QString & alias)2107 void Settings::setFriendAlias(const ToxPk& id, const QString& alias)
2108 {
2109     QMutexLocker locker{&bigLock};
2110     auto& frnd = getOrInsertFriendPropRef(id);
2111     frnd.alias = alias;
2112 }
2113 
getFriendCircleID(const ToxPk & id) const2114 int Settings::getFriendCircleID(const ToxPk& id) const
2115 {
2116     QMutexLocker locker{&bigLock};
2117     auto it = friendLst.find(id.getByteArray());
2118     if (it != friendLst.end())
2119         return it->circleID;
2120 
2121     return -1;
2122 }
2123 
setFriendCircleID(const ToxPk & id,int circleID)2124 void Settings::setFriendCircleID(const ToxPk& id, int circleID)
2125 {
2126     QMutexLocker locker{&bigLock};
2127     auto& frnd = getOrInsertFriendPropRef(id);
2128     frnd.circleID = circleID;
2129 }
2130 
getFriendActivity(const ToxPk & id) const2131 QDateTime Settings::getFriendActivity(const ToxPk& id) const
2132 {
2133     QMutexLocker locker{&bigLock};
2134     auto it = friendLst.find(id.getByteArray());
2135     if (it != friendLst.end())
2136         return it->activity;
2137 
2138     return QDateTime();
2139 }
2140 
setFriendActivity(const ToxPk & id,const QDateTime & activity)2141 void Settings::setFriendActivity(const ToxPk& id, const QDateTime& activity)
2142 {
2143     QMutexLocker locker{&bigLock};
2144     auto& frnd = getOrInsertFriendPropRef(id);
2145     frnd.activity = activity;
2146 }
2147 
saveFriendSettings(const ToxPk & id)2148 void Settings::saveFriendSettings(const ToxPk& id)
2149 {
2150     Q_UNUSED(id);
2151     savePersonal();
2152 }
2153 
removeFriendSettings(const ToxPk & id)2154 void Settings::removeFriendSettings(const ToxPk& id)
2155 {
2156     QMutexLocker locker{&bigLock};
2157     friendLst.remove(id.getByteArray());
2158 }
2159 
getCompactLayout() const2160 bool Settings::getCompactLayout() const
2161 {
2162     QMutexLocker locker{&bigLock};
2163     return compactLayout;
2164 }
2165 
setCompactLayout(bool value)2166 void Settings::setCompactLayout(bool value)
2167 {
2168     QMutexLocker locker{&bigLock};
2169 
2170     if (value != compactLayout) {
2171         compactLayout = value;
2172         emit compactLayoutChanged(value);
2173     }
2174 }
2175 
getFriendSortingMode() const2176 Settings::FriendListSortingMode Settings::getFriendSortingMode() const
2177 {
2178     QMutexLocker locker{&bigLock};
2179     return sortingMode;
2180 }
2181 
setFriendSortingMode(FriendListSortingMode mode)2182 void Settings::setFriendSortingMode(FriendListSortingMode mode)
2183 {
2184     QMutexLocker locker{&bigLock};
2185 
2186     if (mode != sortingMode) {
2187         sortingMode = mode;
2188         emit sortingModeChanged(sortingMode);
2189     }
2190 }
2191 
getSeparateWindow() const2192 bool Settings::getSeparateWindow() const
2193 {
2194     QMutexLocker locker{&bigLock};
2195     return separateWindow;
2196 }
2197 
setSeparateWindow(bool value)2198 void Settings::setSeparateWindow(bool value)
2199 {
2200     QMutexLocker locker{&bigLock};
2201 
2202     if (value != separateWindow) {
2203         separateWindow = value;
2204         emit separateWindowChanged(value);
2205     }
2206 }
2207 
getDontGroupWindows() const2208 bool Settings::getDontGroupWindows() const
2209 {
2210     QMutexLocker locker{&bigLock};
2211     return dontGroupWindows;
2212 }
2213 
setDontGroupWindows(bool value)2214 void Settings::setDontGroupWindows(bool value)
2215 {
2216     QMutexLocker locker{&bigLock};
2217 
2218     if (value != dontGroupWindows) {
2219         dontGroupWindows = value;
2220         emit dontGroupWindowsChanged(dontGroupWindows);
2221     }
2222 }
2223 
getGroupchatPosition() const2224 bool Settings::getGroupchatPosition() const
2225 {
2226     QMutexLocker locker{&bigLock};
2227     return groupchatPosition;
2228 }
2229 
setGroupchatPosition(bool value)2230 void Settings::setGroupchatPosition(bool value)
2231 {
2232     QMutexLocker locker{&bigLock};
2233 
2234     if (value != groupchatPosition) {
2235         groupchatPosition = value;
2236         emit groupchatPositionChanged(value);
2237     }
2238 }
2239 
getShowIdenticons() const2240 bool Settings::getShowIdenticons() const
2241 {
2242     const QMutexLocker locker{&bigLock};
2243     return showIdenticons;
2244 }
2245 
setShowIdenticons(bool value)2246 void Settings::setShowIdenticons(bool value)
2247 {
2248     const QMutexLocker locker{&bigLock};
2249 
2250     if (value != showIdenticons) {
2251         showIdenticons = value;
2252         emit showIdenticonsChanged(value);
2253     }
2254 }
2255 
getCircleCount() const2256 int Settings::getCircleCount() const
2257 {
2258     QMutexLocker locker{&bigLock};
2259     return circleLst.size();
2260 }
2261 
getCircleName(int id) const2262 QString Settings::getCircleName(int id) const
2263 {
2264     QMutexLocker locker{&bigLock};
2265     return circleLst[id].name;
2266 }
2267 
setCircleName(int id,const QString & name)2268 void Settings::setCircleName(int id, const QString& name)
2269 {
2270     QMutexLocker locker{&bigLock};
2271     circleLst[id].name = name;
2272     savePersonal();
2273 }
2274 
addCircle(const QString & name)2275 int Settings::addCircle(const QString& name)
2276 {
2277     QMutexLocker locker{&bigLock};
2278 
2279     circleProp cp;
2280     cp.expanded = false;
2281 
2282     if (name.isEmpty())
2283         cp.name = tr("Circle #%1").arg(circleLst.count() + 1);
2284     else
2285         cp.name = name;
2286 
2287     circleLst.append(cp);
2288     savePersonal();
2289     return circleLst.count() - 1;
2290 }
2291 
getCircleExpanded(int id) const2292 bool Settings::getCircleExpanded(int id) const
2293 {
2294     QMutexLocker locker{&bigLock};
2295     return circleLst[id].expanded;
2296 }
2297 
setCircleExpanded(int id,bool expanded)2298 void Settings::setCircleExpanded(int id, bool expanded)
2299 {
2300     QMutexLocker locker{&bigLock};
2301     circleLst[id].expanded = expanded;
2302 }
2303 
addFriendRequest(const QString & friendAddress,const QString & message)2304 bool Settings::addFriendRequest(const QString& friendAddress, const QString& message)
2305 {
2306     QMutexLocker locker{&bigLock};
2307 
2308     for (auto queued : friendRequests) {
2309         if (queued.address == friendAddress) {
2310             queued.message = message;
2311             queued.read = false;
2312             return false;
2313         }
2314     }
2315 
2316     Request request;
2317     request.address = friendAddress;
2318     request.message = message;
2319     request.read = false;
2320 
2321     friendRequests.push_back(request);
2322     return true;
2323 }
2324 
getUnreadFriendRequests() const2325 unsigned int Settings::getUnreadFriendRequests() const
2326 {
2327     QMutexLocker locker{&bigLock};
2328     unsigned int unreadFriendRequests = 0;
2329     for (auto request : friendRequests)
2330         if (!request.read)
2331             ++unreadFriendRequests;
2332 
2333     return unreadFriendRequests;
2334 }
2335 
getFriendRequest(int index) const2336 Settings::Request Settings::getFriendRequest(int index) const
2337 {
2338     QMutexLocker locker{&bigLock};
2339     return friendRequests.at(index);
2340 }
2341 
getFriendRequestSize() const2342 int Settings::getFriendRequestSize() const
2343 {
2344     QMutexLocker locker{&bigLock};
2345     return friendRequests.size();
2346 }
2347 
clearUnreadFriendRequests()2348 void Settings::clearUnreadFriendRequests()
2349 {
2350     QMutexLocker locker{&bigLock};
2351 
2352     for (auto& request : friendRequests)
2353         request.read = true;
2354 }
2355 
removeFriendRequest(int index)2356 void Settings::removeFriendRequest(int index)
2357 {
2358     QMutexLocker locker{&bigLock};
2359     friendRequests.removeAt(index);
2360 }
2361 
readFriendRequest(int index)2362 void Settings::readFriendRequest(int index)
2363 {
2364     QMutexLocker locker{&bigLock};
2365     friendRequests[index].read = true;
2366 }
2367 
removeCircle(int id)2368 int Settings::removeCircle(int id)
2369 {
2370     // Replace index with last one and remove last one instead.
2371     // This gives you contiguous ids all the time.
2372     circleLst[id] = circleLst.last();
2373     circleLst.pop_back();
2374     savePersonal();
2375     return circleLst.count();
2376 }
2377 
getThemeColor() const2378 int Settings::getThemeColor() const
2379 {
2380     QMutexLocker locker{&bigLock};
2381     return themeColor;
2382 }
2383 
setThemeColor(int value)2384 void Settings::setThemeColor(int value)
2385 {
2386     QMutexLocker locker{&bigLock};
2387 
2388     if (value != themeColor) {
2389         themeColor = value;
2390         emit themeColorChanged(themeColor);
2391     }
2392 }
2393 
getAutoLogin() const2394 bool Settings::getAutoLogin() const
2395 {
2396     QMutexLocker locker{&bigLock};
2397     return autoLogin;
2398 }
2399 
setAutoLogin(bool state)2400 void Settings::setAutoLogin(bool state)
2401 {
2402     QMutexLocker locker{&bigLock};
2403 
2404     if (state != autoLogin) {
2405         autoLogin = state;
2406         emit autoLoginChanged(autoLogin);
2407     }
2408 }
2409 
setEnableGroupChatsColor(bool state)2410 void Settings::setEnableGroupChatsColor(bool state)
2411 {
2412     QMutexLocker locker{&bigLock};
2413     if (state != nameColors) {
2414         nameColors = state;
2415         emit nameColorsChanged(nameColors);
2416     }
2417 }
2418 
getEnableGroupChatsColor() const2419 bool Settings::getEnableGroupChatsColor() const
2420 {
2421     return nameColors;
2422 }
2423 
2424 /**
2425  * @brief Write a default personal .ini settings file for a profile.
2426  * @param basename Filename without extension to save settings.
2427  *
2428  * @note If basename is "profile", settings will be saved in profile.ini
2429  */
createPersonal(QString basename)2430 void Settings::createPersonal(QString basename)
2431 {
2432     QMutexLocker locker{&bigLock};
2433 
2434     QString path = getSettingsDirPath() + QDir::separator() + basename + ".ini";
2435     qDebug() << "Creating new profile settings in " << path;
2436 
2437     QSettings ps(path, QSettings::IniFormat);
2438     ps.setIniCodec("UTF-8");
2439     ps.beginGroup("Friends");
2440     ps.beginWriteArray("Friend", 0);
2441     ps.endArray();
2442     ps.endGroup();
2443 
2444     ps.beginGroup("Privacy");
2445     ps.endGroup();
2446 }
2447 
2448 /**
2449  * @brief Creates a path to the settings dir, if it doesn't already exist
2450  */
createSettingsDir()2451 void Settings::createSettingsDir()
2452 {
2453     QMutexLocker locker{&bigLock};
2454 
2455     QString dir = Settings::getSettingsDirPath();
2456     QDir directory(dir);
2457     if (!directory.exists() && !directory.mkpath(directory.absolutePath()))
2458         qCritical() << "Error while creating directory " << dir;
2459 }
2460 
2461 /**
2462  * @brief Waits for all asynchronous operations to complete
2463  */
sync()2464 void Settings::sync()
2465 {
2466     if (QThread::currentThread() != settingsThread) {
2467         QMetaObject::invokeMethod(&getInstance(), "sync", Qt::BlockingQueuedConnection);
2468         return;
2469     }
2470 
2471     QMutexLocker locker{&bigLock};
2472     qApp->processEvents();
2473 }
2474 
getOrInsertFriendPropRef(const ToxPk & id)2475 Settings::friendProp& Settings::getOrInsertFriendPropRef(const ToxPk& id)
2476 {
2477     // No mutex lock, this is a private fn that should only be called by other
2478     // public functions that already locked the mutex
2479     auto it = friendLst.find(id.getByteArray());
2480     if (it == friendLst.end()) {
2481         it = friendLst.insert(id.getByteArray(), friendProp{id.toString()});
2482     }
2483 
2484     return *it;
2485 }
2486 
fixInvalidProxyType(ICoreSettings::ProxyType proxyType)2487 ICoreSettings::ProxyType Settings::fixInvalidProxyType(ICoreSettings::ProxyType proxyType)
2488 {
2489     // Repair uninitialized enum that was saved to settings due to bug (https://github.com/qTox/qTox/issues/5311)
2490     switch (proxyType) {
2491     case ICoreSettings::ProxyType::ptNone:
2492     case ICoreSettings::ProxyType::ptSOCKS5:
2493     case ICoreSettings::ProxyType::ptHTTP:
2494         return proxyType;
2495     default:
2496         qWarning() << "Repairing invalid ProxyType, UDP will be enabled";
2497         return ICoreSettings::ProxyType::ptNone;
2498     }
2499 }
2500