diff --git a/src/common/common.cmake b/src/common/common.cmake index 671973579f0bb..12654276970f8 100644 --- a/src/common/common.cmake +++ b/src/common/common.cmake @@ -17,4 +17,18 @@ set(common_SOURCES ${CMAKE_CURRENT_LIST_DIR}/syncfilestatus.cpp ) +if(WIN32) + list(APPEND common_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/utility_win.cpp + ) +elseif(APPLE) + list(APPEND common_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/utility_mac.mm + ) +elseif(UNIX AND NOT APPLE) + list(APPEND common_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/utility_unix.cpp + ) +endif() + configure_file(${CMAKE_CURRENT_LIST_DIR}/vfspluginmetadata.json.in ${CMAKE_CURRENT_BINARY_DIR}/vfspluginmetadata.json) diff --git a/src/common/utility.cpp b/src/common/utility.cpp index 607bb0414ec7c..a9e538c9e6a65 100644 --- a/src/common/utility.cpp +++ b/src/common/utility.cpp @@ -39,7 +39,6 @@ #include #include - #ifdef Q_OS_UNIX #include #include @@ -50,14 +49,6 @@ #include #include -#if defined(Q_OS_WIN) -#include "utility_win.cpp" -#elif defined(Q_OS_MAC) -#include "utility_mac.cpp" -#else -#include "utility_unix.cpp" -#endif - namespace OCC { Q_LOGGING_CATEGORY(lcUtility, "nextcloud.sync.utility", QtInfoMsg) @@ -104,15 +95,6 @@ QString Utility::formatFingerprint(const QByteArray &fmhash, bool colonSeparated return fp; } -void Utility::setupFavLink(const QString &folder) -{ - setupFavLink_private(folder); -} - -void Utility::removeFavLink(const QString &folder) -{ - removeFavLink_private(folder); -} QString Utility::octetsToString(qint64 octets) { @@ -200,26 +182,6 @@ QByteArray Utility::friendlyUserAgentString() return userAgent.toUtf8(); } -bool Utility::hasSystemLaunchOnStartup(const QString &appName) -{ -#if defined(Q_OS_WIN) - return hasSystemLaunchOnStartup_private(appName); -#else - Q_UNUSED(appName) - return false; -#endif -} - -bool Utility::hasLaunchOnStartup(const QString &appName) -{ - return hasLaunchOnStartup_private(appName); -} - -void Utility::setLaunchOnStartup(const QString &appName, const QString &guiName, bool enable) -{ - setLaunchOnStartup_private(appName, guiName, enable); -} - qint64 Utility::freeDiskSpace(const QString &path) { #if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) || defined(Q_OS_FREEBSD_KERNEL) || defined(Q_OS_NETBSD) || defined(Q_OS_OPENBSD) @@ -404,12 +366,6 @@ QByteArray Utility::normalizeEtag(QByteArray etag) return etag; } -bool Utility::hasDarkSystray() -{ - return hasDarkSystray_private(); -} - - QString Utility::platformName() { return QSysInfo::prettyProductName(); diff --git a/src/common/utility.h b/src/common/utility.h index e19f0da6ae1ee..b5e9b39a5a647 100644 --- a/src/common/utility.h +++ b/src/common/utility.h @@ -70,7 +70,7 @@ namespace Utility { */ OCSYNC_EXPORT bool hasSystemLaunchOnStartup(const QString &appName); OCSYNC_EXPORT bool hasLaunchOnStartup(const QString &appName); - OCSYNC_EXPORT void setLaunchOnStartup(const QString &appName, const QString &guiName, bool launch); + OCSYNC_EXPORT void setLaunchOnStartup(const QString &appName, const QString &guiName, const bool launch); OCSYNC_EXPORT uint convertSizeToUint(size_t &convertVar); OCSYNC_EXPORT int convertSizeToInt(size_t &convertVar); diff --git a/src/common/utility_mac.cpp b/src/common/utility_mac.cpp deleted file mode 100644 index 9a220527fa5ff..0000000000000 --- a/src/common/utility_mac.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) by Daniel Molkentin - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include - -namespace OCC { - -static void setupFavLink_private(const QString &folder) -{ - // Finder: Place under "Places"/"Favorites" on the left sidebar - CFStringRef folderCFStr = CFStringCreateWithCString(0, folder.toUtf8().data(), kCFStringEncodingUTF8); - CFURLRef urlRef = CFURLCreateWithFileSystemPath(0, folderCFStr, kCFURLPOSIXPathStyle, true); - - LSSharedFileListRef placesItems = LSSharedFileListCreate(0, kLSSharedFileListFavoriteItems, 0); - if (placesItems) { - //Insert an item to the list. - LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(placesItems, - kLSSharedFileListItemLast, 0, 0, - urlRef, 0, 0); - if (item) - CFRelease(item); - } - CFRelease(placesItems); - CFRelease(folderCFStr); - CFRelease(urlRef); -} - -static void removeFavLink_private(const QString &folder) -{ - Q_UNUSED(folder) -} - -bool hasLaunchOnStartup_private(const QString &) -{ - // this is quite some duplicate code with setLaunchOnStartup, at some point we should fix this FIXME. - bool returnValue = false; - QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath(); - CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8); - CFURLRef urlRef = CFURLCreateWithFileSystemPath(0, folderCFStr, kCFURLPOSIXPathStyle, true); - LSSharedFileListRef loginItems = LSSharedFileListCreate(0, kLSSharedFileListSessionLoginItems, 0); - if (loginItems) { - // We need to iterate over the items and check which one is "ours". - UInt32 seedValue; - CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue); - CFStringRef appUrlRefString = CFURLGetString(urlRef); // no need for release - for (int i = 0; i < CFArrayGetCount(itemsArray); i++) { - LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(itemsArray, i); - CFURLRef itemUrlRef = nullptr; - - if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, nullptr) == noErr && itemUrlRef) { - CFStringRef itemUrlString = CFURLGetString(itemUrlRef); - if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) { - returnValue = true; - } - CFRelease(itemUrlRef); - } - } - CFRelease(itemsArray); - } - CFRelease(loginItems); - CFRelease(folderCFStr); - CFRelease(urlRef); - return returnValue; -} - -void setLaunchOnStartup_private(const QString &appName, const QString &guiName, bool enable) -{ - Q_UNUSED(appName) - Q_UNUSED(guiName) - QString filePath = QDir(QCoreApplication::applicationDirPath() + QLatin1String("/../..")).absolutePath(); - CFStringRef folderCFStr = CFStringCreateWithCString(0, filePath.toUtf8().data(), kCFStringEncodingUTF8); - CFURLRef urlRef = CFURLCreateWithFileSystemPath(0, folderCFStr, kCFURLPOSIXPathStyle, true); - LSSharedFileListRef loginItems = LSSharedFileListCreate(0, kLSSharedFileListSessionLoginItems, 0); - - if (loginItems && enable) { - //Insert an item to the list. - LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(loginItems, - kLSSharedFileListItemLast, 0, 0, - urlRef, 0, 0); - if (item) - CFRelease(item); - CFRelease(loginItems); - } else if (loginItems && !enable) { - // We need to iterate over the items and check which one is "ours". - UInt32 seedValue; - CFArrayRef itemsArray = LSSharedFileListCopySnapshot(loginItems, &seedValue); - CFStringRef appUrlRefString = CFURLGetString(urlRef); - for (int i = 0; i < CFArrayGetCount(itemsArray); i++) { - LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(itemsArray, i); - CFURLRef itemUrlRef = nullptr; - - if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, nullptr) == noErr && itemUrlRef) { - CFStringRef itemUrlString = CFURLGetString(itemUrlRef); - if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) { - LSSharedFileListItemRemove(loginItems, item); // remove it! - } - CFRelease(itemUrlRef); - } - } - CFRelease(itemsArray); - CFRelease(loginItems); - }; - - CFRelease(folderCFStr); - CFRelease(urlRef); -} - -static bool hasDarkSystray_private() -{ - bool returnValue = false; - CFStringRef interfaceStyleKey = CFSTR("AppleInterfaceStyle"); - CFStringRef interfaceStyle = nullptr; - CFStringRef darkInterfaceStyle = CFSTR("Dark"); - interfaceStyle = (CFStringRef)CFPreferencesCopyAppValue(interfaceStyleKey, - kCFPreferencesCurrentApplication); - if (interfaceStyle) { - returnValue = (kCFCompareEqualTo == CFStringCompare(interfaceStyle, darkInterfaceStyle, 0)); - CFRelease(interfaceStyle); - } - return returnValue; -} - -QString Utility::getCurrentUserName() -{ - return {}; -} - -void Utility::registerUriHandlerForLocalEditing() { /* URI handler is registered via MacOSXBundleInfo.plist.in */ } - -} // namespace OCC diff --git a/src/common/utility_mac.mm b/src/common/utility_mac.mm new file mode 100644 index 0000000000000..9d0f4f03d4979 --- /dev/null +++ b/src/common/utility_mac.mm @@ -0,0 +1,263 @@ +/* + * Copyright (C) by Daniel Molkentin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "result.h" +#include "utility.h" + +#include +#include +#include +#include + +#include +#include +#import +#import + +namespace OCC { + +void Utility::setupFavLink(const QString &folder) +{ + // Finder: Place under "Places"/"Favorites" on the left sidebar + CFStringRef folderCFStr = CFStringCreateWithCString(nullptr, folder.toUtf8().data(), kCFStringEncodingUTF8); + QScopeGuard freeFolder([folderCFStr]() { CFRelease(folderCFStr); }); + + CFURLRef urlRef = CFURLCreateWithFileSystemPath(nullptr, folderCFStr, kCFURLPOSIXPathStyle, true); + QScopeGuard freeUrl([urlRef]() { CFRelease(urlRef); }); + + LSSharedFileListRef placesItems = LSSharedFileListCreate(nullptr, kLSSharedFileListFavoriteItems, nullptr); + QScopeGuard freePlaces([placesItems]() { CFRelease(placesItems); }); + + if (placesItems) { + //Insert an item to the list. + if (LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(placesItems, + kLSSharedFileListItemLast, nullptr, nullptr, urlRef, nullptr, nullptr)) { + CFRelease(item); + } + } +} + +bool Utility::hasSystemLaunchOnStartup(const QString &appName) +{ + Q_UNUSED(appName) + return false; +} + +void Utility::removeFavLink(const QString &folder) +{ + Q_UNUSED(folder) +} + +static Result writePlistToFile(NSString *plistFile, NSDictionary *plist) +{ + NSError *error = nil; + + // Check if the directory exists. On a fresh installation for example, the directory does not + // exist, so writing the plist file below will fail. + const auto plistDir = QFileInfo(QString::fromNSString(plistFile)).dir(); + if (!plistDir.exists()) { + if (!QDir().mkpath(plistDir.path())) { + return QString(QStringLiteral("Failed to create directory '%1'")).arg(plistDir.path()); + } + + // Permissions always seem to be 0700, so set that. + // Note: the Permission enum documentation states that on unix the owner permissions are + // returned, but: "This behavior might change in a future Qt version." So we play it safe, + // and set both the user and the owner permissions to rwx. + if (!QFile(plistDir.path()).setPermissions(QFileDevice::ReadOwner | QFileDevice::ReadUser | QFileDevice::WriteOwner | QFileDevice::WriteUser | QFileDevice::ExeOwner | QFileDevice::ExeUser)) { + qCInfo(lcUtility()) << "Failed to set directory permmissions for" << plistDir.path(); + } + } + + // Now write the file. + qCInfo(lcUtility()) << "Writing plist file" << QString::fromNSString(plistFile); // Especially for branded clients: log the file name, so it can be found when debugging. + if (![plist writeToURL:[NSURL fileURLWithPath:plistFile isDirectory:NO] error:&error]) { + return QString::fromNSString(error.localizedDescription); + } + + return {}; +} + +static Result readPlistFromFile(NSString *plistFile) +{ + NSError *error = nil; + if (NSDictionary *plist = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:plistFile isDirectory:NO] error:&error]) { + return plist; + } else { + return QString::fromNSString(error.localizedDescription); + } +} + +bool Utility::hasLaunchOnStartup(const QString &appName) +{ + Q_UNUSED(appName) + + @autoreleasepool { + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *appIdentifier = QCoreApplication::organizationDomain().toNSString(); + NSString *plistFile = [NSHomeDirectory() stringByAppendingFormat:@"/Library/LaunchAgents/%@.plist", appIdentifier]; + + if ([fileManager fileExistsAtPath:plistFile]) { + auto maybePlist = readPlistFromFile(plistFile); + if (!maybePlist) { + qCInfo(lcUtility()).nospace() << "Cannot read '" << QString::fromNSString(plistFile) + << "', probably not a valid plist file"; + return false; + } + + if (NSDictionary *plist = *maybePlist) { + // yes, there is a valid plist file... + if (id label = plist[@"Label"]) { + // ... with a label... + if ([appIdentifier isEqualToString:label]) { + // ... and yes, it's the correct app-id... + if (id program = plist[@"Program"]) { + // .. and there is a program mentioned ... + // (Note: case insensitive compare, because most fs setups on mac are case insensitive) + if ([QCoreApplication::applicationFilePath().toNSString() compare:program options:NSCaseInsensitiveSearch] == NSOrderedSame) { + // ... and it's our executable .. + if (NSNumber *value = plist[@"RunAtLoad"]) { + // yes, there is even a RunAtLoad key, so use it! + return [value boolValue]; + } + } + } + } + } + } + } + } + + return false; +} + +static Result writeNewPlistFile(NSString *plistFile, NSString *fullPath, bool enable) +{ + NSDictionary *plistTemplate = @{ + @"Label" : QCoreApplication::organizationDomain().toNSString(), + @"KeepAlive" : @NO, + @"Program" : fullPath, + @"RunAtLoad" : enable ? @YES : @NO + }; + + return writePlistToFile(plistFile, plistTemplate); +} + +static Result modifyPlist(NSString *plistFile, NSDictionary *plist, bool enable) +{ + if (NSNumber *value = plist[@"RunAtLoad"]) { + // ok, there is a key + if ([value boolValue] == enable) { + // nothing to do + return {}; + } + } + + // now either the key was missing, or it had the wrong value, so set the key and write the plist back + NSMutableDictionary *newPlist = [plist mutableCopy]; + newPlist[@"RunAtLoad"] = enable ? @YES : @NO; + return writePlistToFile(plistFile, newPlist); +} + +void Utility::setLaunchOnStartup(const QString &appName, const QString &guiName, bool enable) +{ + Q_UNUSED(appName) + Q_UNUSED(guiName) + + @autoreleasepool { + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *fullPath = QCoreApplication::applicationFilePath().toNSString(); + NSString *appIdentifier = QCoreApplication::organizationDomain().toNSString(); + NSString *plistFile = [NSHomeDirectory() stringByAppendingFormat:@"/Library/LaunchAgents/%@.plist", appIdentifier]; + + // An error might occur in the code below, but we cannot report anything, so we just ignore them. + + if ([fileManager fileExistsAtPath:plistFile]) { + auto maybePlist = readPlistFromFile(plistFile); + if (!maybePlist) { + // broken plist, overwrite it + auto result = writeNewPlistFile(plistFile, fullPath, enable); + if (!result) { + qCWarning(lcUtility) << Q_FUNC_INFO << result.error(); + } + return; + } + NSDictionary *plist = *maybePlist; + + id programValue = plist[@"Program"]; + if (programValue == nil) { + // broken plist, overwrite it + auto result = writeNewPlistFile(plistFile, fullPath, enable); + if (!result) { + qCWarning(lcUtility) << result.error(); + } + } else if (![fileManager fileExistsAtPath:programValue]) { + // Ok, a plist from some removed program, overwrite it + auto result = writeNewPlistFile(plistFile, fullPath, enable); + if (!result) { + qCWarning(lcUtility) << result.error(); + } + } else if ([fullPath compare:programValue options:NSCaseInsensitiveSearch] == NSOrderedSame) { // (Note: case insensitive compare, because most fs setups on mac are lse if (![fileManager fileExistscase insensitive) + // Wohoo, it's ours! Now carefully change only the RunAtLoad entry. If any value for + // e.g. KeepAlive was changed, we leave it as-is. + auto result = modifyPlist(plistFile, plist, enable); + if (!result) { + qCWarning(lcUtility) << result.error(); + } + } else if ([fullPath hasPrefix:@"/Applications/"]) { + // ok, we seem to be an officially installed application, overwrite the file + auto result = writeNewPlistFile(plistFile, fullPath, enable); + if (!result) { + qCWarning(lcUtility) << result.error(); + } + } else { + qCInfo(lcUtility) << "We're not an installed application, there is anoter executable " + "mentioned in the plist file, and that executable seems to exist, " + "so let's not touch the file."; + } + } else { + // plist doens't exist, write a new one. + auto result = writeNewPlistFile(plistFile, fullPath, enable); + if (!result) { + qCWarning(lcUtility) << result.error(); + } + } + } +} + +#ifndef TOKEN_AUTH_ONLY +bool Utility::hasDarkSystray() +{ + @autoreleasepool { + if (auto style = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]) { + return [style isEqualToString:@"Dark"]; + } + } + + return false; +} +#endif + +QString Utility::getCurrentUserName() +{ + return {}; +} + +void Utility::registerUriHandlerForLocalEditing() { /* URI handler is registered via MacOSXBundleInfo.plist.in */ } + +} // namespace OCC diff --git a/src/common/utility_unix.cpp b/src/common/utility_unix.cpp index 27e66dc73aad9..0b47ee78e13bc 100644 --- a/src/common/utility_unix.cpp +++ b/src/common/utility_unix.cpp @@ -17,13 +17,21 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "utility.h" +#include "config.h" + +#include +#include +#include #include #include #include +#include +#include namespace OCC { -static void setupFavLink_private(const QString &folder) +void Utility::setupFavLink(const QString &folder) { // Nautilus: add to ~/.gtk-bookmarks QFile gtkBookmarks(QDir::homePath() + QLatin1String("/.config/gtk-3.0/bookmarks")); @@ -38,36 +46,36 @@ static void setupFavLink_private(const QString &folder) } } -static void removeFavLink_private(const QString &folder) +void Utility::removeFavLink(const QString &folder) { Q_UNUSED(folder) } // returns the autostart directory the linux way // and respects the XDG_CONFIG_HOME env variable -QString getUserAutostartDir_private() +static QString getUserAutostartDir() { QString config = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); config += QLatin1String("/autostart/"); return config; } -bool hasLaunchOnStartup_private(const QString &appName) +bool Utility::hasSystemLaunchOnStartup(const QString &appName) { Q_UNUSED(appName) - QString desktopFileLocation = getUserAutostartDir_private() - + QLatin1String(LINUX_APPLICATION_ID) - + QLatin1String(".desktop"); + return false; +} + +bool Utility::hasLaunchOnStartup(const QString &appName) +{ + const QString desktopFileLocation = getUserAutostartDir() + appName + QLatin1String(".desktop"); return QFile::exists(desktopFileLocation); } -void setLaunchOnStartup_private(const QString &appName, const QString &guiName, bool enable) +void Utility::setLaunchOnStartup(const QString &appName, const QString &guiName, bool enable) { - Q_UNUSED(appName) - QString userAutoStartPath = getUserAutostartDir_private(); - QString desktopFileLocation = userAutoStartPath - + QLatin1String(LINUX_APPLICATION_ID) - + QLatin1String(".desktop"); + const auto userAutoStartPath = getUserAutostartDir(); + const QString desktopFileLocation = userAutoStartPath + appName + QLatin1String(".desktop"); if (enable) { if (!QDir().exists(userAutoStartPath) && !QDir().mkpath(userAutoStartPath)) { qCWarning(lcUtility) << "Could not create autostart folder" << userAutoStartPath; @@ -104,7 +112,7 @@ void setLaunchOnStartup_private(const QString &appName, const QString &guiName, } } -static inline bool hasDarkSystray_private() +bool Utility::hasDarkSystray() { return true; } diff --git a/src/common/utility_win.cpp b/src/common/utility_win.cpp index 37b348660c0de..1fcb4cda8747f 100644 --- a/src/common/utility_win.cpp +++ b/src/common/utility_win.cpp @@ -41,7 +41,7 @@ static const char runPathC[] = R"(HKEY_CURRENT_USER\Software\Microsoft\Windows\C namespace OCC { -static void setupFavLink_private(const QString &folder) +void Utility::setupFavLink(const QString &folder) { // First create a Desktop.ini so that the folder and favorite link show our application's icon. QFile desktopIni(folder + QLatin1String("/Desktop.ini")); @@ -86,7 +86,7 @@ static void setupFavLink_private(const QString &folder) qCWarning(lcUtility) << "linking" << folder << "to" << linkName << "failed!"; } -static void removeFavLink_private(const QString &folder) +void Utility::removeFavLink(const QString &folder) { const QDir folderDir(folder); @@ -120,21 +120,21 @@ static void removeFavLink_private(const QString &folder) } } -bool hasSystemLaunchOnStartup_private(const QString &appName) +bool Utility::hasSystemLaunchOnStartup(const QString &appName) { QString runPath = QLatin1String(systemRunPathC); QSettings settings(runPath, QSettings::NativeFormat); return settings.contains(appName); } -bool hasLaunchOnStartup_private(const QString &appName) +bool Utility::hasLaunchOnStartup(const QString &appName) { QString runPath = QLatin1String(runPathC); QSettings settings(runPath, QSettings::NativeFormat); return settings.contains(appName); } -void setLaunchOnStartup_private(const QString &appName, const QString &guiName, bool enable) +void Utility::setLaunchOnStartup(const QString &appName, const QString &guiName, bool enable) { Q_UNUSED(guiName); QString runPath = QLatin1String(runPathC); @@ -146,8 +146,7 @@ void setLaunchOnStartup_private(const QString &appName, const QString &guiName, } } -// TODO: Right now only detection on toggle/startup, not when windows theme is switched while nextcloud is running -static inline bool hasDarkSystray_private() +bool Utility::hasDarkSystray() { if(Utility::registryGetKeyValue( HKEY_CURRENT_USER, QStringLiteral(R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)"), diff --git a/src/gui/application.cpp b/src/gui/application.cpp index a692cb6b7903d..c318868907887 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -158,6 +158,11 @@ bool Application::configVersionMigration() return true; } + // 'Launch on system startup' defaults to true > 3.11.x + const auto theme = Theme::instance(); + configFile.setLaunchOnSystemStartup(configFile.launchOnSystemStartup()); + Utility::setLaunchOnStartup(theme->appName(), theme->appNameGUI(), configFile.launchOnSystemStartup()); + // back up all old config files QStringList backupFilesList; QDir configDir(configFile.configPath()); @@ -639,20 +644,7 @@ void Application::slotownCloudWizardDone(int res) _checkConnectionTimer.start(); slotCheckConnection(); - // If one account is configured: enable autostart -#ifndef QT_DEBUG - bool shouldSetAutoStart = AccountManager::instance()->accounts().size() == 1; -#else - bool shouldSetAutoStart = false; -#endif -#ifdef Q_OS_MAC - // Don't auto start when not being 'installed' - shouldSetAutoStart = shouldSetAutoStart - && QCoreApplication::applicationDirPath().startsWith("/Applications/"); -#endif - if (shouldSetAutoStart) { - Utility::setLaunchOnStartup(_theme->appName(), _theme->appNameGUI(), true); - } + Utility::setLaunchOnStartup(_theme->appName(), _theme->appNameGUI(), true); Systray::instance()->showWindow(); } diff --git a/src/gui/generalsettings.cpp b/src/gui/generalsettings.cpp index 394d13c0d9772..ecb051eab9a94 100644 --- a/src/gui/generalsettings.cpp +++ b/src/gui/generalsettings.cpp @@ -156,16 +156,13 @@ GeneralSettings::GeneralSettings(QWidget *parent) _ui->showInExplorerNavigationPaneCheckBox->setText(txt); #endif - if(Utility::hasSystemLaunchOnStartup(Theme::instance()->appName())) { - _ui->autostartCheckBox->setChecked(true); - _ui->autostartCheckBox->setDisabled(true); + if(const auto hasSystemAutoStart = Utility::hasSystemLaunchOnStartup(Theme::instance()->appName())) { + _ui->autostartCheckBox->setChecked(hasSystemAutoStart); + _ui->autostartCheckBox->setDisabled(hasSystemAutoStart); _ui->autostartCheckBox->setToolTip(tr("You cannot disable autostart because system-wide autostart is enabled.")); - } else { - const bool hasAutoStart = Utility::hasLaunchOnStartup(Theme::instance()->appName()); - // make sure the binary location is correctly set - slotToggleLaunchOnStartup(hasAutoStart); - _ui->autostartCheckBox->setChecked(hasAutoStart); + } else { connect(_ui->autostartCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotToggleLaunchOnStartup); + _ui->autostartCheckBox->setChecked(ConfigFile().launchOnSystemStartup()); } // setup about section @@ -454,7 +451,13 @@ void GeneralSettings::saveMiscSettings() void GeneralSettings::slotToggleLaunchOnStartup(bool enable) { - Theme *theme = Theme::instance(); + const auto theme = Theme::instance(); + if (enable == Utility::hasLaunchOnStartup(theme->appName())) { + return; + } + + ConfigFile configFile; + configFile.setLaunchOnSystemStartup(enable); Utility::setLaunchOnStartup(theme->appName(), theme->appNameGUI(), enable); } diff --git a/src/libsync/configfile.cpp b/src/libsync/configfile.cpp index 60cb2cc71aeba..7a568f2c00499 100644 --- a/src/libsync/configfile.cpp +++ b/src/libsync/configfile.cpp @@ -84,6 +84,7 @@ static constexpr char logExpireC[] = "logExpire"; static constexpr char logFlushC[] = "logFlush"; static constexpr char showExperimentalOptionsC[] = "showExperimentalOptions"; static constexpr char clientVersionC[] = "clientVersion"; +static constexpr char launchOnSystemStartupC[] = "launchOnSystemStartup"; static constexpr char proxyHostC[] = "Proxy/host"; static constexpr char proxyTypeC[] = "Proxy/type"; @@ -1140,6 +1141,18 @@ void ConfigFile::setClientVersionString(const QString &version) settings.setValue(QLatin1String(clientVersionC), version); } +bool ConfigFile::launchOnSystemStartup() const +{ + QSettings settings(configFile(), QSettings::IniFormat); + return settings.value(QLatin1String(launchOnSystemStartupC), true).toBool(); +} + +void ConfigFile::setLaunchOnSystemStartup(const bool autostart) +{ + QSettings settings(configFile(), QSettings::IniFormat); + settings.setValue(QLatin1String(launchOnSystemStartupC), autostart); +} + Q_GLOBAL_STATIC(QString, g_configFileName) std::unique_ptr ConfigFile::settingsWithGroup(const QString &group, QObject *parent) diff --git a/src/libsync/configfile.h b/src/libsync/configfile.h index 21e58412afcc5..bd99885dfa107 100644 --- a/src/libsync/configfile.h +++ b/src/libsync/configfile.h @@ -216,6 +216,11 @@ class OWNCLOUDSYNC_EXPORT ConfigFile [[nodiscard]] QString clientVersionString() const; void setClientVersionString(const QString &version); + /** If the option 'Launch on system startup' is set + Updated by configVersionMigration() at client startup. */ + [[nodiscard]] bool launchOnSystemStartup() const; + void setLaunchOnSystemStartup(const bool autostart); + /** Returns a new settings pre-set in a specific group. The Settings will be created with the given parent. If no parent is specified, the caller must destroy the settings */ static std::unique_ptr settingsWithGroup(const QString &group, QObject *parent = nullptr);