Initial QSfera import
This commit is contained in:
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
|
||||
*
|
||||
* 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 <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
#include <QtMacExtras/QtMacExtras>
|
||||
#endif
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#import <Foundation/NSFileManager.h>
|
||||
#import <Foundation/NSUserDefaults.h>
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static Result<void, QString> 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.
|
||||
QDir 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<NSDictionary *, QString> 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<void, QString> 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<void, QString> 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 case 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 {
|
||||
if (enable) {
|
||||
// plist doens't exist, write a new one.
|
||||
auto result = writeNewPlistFile(plistFile, fullPath, enable);
|
||||
if (!result) {
|
||||
qCWarning(lcUtility) << result.error();
|
||||
}
|
||||
} else {
|
||||
// Do nothing: if the file doesn't exist, the client won't be auto-started.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef TOKEN_AUTH_ONLY
|
||||
bool Utility::hasDarkSystray()
|
||||
{
|
||||
@autoreleasepool {
|
||||
if (auto style = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]) {
|
||||
return [style isEqualToString:@"Dark"];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
QString Utility::currentCpuArch()
|
||||
{
|
||||
static const QString rv = []() {
|
||||
int procTranslated = 0;
|
||||
size_t size = sizeof(procTranslated);
|
||||
|
||||
const auto rv = sysctlbyname("sysctl.proc_translated", &procTranslated, &size, nullptr, 0);
|
||||
|
||||
// should be backed up if not evaluated immediately
|
||||
const auto error = errno;
|
||||
|
||||
qCDebug(lcUtility) << "proc_translated:" << procTranslated;
|
||||
qCDebug(lcUtility) << "error:" << strerror(error);
|
||||
|
||||
if (rv != -1) {
|
||||
if (procTranslated == 1) {
|
||||
qCInfo(lcUtility) << "process translated with rosetta2";
|
||||
return QStringLiteral("arm64");
|
||||
} else {
|
||||
qCInfo(lcUtility) << "process not translated";
|
||||
}
|
||||
} else {
|
||||
if (error == ENOENT) {
|
||||
qCDebug(lcUtility) << "not running on Apple Silicon";
|
||||
} else {
|
||||
qCDebug(lcUtility) << "unknown error trying to detect whether proc is translated";
|
||||
}
|
||||
}
|
||||
|
||||
const auto qtArch = QSysInfo::currentCpuArchitecture();
|
||||
qCDebug(lcUtility) << "CPU arch reported by Qt:" << qtArch;
|
||||
return qtArch;
|
||||
}();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
Reference in New Issue
Block a user