From 41ba5997bba81e225dbc449bb60ac86d88ed7fe3 Mon Sep 17 00:00:00 2001 From: Pasi Matilainen Date: Fri, 2 Mar 2012 15:06:08 +0200 Subject: Enable storage of global Qt settings in app-local settings file on Mac MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Mac App Store has limitations on where applications can write their settings, and com.trolltech.plist is not allowed. Changed the settings code to store all settings in the app-local file when the application runs in sandbox, or when the application's Info.plist contains a key "ForAppStore" with value "yes". The application's bundle identifier is also used for naming the settings file in these cases. Task-number: QTBUG-16549 Change-Id: Idd2241fbd7eb346da987226f05460642b0d6e5a3 Reviewed-by: Morten Johan Sørvig --- src/corelib/corelib.pro | 4 +- src/corelib/io/qsettings_mac.cpp | 82 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/corelib/corelib.pro b/src/corelib/corelib.pro index 9673861..d7306e0 100644 --- a/src/corelib/corelib.pro +++ b/src/corelib/corelib.pro @@ -22,10 +22,10 @@ include(xml/xml.pri) !qpa:mac|darwin:LIBS_PRIVATE += -framework ApplicationServices qpa { contains(QT_CONFIG, coreservices) { - LIBS_PRIVATE += -framework CoreServices + LIBS_PRIVATE += -framework CoreServices -framework Security } } else:mac|darwin { - LIBS_PRIVATE += -framework CoreFoundation + LIBS_PRIVATE += -framework CoreFoundation -framework Security } mac:lib_bundle:DEFINES += QT_NO_DEBUG_PLUGIN_CHECK win32:DEFINES-=QT_NO_CAST_TO_ASCII diff --git a/src/corelib/io/qsettings_mac.cpp b/src/corelib/io/qsettings_mac.cpp index 6d0af2a..3e3189e 100644 --- a/src/corelib/io/qsettings_mac.cpp +++ b/src/corelib/io/qsettings_mac.cpp @@ -45,6 +45,13 @@ #include "qdir.h" #include "qvarlengtharray.h" #include "private/qcore_mac_p.h" +#ifndef QT_BOOTSTRAPPED +#include "qcoreapplication.h" +#endif +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +#include +#include +#endif QT_BEGIN_NAMESPACE @@ -343,6 +350,7 @@ class QMacSettingsPrivate : public QSettingsPrivate public: QMacSettingsPrivate(QSettings::Scope scope, const QString &organization, const QString &application); + QMacSettingsPrivate(CFStringRef bundleIdentifier); ~QMacSettingsPrivate(); void remove(const QString &key); @@ -423,6 +431,22 @@ QMacSettingsPrivate::QMacSettingsPrivate(QSettings::Scope scope, const QString & sync(); } +QMacSettingsPrivate::QMacSettingsPrivate(CFStringRef bundleIdentifier) + : QSettingsPrivate(QSettings::NativeFormat, QSettings::UserScope, QString(), QString()) +{ + // applicationId and suiteId are QCFStrings and take ownership, retain to prevent double deletes. + CFRetain(bundleIdentifier); + applicationId = bundleIdentifier; + CFRetain(bundleIdentifier); + suiteId = bundleIdentifier; + + numDomains = 1; + domains[0].userName = kCFPreferencesCurrentUser; + domains[0].applicationOrSuiteId = bundleIdentifier; + hostName = kCFPreferencesAnyHost; + sync(); +} + QMacSettingsPrivate::~QMacSettingsPrivate() { } @@ -579,6 +603,64 @@ QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, const QString &organization, const QString &application) { +#ifndef QT_BOOTSTRAPPED + static bool useAppLocalStorage = false; + static bool initialized = false; + + if (!initialized) { + bool inSandbox = false; + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + // If we are running on at least 10.7.0 and have the com.apple.security.app-sandbox + // entitlement, we are in a sandbox + SInt32 version = 0; + Gestalt(gestaltSystemVersion, &version); + SecCodeRef secCodeSelf; + if (version >= 0x1070 && SecCodeCopySelf(kSecCSDefaultFlags, &secCodeSelf) == errSecSuccess) { + SecRequirementRef sandboxReq; + CFStringRef entitlement = CFSTR("entitlement [\"com.apple.security.app-sandbox\"]"); + if (SecRequirementCreateWithString(entitlement, kSecCSDefaultFlags, &sandboxReq) == errSecSuccess) { + if (SecCodeCheckValidity(secCodeSelf, kSecCSDefaultFlags, sandboxReq) == errSecSuccess) + inSandbox = true; + CFRelease(sandboxReq); + } + CFRelease(secCodeSelf); + } +#endif + + bool forAppStore = false; + if (!inSandbox) { + CFTypeRef val = CFBundleGetValueForInfoDictionaryKey(CFBundleGetMainBundle(), CFSTR("ForAppStore")); + forAppStore = (val && + CFGetTypeID(val) == CFStringGetTypeID() && + CFStringCompare(CFStringRef(val), CFSTR("yes"), kCFCompareCaseInsensitive) == 0); + } + + useAppLocalStorage = inSandbox || forAppStore; + initialized = true; + } + + if (useAppLocalStorage) { + // Ensure that the global and app-local settings go to the same file, since that's + // what we really want + if (organization == QLatin1String("Trolltech") || + organization.isEmpty() || + (organization == qApp->organizationDomain() && application == qApp->applicationName()) || + (organization == qApp->organizationName()) && application == qApp->applicationName()) + { + CFStringRef bundleIdentifier = CFBundleGetIdentifier(CFBundleGetMainBundle()); + if (!bundleIdentifier) { + qWarning("QSettingsPrivate::create: You must set the bundle identifier when using ForAppStore"); + } else { + QSettingsPrivate* settings = new QMacSettingsPrivate(bundleIdentifier); + if (organization == QLatin1String("Trolltech")) + settings->beginGroupOrArray(QSettingsGroup("QtLibrarySettings")); + return settings; + } + } + } +#endif + if (format == QSettings::NativeFormat) { return new QMacSettingsPrivate(scope, organization, application); } else { -- cgit v0.12