1 /*
2 * Copyright (C) by Klaas Freitag <freitag@owncloud.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 */
14
15 #include "config.h"
16
17 #include "common/utility.h"
18 #include "common/asserts.h"
19 #include "configfile.h"
20 #include "logger.h"
21 #include "theme.h"
22 #include "version.h"
23
24 #include "creds/abstractcredentials.h"
25
26 #include "csync_exclude.h"
27
28 #ifndef TOKEN_AUTH_ONLY
29 #include <QWidget>
30 #include <QHeaderView>
31 #endif
32
33 #include <QCoreApplication>
34 #include <QDir>
35 #include <QFile>
36 #include <QFileInfo>
37 #include <QLoggingCategory>
38 #include <QSettings>
39 #include <QNetworkProxy>
40 #include <QOperatingSystemVersion>
41 #include <QStandardPaths>
42
43 #define DEFAULT_REMOTE_POLL_INTERVAL 30000 // default remote poll time in milliseconds
44 #define DEFAULT_MAX_LOG_LINES 20000
45
46 namespace OCC {
47
48 namespace chrono = std::chrono;
49
50 Q_LOGGING_CATEGORY(lcConfigFile, "sync.configfile", QtInfoMsg)
51 namespace {
logHttpC()52 const QString logHttpC() { return QStringLiteral("logHttp"); }
remotePollIntervalC()53 const QString remotePollIntervalC() { return QStringLiteral("remotePollInterval"); }
54 //const QString caCertsKeyC() { return QStringLiteral("CaCertificates"); } only used from account.cpp
forceSyncIntervalC()55 const QString forceSyncIntervalC() { return QStringLiteral("forceSyncInterval"); }
fullLocalDiscoveryIntervalC()56 const QString fullLocalDiscoveryIntervalC() { return QStringLiteral("fullLocalDiscoveryInterval"); }
notificationRefreshIntervalC()57 const QString notificationRefreshIntervalC() { return QStringLiteral("notificationRefreshInterval"); }
monoIconsC()58 const QString monoIconsC() { return QStringLiteral("monoIcons"); }
promptDeleteC()59 const QString promptDeleteC() { return QStringLiteral("promptDeleteAllFiles"); }
crashReporterC()60 const QString crashReporterC() { return QStringLiteral("crashReporter"); }
optionalDesktopNoficationsC()61 const QString optionalDesktopNoficationsC() { return QStringLiteral("optionalDesktopNotifications"); }
showInExplorerNavigationPaneC()62 const QString showInExplorerNavigationPaneC() { return QStringLiteral("showInExplorerNavigationPane"); }
skipUpdateCheckC()63 const QString skipUpdateCheckC() { return QStringLiteral("skipUpdateCheck"); }
updateCheckIntervalC()64 const QString updateCheckIntervalC() { return QStringLiteral("updateCheckInterval"); }
updateChannelC()65 const QString updateChannelC() { return QStringLiteral("updateChannel"); }
geometryC()66 const QString geometryC() { return QStringLiteral("geometry"); }
timeoutC()67 const QString timeoutC() { return QStringLiteral("timeout"); }
chunkSizeC()68 const QString chunkSizeC() { return QStringLiteral("chunkSize"); }
minChunkSizeC()69 const QString minChunkSizeC() { return QStringLiteral("minChunkSize"); }
maxChunkSizeC()70 const QString maxChunkSizeC() { return QStringLiteral("maxChunkSize"); }
targetChunkUploadDurationC()71 const QString targetChunkUploadDurationC() { return QStringLiteral("targetChunkUploadDuration"); }
automaticLogDirC()72 const QString automaticLogDirC() { return QStringLiteral("logToTemporaryLogDir"); }
deleteOldLogsAfterHoursC()73 const QString deleteOldLogsAfterHoursC() { return QStringLiteral("temporaryLogDirDeleteOldLogsAfterHours"); }
showExperimentalOptionsC()74 const QString showExperimentalOptionsC() { return QStringLiteral("showExperimentalOptions"); }
clientVersionC()75 const QString clientVersionC() { return QStringLiteral("clientVersion"); }
76
proxyHostC()77 const QString proxyHostC() { return QStringLiteral("Proxy/host"); }
proxyTypeC()78 const QString proxyTypeC() { return QStringLiteral("Proxy/type"); }
proxyPortC()79 const QString proxyPortC() { return QStringLiteral("Proxy/port"); }
proxyUserC()80 const QString proxyUserC() { return QStringLiteral("Proxy/user"); }
proxyPassC()81 const QString proxyPassC() { return QStringLiteral("Proxy/pass"); }
proxyNeedsAuthC()82 const QString proxyNeedsAuthC() { return QStringLiteral("Proxy/needsAuth"); }
83
useUploadLimitC()84 const QString useUploadLimitC() { return QStringLiteral("BWLimit/useUploadLimit"); }
useDownloadLimitC()85 const QString useDownloadLimitC() { return QStringLiteral("BWLimit/useDownloadLimit"); }
uploadLimitC()86 const QString uploadLimitC() { return QStringLiteral("BWLimit/uploadLimit"); }
downloadLimitC()87 const QString downloadLimitC() { return QStringLiteral("BWLimit/downloadLimit"); }
88
newBigFolderSizeLimitC()89 const QString newBigFolderSizeLimitC() { return QStringLiteral("newBigFolderSizeLimit"); }
useNewBigFolderSizeLimitC()90 const QString useNewBigFolderSizeLimitC() { return QStringLiteral("useNewBigFolderSizeLimit"); }
confirmExternalStorageC()91 const QString confirmExternalStorageC() { return QStringLiteral("confirmExternalStorage"); }
moveToTrashC()92 const QString moveToTrashC() { return QStringLiteral("moveToTrash"); }
93
certPath()94 const QString certPath() { return QStringLiteral("http_certificatePath"); }
certPasswd()95 const QString certPasswd() { return QStringLiteral("http_certificatePasswd"); }
96 }
97
98 QString ConfigFile::_confDir = QString();
99 bool ConfigFile::_askedUser = false;
100
millisecondsValue(const QSettings & setting,const QString & key,chrono::milliseconds defaultValue)101 static chrono::milliseconds millisecondsValue(const QSettings &setting, const QString &key,
102 chrono::milliseconds defaultValue)
103 {
104 return chrono::milliseconds(setting.value(key, qlonglong(defaultValue.count())).toLongLong());
105 }
106
ConfigFile()107 ConfigFile::ConfigFile()
108 {
109 // QDesktopServices uses the application name to create a config path
110 // TODO: we use multiple calls to setApplicationName with different arguments...
111 qApp->setApplicationName(Theme::instance()->appNameGUI());
112
113 QSettings::setDefaultFormat(QSettings::IniFormat);
114
115 const QString config = configFile();
116
117
118 QSettings settings(config, QSettings::IniFormat);
119 settings.beginGroup(defaultConnection());
120
121 // run init only once
122 static bool init = [this]() {
123 setLogHttp(logHttp());
124 return false;
125 }();
126 Q_UNUSED(init);
127 }
128
setConfDir(const QString & value)129 bool ConfigFile::setConfDir(const QString &value)
130 {
131 QString dirPath = value;
132 if (dirPath.isEmpty())
133 return false;
134
135 QFileInfo fi(dirPath);
136 if (!fi.exists()) {
137 QDir().mkpath(dirPath);
138 fi.setFile(dirPath);
139 }
140 if (fi.exists() && fi.isDir()) {
141 dirPath = fi.absoluteFilePath();
142 qCInfo(lcConfigFile) << "Using custom config dir " << dirPath;
143 _confDir = dirPath;
144 return true;
145 }
146 return false;
147 }
148
optionalDesktopNotifications() const149 bool ConfigFile::optionalDesktopNotifications() const
150 {
151 QSettings settings(configFile(), QSettings::IniFormat);
152 return settings.value(optionalDesktopNoficationsC(), true).toBool();
153 }
154
showInExplorerNavigationPane() const155 bool ConfigFile::showInExplorerNavigationPane() const
156 {
157 QSettings settings(configFile(), QSettings::IniFormat);
158 return settings.value(showInExplorerNavigationPaneC(), QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10).toBool();
159 }
160
setShowInExplorerNavigationPane(bool show)161 void ConfigFile::setShowInExplorerNavigationPane(bool show)
162 {
163 QSettings settings(configFile(), QSettings::IniFormat);
164 settings.setValue(showInExplorerNavigationPaneC(), show);
165 settings.sync();
166 }
167
timeout() const168 int ConfigFile::timeout() const
169 {
170 QSettings settings(configFile(), QSettings::IniFormat);
171 return settings.value(timeoutC(), 300).toInt(); // default to 5 min
172 }
173
chunkSize() const174 qint64 ConfigFile::chunkSize() const
175 {
176 QSettings settings(configFile(), QSettings::IniFormat);
177 return settings.value(chunkSizeC(), 10 * 1000 * 1000).toLongLong(); // default to 10 MB
178 }
179
maxChunkSize() const180 qint64 ConfigFile::maxChunkSize() const
181 {
182 QSettings settings(configFile(), QSettings::IniFormat);
183 return settings.value(maxChunkSizeC(), 100 * 1000 * 1000).toLongLong(); // default to 100 MB
184 }
185
minChunkSize() const186 qint64 ConfigFile::minChunkSize() const
187 {
188 QSettings settings(configFile(), QSettings::IniFormat);
189 return settings.value(minChunkSizeC(), 1000 * 1000).toLongLong(); // default to 1 MB
190 }
191
targetChunkUploadDuration() const192 chrono::milliseconds ConfigFile::targetChunkUploadDuration() const
193 {
194 QSettings settings(configFile(), QSettings::IniFormat);
195 return millisecondsValue(settings, targetChunkUploadDurationC(), chrono::minutes(1));
196 }
197
setOptionalDesktopNotifications(bool show)198 void ConfigFile::setOptionalDesktopNotifications(bool show)
199 {
200 QSettings settings(configFile(), QSettings::IniFormat);
201 settings.setValue(optionalDesktopNoficationsC(), show);
202 settings.sync();
203 }
204
saveGeometry(QWidget * w)205 void ConfigFile::saveGeometry(QWidget *w)
206 {
207 #ifndef TOKEN_AUTH_ONLY
208 OC_ASSERT(!w->objectName().isNull());
209 QSettings settings(configFile(), QSettings::IniFormat);
210 settings.beginGroup(w->objectName());
211 settings.setValue(geometryC(), w->saveGeometry());
212 settings.sync();
213 #endif
214 }
215
restoreGeometry(QWidget * w)216 void ConfigFile::restoreGeometry(QWidget *w)
217 {
218 #ifndef TOKEN_AUTH_ONLY
219 w->restoreGeometry(getValue(geometryC(), w->objectName()).toByteArray());
220 #endif
221 }
222
saveGeometryHeader(QHeaderView * header)223 void ConfigFile::saveGeometryHeader(QHeaderView *header)
224 {
225 #ifndef TOKEN_AUTH_ONLY
226 if (!header)
227 return;
228 OC_ASSERT(!header->objectName().isEmpty());
229
230 QSettings settings(configFile(), QSettings::IniFormat);
231 settings.beginGroup(header->objectName());
232 settings.setValue(geometryC(), header->saveState());
233 settings.sync();
234 #endif
235 }
236
restoreGeometryHeader(QHeaderView * header)237 void ConfigFile::restoreGeometryHeader(QHeaderView *header)
238 {
239 #ifndef TOKEN_AUTH_ONLY
240 if (!header)
241 return;
242 OC_ASSERT(!header->objectName().isNull());
243
244 QSettings settings(configFile(), QSettings::IniFormat);
245 settings.beginGroup(header->objectName());
246 header->restoreState(settings.value(geometryC()).toByteArray());
247 #endif
248 }
249
getPolicySetting(const QString & setting,const QVariant & defaultValue) const250 QVariant ConfigFile::getPolicySetting(const QString &setting, const QVariant &defaultValue) const
251 {
252 if (Utility::isWindows()) {
253 // check for policies first and return immediately if a value is found.
254 QSettings userPolicy(QStringLiteral("HKEY_CURRENT_USER\\Software\\Policies\\%1\\%2")
255 .arg(QStringLiteral(APPLICATION_VENDOR), Theme::instance()->appNameGUI()),
256 QSettings::NativeFormat);
257 if (userPolicy.contains(setting)) {
258 return userPolicy.value(setting);
259 }
260
261 QSettings machinePolicy(QStringLiteral("HKEY_LOCAL_MACHINE\\Software\\Policies\\%1\\%2")
262 .arg(QStringLiteral(APPLICATION_VENDOR), Theme::instance()->appNameGUI()),
263 QSettings::NativeFormat);
264 if (machinePolicy.contains(setting)) {
265 return machinePolicy.value(setting);
266 }
267 }
268 return defaultValue;
269 }
270
configPath() const271 QString ConfigFile::configPath() const
272 {
273 if (_confDir.isEmpty()) {
274 // On Unix, use the AppConfigLocation for the settings, that's configurable with the XDG_CONFIG_HOME env variable.
275 // On Windows, use AppDataLocation, that's where the roaming data is and where we should store the config file
276 _confDir = QStandardPaths::writableLocation(Utility::isWindows() ? QStandardPaths::AppDataLocation : QStandardPaths::AppConfigLocation);
277 }
278 QString dir = _confDir;
279
280 if (!dir.endsWith(QLatin1Char('/')))
281 dir.append(QLatin1Char('/'));
282 return dir;
283 }
284
285 static const QLatin1String exclFile("sync-exclude.lst");
286
excludeFile(Scope scope) const287 QString ConfigFile::excludeFile(Scope scope) const
288 {
289 // prefer sync-exclude.lst, but if it does not exist, check for
290 // exclude.lst for compatibility reasons in the user writeable
291 // directories.
292 QFileInfo fi;
293
294 switch (scope) {
295 case UserScope:
296 fi.setFile(configPath(), exclFile);
297
298 if (!fi.isReadable()) {
299 fi.setFile(configPath(), QStringLiteral("exclude.lst"));
300 }
301 if (!fi.isReadable()) {
302 fi.setFile(configPath(), exclFile);
303 }
304 return fi.absoluteFilePath();
305 case SystemScope:
306 return ConfigFile::excludeFileFromSystem();
307 }
308
309 OC_ASSERT(false);
310 return QString();
311 }
312
excludeFileFromSystem()313 QString ConfigFile::excludeFileFromSystem()
314 {
315 QFileInfo fi;
316 #ifdef Q_OS_WIN
317 fi.setFile(QCoreApplication::applicationDirPath(), exclFile);
318 #endif
319 #ifdef Q_OS_UNIX
320 fi.setFile(QStringLiteral(SYSCONFDIR "/%1").arg(Theme::instance()->appName()), exclFile);
321 if (!fi.exists()) {
322 // Prefer to return the preferred path! Only use the fallback location
323 // if the other path does not exist and the fallback is valid.
324 QFileInfo nextToBinary(QCoreApplication::applicationDirPath(), exclFile);
325 if (nextToBinary.exists()) {
326 fi = nextToBinary;
327 } else {
328 // For AppImage, the file might reside under a temporary mount path
329 QDir d(QCoreApplication::applicationDirPath()); // supposed to be /tmp/mount.xyz/usr/bin
330 d.cdUp(); // go out of bin
331 d.cdUp(); // go out of usr
332 if (!d.isRoot()) { // it is really a mountpoint
333 if (d.cd(QStringLiteral("etc")) && d.cd(Theme::instance()->appName())) {
334 QFileInfo inMountDir(d, exclFile);
335 if (inMountDir.exists()) {
336 fi = inMountDir;
337 }
338 };
339 }
340 }
341 }
342 #endif
343 #ifdef Q_OS_MAC
344 // exec path is inside the bundle
345 fi.setFile(QCoreApplication::applicationDirPath(),
346 QLatin1String("../Resources/") + exclFile);
347 #endif
348
349 return fi.absoluteFilePath();
350 }
351
backup() const352 QString ConfigFile::backup() const
353 {
354 QString baseFile = configFile();
355 auto versionString = clientVersionString();
356 if (!versionString.isEmpty())
357 versionString.prepend(QLatin1Char('_'));
358 QString backupFile =
359 QStringLiteral("%1.backup_%2%3")
360 .arg(baseFile)
361 .arg(QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd_HHmmss")))
362 .arg(versionString);
363
364 // If this exact file already exists it's most likely that a backup was
365 // already done. (two backup calls directly after each other, potentially
366 // even with source alterations in between!)
367 if (!QFile::exists(backupFile)) {
368 QFile f(baseFile);
369 f.copy(backupFile);
370 }
371 return backupFile;
372 }
373
configFile() const374 QString ConfigFile::configFile() const
375 {
376 return configPath() + Theme::instance()->configFileName();
377 }
378
exists()379 bool ConfigFile::exists()
380 {
381 QFile file(configFile());
382 return file.exists();
383 }
384
defaultConnection() const385 QString ConfigFile::defaultConnection() const
386 {
387 return Theme::instance()->appName();
388 }
389
storeData(const QString & group,const QString & key,const QVariant & value)390 void ConfigFile::storeData(const QString &group, const QString &key, const QVariant &value)
391 {
392 const QString con(group.isEmpty() ? defaultConnection() : group);
393 QSettings settings(configFile(), QSettings::IniFormat);
394
395 settings.beginGroup(con);
396 settings.setValue(key, value);
397 settings.sync();
398 }
399
retrieveData(const QString & group,const QString & key) const400 QVariant ConfigFile::retrieveData(const QString &group, const QString &key) const
401 {
402 const QString con(group.isEmpty() ? defaultConnection() : group);
403 QSettings settings(configFile(), QSettings::IniFormat);
404
405 settings.beginGroup(con);
406 return settings.value(key);
407 }
408
removeData(const QString & group,const QString & key)409 void ConfigFile::removeData(const QString &group, const QString &key)
410 {
411 const QString con(group.isEmpty() ? defaultConnection() : group);
412 QSettings settings(configFile(), QSettings::IniFormat);
413
414 settings.beginGroup(con);
415 settings.remove(key);
416 }
417
dataExists(const QString & group,const QString & key) const418 bool ConfigFile::dataExists(const QString &group, const QString &key) const
419 {
420 const QString con(group.isEmpty() ? defaultConnection() : group);
421 QSettings settings(configFile(), QSettings::IniFormat);
422
423 settings.beginGroup(con);
424 return settings.contains(key);
425 }
426
remotePollInterval(const QString & connection) const427 chrono::milliseconds ConfigFile::remotePollInterval(const QString &connection) const
428 {
429 QString con(connection);
430 if (connection.isEmpty())
431 con = defaultConnection();
432
433 QSettings settings(configFile(), QSettings::IniFormat);
434 settings.beginGroup(con);
435
436 auto defaultPollInterval = chrono::milliseconds(DEFAULT_REMOTE_POLL_INTERVAL);
437 auto remoteInterval = millisecondsValue(settings, remotePollIntervalC(), defaultPollInterval);
438 if (remoteInterval < chrono::seconds(5)) {
439 qCWarning(lcConfigFile) << "Remote Interval is less than 5 seconds, reverting to" << DEFAULT_REMOTE_POLL_INTERVAL;
440 remoteInterval = defaultPollInterval;
441 }
442 return remoteInterval;
443 }
444
setRemotePollInterval(chrono::milliseconds interval,const QString & connection)445 void ConfigFile::setRemotePollInterval(chrono::milliseconds interval, const QString &connection)
446 {
447 QString con(connection);
448 if (connection.isEmpty())
449 con = defaultConnection();
450
451 if (interval < chrono::seconds(5)) {
452 qCWarning(lcConfigFile) << "Remote Poll interval of " << interval.count() << " is below five seconds.";
453 return;
454 }
455 QSettings settings(configFile(), QSettings::IniFormat);
456 settings.beginGroup(con);
457 settings.setValue(remotePollIntervalC(), qlonglong(interval.count()));
458 settings.sync();
459 }
460
forceSyncInterval(const QString & connection) const461 chrono::milliseconds ConfigFile::forceSyncInterval(const QString &connection) const
462 {
463 auto pollInterval = remotePollInterval(connection);
464
465 QString con(connection);
466 if (connection.isEmpty())
467 con = defaultConnection();
468 QSettings settings(configFile(), QSettings::IniFormat);
469 settings.beginGroup(con);
470
471 auto defaultInterval = chrono::hours(2);
472 auto interval = millisecondsValue(settings, forceSyncIntervalC(), defaultInterval);
473 if (interval < pollInterval) {
474 qCWarning(lcConfigFile) << "Force sync interval is less than the remote poll inteval, reverting to" << pollInterval.count();
475 interval = pollInterval;
476 }
477 return interval;
478 }
479
fullLocalDiscoveryInterval() const480 chrono::milliseconds OCC::ConfigFile::fullLocalDiscoveryInterval() const
481 {
482 QSettings settings(configFile(), QSettings::IniFormat);
483 settings.beginGroup(defaultConnection());
484 return millisecondsValue(settings, fullLocalDiscoveryIntervalC(), chrono::hours(1));
485 }
486
notificationRefreshInterval(const QString & connection) const487 chrono::milliseconds ConfigFile::notificationRefreshInterval(const QString &connection) const
488 {
489 QString con(connection);
490 if (connection.isEmpty())
491 con = defaultConnection();
492 QSettings settings(configFile(), QSettings::IniFormat);
493 settings.beginGroup(con);
494
495 auto defaultInterval = chrono::minutes(5);
496 auto interval = millisecondsValue(settings, notificationRefreshIntervalC(), defaultInterval);
497 if (interval < chrono::minutes(1)) {
498 qCWarning(lcConfigFile) << "Notification refresh interval smaller than one minute, setting to one minute";
499 interval = chrono::minutes(1);
500 }
501 return interval;
502 }
503
updateCheckInterval(const QString & connection) const504 chrono::milliseconds ConfigFile::updateCheckInterval(const QString &connection) const
505 {
506 QString con(connection);
507 if (connection.isEmpty())
508 con = defaultConnection();
509 QSettings settings(configFile(), QSettings::IniFormat);
510 settings.beginGroup(con);
511
512 auto defaultInterval = chrono::hours(10);
513 auto interval = millisecondsValue(settings, updateCheckIntervalC(), defaultInterval);
514
515 auto minInterval = chrono::minutes(5);
516 if (interval < minInterval) {
517 qCWarning(lcConfigFile) << "Update check interval less than five minutes, resetting to 5 minutes";
518 interval = minInterval;
519 }
520 return interval;
521 }
522
skipUpdateCheck(const QString & connection) const523 bool ConfigFile::skipUpdateCheck(const QString &connection) const
524 {
525 QString con(connection);
526 if (connection.isEmpty())
527 con = defaultConnection();
528
529 #if 0
530 QVariant fallback = getValue(skipUpdateCheckC(), con, false);
531 #else
532 QVariant fallback = getValue(skipUpdateCheckC(), con, true);
533 #endif
534 fallback = getValue(skipUpdateCheckC(), QString(), fallback);
535
536 QVariant value = getPolicySetting(skipUpdateCheckC(), fallback);
537 #if 0
538 return value.toBool();
539 #else
540 if ( !value.toBool() )
541 qDebug() << "FreeBSD package disabled the UpdateCheck mechanism.";
542
543 return true;
544 #endif
545 }
546
setSkipUpdateCheck(bool skip,const QString & connection)547 void ConfigFile::setSkipUpdateCheck(bool skip, const QString &connection)
548 {
549 QString con(connection);
550 if (connection.isEmpty())
551 con = defaultConnection();
552
553 QSettings settings(configFile(), QSettings::IniFormat);
554 settings.beginGroup(con);
555
556 settings.setValue(skipUpdateCheckC(), QVariant(skip));
557 settings.sync();
558 }
559
updateChannel() const560 QString ConfigFile::updateChannel() const
561 {
562 QString defaultUpdateChannel = QStringLiteral("stable");
563 QString suffix = QString::fromLatin1(MIRALL_STRINGIFY(MIRALL_VERSION_SUFFIX));
564 if (suffix.startsWith(QLatin1String("daily"))
565 || suffix.startsWith(QLatin1String("nightly"))
566 || suffix.startsWith(QLatin1String("alpha"))
567 || suffix.startsWith(QLatin1String("rc"))
568 || suffix.startsWith(QLatin1String("beta"))) {
569 defaultUpdateChannel = QStringLiteral("beta");
570 }
571
572 QSettings settings(configFile(), QSettings::IniFormat);
573 return settings.value(updateChannelC(), defaultUpdateChannel).toString();
574 }
575
setUpdateChannel(const QString & channel)576 void ConfigFile::setUpdateChannel(const QString &channel)
577 {
578 QSettings settings(configFile(), QSettings::IniFormat);
579 settings.setValue(updateChannelC(), channel);
580 }
581
setProxyType(int proxyType,const QString & host,int port,bool needsAuth,const QString & user,const QString & pass)582 void ConfigFile::setProxyType(int proxyType,
583 const QString &host,
584 int port, bool needsAuth,
585 const QString &user,
586 const QString &pass)
587 {
588 QSettings settings(configFile(), QSettings::IniFormat);
589
590 settings.setValue(proxyTypeC(), proxyType);
591
592 if (proxyType == QNetworkProxy::HttpProxy || proxyType == QNetworkProxy::Socks5Proxy) {
593 settings.setValue(proxyHostC(), host);
594 settings.setValue(proxyPortC(), port);
595 settings.setValue(proxyNeedsAuthC(), needsAuth);
596 settings.setValue(proxyUserC(), user);
597 settings.setValue(proxyPassC(), pass.toUtf8().toBase64());
598 }
599 settings.sync();
600 }
601
getValue(const QString & param,const QString & group,const QVariant & defaultValue) const602 QVariant ConfigFile::getValue(const QString ¶m, const QString &group,
603 const QVariant &defaultValue) const
604 {
605 QVariant systemSetting;
606 if (Utility::isMac()) {
607 QSettings systemSettings(QStringLiteral("/Library/Preferences/" APPLICATION_REV_DOMAIN ".plist"), QSettings::NativeFormat);
608 if (!group.isEmpty()) {
609 systemSettings.beginGroup(group);
610 }
611 systemSetting = systemSettings.value(param, defaultValue);
612 } else if (Utility::isUnix()) {
613 QSettings systemSettings(QStringLiteral(SYSCONFDIR "/%1/%1.conf").arg(Theme::instance()->appName()), QSettings::NativeFormat);
614 if (!group.isEmpty()) {
615 systemSettings.beginGroup(group);
616 }
617 systemSetting = systemSettings.value(param, defaultValue);
618 } else { // Windows
619 QSettings systemSettings(QStringLiteral("HKEY_LOCAL_MACHINE\\Software\\" APPLICATION_VENDOR "\\%1")
620 .arg(Theme::instance()->appNameGUI()),
621 QSettings::NativeFormat);
622 if (!group.isEmpty()) {
623 systemSettings.beginGroup(group);
624 }
625 systemSetting = systemSettings.value(param, defaultValue);
626 }
627
628 QSettings settings(configFile(), QSettings::IniFormat);
629 if (!group.isEmpty())
630 settings.beginGroup(group);
631
632 return settings.value(param, systemSetting);
633 }
634
setValue(const QString & key,const QVariant & value)635 void ConfigFile::setValue(const QString &key, const QVariant &value)
636 {
637 QSettings settings(configFile(), QSettings::IniFormat);
638
639 settings.setValue(key, value);
640 }
641
proxyType() const642 int ConfigFile::proxyType() const
643 {
644 if (Theme::instance()->forceSystemNetworkProxy()) {
645 return QNetworkProxy::DefaultProxy;
646 }
647 return getValue(proxyTypeC()).toInt();
648 }
649
proxyHostName() const650 QString ConfigFile::proxyHostName() const
651 {
652 return getValue(proxyHostC()).toString();
653 }
654
proxyPort() const655 int ConfigFile::proxyPort() const
656 {
657 return getValue(proxyPortC()).toInt();
658 }
659
proxyNeedsAuth() const660 bool ConfigFile::proxyNeedsAuth() const
661 {
662 return getValue(proxyNeedsAuthC()).toBool();
663 }
664
proxyUser() const665 QString ConfigFile::proxyUser() const
666 {
667 return getValue(proxyUserC()).toString();
668 }
669
proxyPassword() const670 QString ConfigFile::proxyPassword() const
671 {
672 QByteArray pass = getValue(proxyPassC()).toByteArray();
673 return QString::fromUtf8(QByteArray::fromBase64(pass));
674 }
675
useUploadLimit() const676 int ConfigFile::useUploadLimit() const
677 {
678 return getValue(useUploadLimitC(), QString(), 0).toInt();
679 }
680
useDownloadLimit() const681 int ConfigFile::useDownloadLimit() const
682 {
683 return getValue(useDownloadLimitC(), QString(), 0).toInt();
684 }
685
setUseUploadLimit(int val)686 void ConfigFile::setUseUploadLimit(int val)
687 {
688 setValue(useUploadLimitC(), val);
689 }
690
setUseDownloadLimit(int val)691 void ConfigFile::setUseDownloadLimit(int val)
692 {
693 setValue(useDownloadLimitC(), val);
694 }
695
uploadLimit() const696 int ConfigFile::uploadLimit() const
697 {
698 return getValue(uploadLimitC(), QString(), 10).toInt();
699 }
700
downloadLimit() const701 int ConfigFile::downloadLimit() const
702 {
703 return getValue(downloadLimitC(), QString(), 80).toInt();
704 }
705
setUploadLimit(int kbytes)706 void ConfigFile::setUploadLimit(int kbytes)
707 {
708 setValue(uploadLimitC(), kbytes);
709 }
710
setDownloadLimit(int kbytes)711 void ConfigFile::setDownloadLimit(int kbytes)
712 {
713 setValue(downloadLimitC(), kbytes);
714 }
715
newBigFolderSizeLimit() const716 QPair<bool, qint64> ConfigFile::newBigFolderSizeLimit() const
717 {
718 auto defaultValue = Theme::instance()->newBigFolderSizeLimit();
719 qint64 value = getValue(newBigFolderSizeLimitC(), QString(), defaultValue).toLongLong();
720 bool use = value >= 0 && getValue(useNewBigFolderSizeLimitC(), QString(), true).toBool();
721 return qMakePair(use, qMax<qint64>(0, value));
722 }
723
setNewBigFolderSizeLimit(bool isChecked,qint64 mbytes)724 void ConfigFile::setNewBigFolderSizeLimit(bool isChecked, qint64 mbytes)
725 {
726 setValue(newBigFolderSizeLimitC(), mbytes);
727 setValue(useNewBigFolderSizeLimitC(), isChecked);
728 }
729
confirmExternalStorage() const730 bool ConfigFile::confirmExternalStorage() const
731 {
732 return getValue(confirmExternalStorageC(), QString(), true).toBool();
733 }
734
setConfirmExternalStorage(bool isChecked)735 void ConfigFile::setConfirmExternalStorage(bool isChecked)
736 {
737 setValue(confirmExternalStorageC(), isChecked);
738 }
739
moveToTrash() const740 bool ConfigFile::moveToTrash() const
741 {
742 return getValue(moveToTrashC(), QString(), false).toBool();
743 }
744
setMoveToTrash(bool isChecked)745 void ConfigFile::setMoveToTrash(bool isChecked)
746 {
747 setValue(moveToTrashC(), isChecked);
748 }
749
promptDeleteFiles() const750 bool ConfigFile::promptDeleteFiles() const
751 {
752 QSettings settings(configFile(), QSettings::IniFormat);
753 return settings.value(promptDeleteC(), true).toBool();
754 }
755
setPromptDeleteFiles(bool promptDeleteFiles)756 void ConfigFile::setPromptDeleteFiles(bool promptDeleteFiles)
757 {
758 QSettings settings(configFile(), QSettings::IniFormat);
759 settings.setValue(promptDeleteC(), promptDeleteFiles);
760 }
761
monoIcons() const762 bool ConfigFile::monoIcons() const
763 {
764 QSettings settings(configFile(), QSettings::IniFormat);
765 bool monoDefault = false; // On Mac we want bw by default
766 #ifdef Q_OS_MAC
767 // OEM themes are not obliged to ship mono icons
768 monoDefault = (0 == (strcmp("ownCloud", APPLICATION_NAME)));
769 #endif
770 return settings.value(monoIconsC(), monoDefault).toBool();
771 }
772
setMonoIcons(bool useMonoIcons)773 void ConfigFile::setMonoIcons(bool useMonoIcons)
774 {
775 QSettings settings(configFile(), QSettings::IniFormat);
776 settings.setValue(monoIconsC(), useMonoIcons);
777 }
778
crashReporter() const779 bool ConfigFile::crashReporter() const
780 {
781 QSettings settings(configFile(), QSettings::IniFormat);
782 return settings.value(crashReporterC(), true).toBool();
783 }
784
setCrashReporter(bool enabled)785 void ConfigFile::setCrashReporter(bool enabled)
786 {
787 QSettings settings(configFile(), QSettings::IniFormat);
788 settings.setValue(crashReporterC(), enabled);
789 }
790
automaticLogDir() const791 bool ConfigFile::automaticLogDir() const
792 {
793 QSettings settings(configFile(), QSettings::IniFormat);
794 return settings.value(automaticLogDirC(), false).toBool();
795 }
796
setAutomaticLogDir(bool enabled)797 void ConfigFile::setAutomaticLogDir(bool enabled)
798 {
799 QSettings settings(configFile(), QSettings::IniFormat);
800 settings.setValue(automaticLogDirC(), enabled);
801 }
802
automaticDeleteOldLogsAge() const803 Optional<chrono::hours> ConfigFile::automaticDeleteOldLogsAge() const
804 {
805 QSettings settings(configFile(), QSettings::IniFormat);
806 auto value = settings.value(deleteOldLogsAfterHoursC());
807 if (!value.isValid())
808 return chrono::hours(4);
809 auto hours = value.toInt();
810 if (hours <= 0)
811 return {};
812 return chrono::hours(hours);
813 }
814
setAutomaticDeleteOldLogsAge(Optional<chrono::hours> expireTime)815 void ConfigFile::setAutomaticDeleteOldLogsAge(Optional<chrono::hours> expireTime)
816 {
817 QSettings settings(configFile(), QSettings::IniFormat);
818 if (!expireTime) {
819 settings.setValue(deleteOldLogsAfterHoursC(), -1);
820 } else {
821 settings.setValue(deleteOldLogsAfterHoursC(), QVariant::fromValue(expireTime->count()));
822 }
823 }
824
setLogHttp(bool b)825 void ConfigFile::setLogHttp(bool b)
826 {
827 QSettings settings(configFile(), QSettings::IniFormat);
828 settings.setValue(logHttpC(), b);
829 const QSet<QString> rule = { QStringLiteral("sync.httplogger=true") };
830 if (b) {
831 Logger::instance()->addLogRule(rule);
832 } else {
833 Logger::instance()->removeLogRule(rule);
834 }
835 }
836
logHttp() const837 bool ConfigFile::logHttp() const
838 {
839 QSettings settings(configFile(), QSettings::IniFormat);
840 return settings.value(logHttpC(), false).toBool();
841 }
842
showExperimentalOptions() const843 bool ConfigFile::showExperimentalOptions() const
844 {
845 QSettings settings(configFile(), QSettings::IniFormat);
846 return settings.value(showExperimentalOptionsC(), false).toBool();
847 }
848
certificatePath() const849 QString ConfigFile::certificatePath() const
850 {
851 return retrieveData(QString(), certPath()).toString();
852 }
853
setCertificatePath(const QString & cPath)854 void ConfigFile::setCertificatePath(const QString &cPath)
855 {
856 QSettings settings(configFile(), QSettings::IniFormat);
857 settings.setValue(certPath(), cPath);
858 settings.sync();
859 }
860
certificatePasswd() const861 QString ConfigFile::certificatePasswd() const
862 {
863 return retrieveData(QString(), certPasswd()).toString();
864 }
865
setCertificatePasswd(const QString & cPasswd)866 void ConfigFile::setCertificatePasswd(const QString &cPasswd)
867 {
868 QSettings settings(configFile(), QSettings::IniFormat);
869 settings.setValue(certPasswd(), cPasswd);
870 settings.sync();
871 }
872
clientVersionString() const873 QString ConfigFile::clientVersionString() const
874 {
875 QSettings settings(configFile(), QSettings::IniFormat);
876 return settings.value(clientVersionC(), QString()).toString();
877 }
878
setClientVersionString(const QString & version)879 void ConfigFile::setClientVersionString(const QString &version)
880 {
881 QSettings settings(configFile(), QSettings::IniFormat);
882 settings.setValue(clientVersionC(), version);
883 }
884
Q_GLOBAL_STATIC(QString,g_configFileName)885 Q_GLOBAL_STATIC(QString, g_configFileName)
886
887 std::unique_ptr<QSettings> ConfigFile::settingsWithGroup(const QString &group, QObject *parent)
888 {
889 if (g_configFileName()->isEmpty()) {
890 // cache file name
891 ConfigFile cfg;
892 *g_configFileName() = cfg.configFile();
893 }
894 std::unique_ptr<QSettings> settings(new QSettings(*g_configFileName(), QSettings::IniFormat, parent));
895 settings->beginGroup(group);
896 return settings;
897 }
898
setupDefaultExcludeFilePaths(ExcludedFiles & excludedFiles)899 void ConfigFile::setupDefaultExcludeFilePaths(ExcludedFiles &excludedFiles)
900 {
901 ConfigFile cfg;
902 QString systemList = cfg.excludeFile(ConfigFile::SystemScope);
903 qCInfo(lcConfigFile) << "Adding system ignore list to csync:" << systemList;
904 excludedFiles.addExcludeFilePath(systemList);
905
906 QString userList = cfg.excludeFile(ConfigFile::UserScope);
907 if (QFile::exists(userList)) {
908 qCInfo(lcConfigFile) << "Adding user defined ignore list to csync:" << userList;
909 excludedFiles.addExcludeFilePath(userList);
910 }
911 }
912 }
913