[KDE/Mac] KDE/kdelibs/kdecore/localization [POSSIBLY UNSAFE]

John Layt john at layt.net
Mon Aug 23 00:54:02 CEST 2010


SVN commit 1166797 by jlayt:

Try to choose a default locale Country if the user hasn't set one yet.

See http://reviewboard.kde.org/r/4915/

Under Linux this will use QLocale to try obtain a default country.

Under Windows and Mac this will fake locale support by loading the KDE settings
for the system country, proper support using real host settings will follow
later.

Fix my stupidity with not understanding how virtual call work in constructors.

Add validation checking to country code values, and improve documentation.

Compiled and tested under Linux and Windows, I am unable to build a Mac test
environment atm, please patch or beat me up if it fails under Mac.

CCMAIL: kde-windows at kde.org
CCMAIL: kde-mac at kde.org
CCBUG: 176650



 M  +7 -3      klocale.cpp  
 M  +14 -2     klocale.h  
 M  +84 -26    klocale_kde.cpp   [POSSIBLY UNSAFE: system]
 M  +15 -0     klocale_mac.cpp  
 M  +13 -0     klocale_mac_p.h  
 M  +31 -1     klocale_p.h   [POSSIBLY UNSAFE: system]
 M  +23 -2     klocale_win.cpp  
 M  +12 -0     klocale_win_p.h  


--- trunk/KDE/kdelibs/kdecore/localization/klocale.cpp #1166796:1166797
@@ -23,9 +23,13 @@
 */
 
 #include "klocale.h"
-#include "klocale_p.h"
+#if defined Q_WS_WIN
 #include "klocale_win_p.h"
+#elif defined Q_OS_MAC
 #include "klocale_mac_p.h"
+#else
+    #include "klocale_unix_p.h"
+#endif
 
 #include <QtCore/QDateTime>
 #include <QtCore/QTextCodec>
@@ -46,7 +50,7 @@
 #elif defined Q_OS_MAC
         : d(new KLocaleMacPrivate(this, catalog, config.data()))
 #else
-        : d(new KLocalePrivate(this, catalog, config.data()))
+        : d(new KLocaleUnixPrivate(this, catalog, config.data()))
 #endif
 {
 }
@@ -57,7 +61,7 @@
 #elif defined Q_OS_MAC
         : d(new KLocaleMacPrivate(this, catalog, config, language, country))
 #else
-        : d(new KLocalePrivate(this, catalog, config, language, country))
+        : d(new KLocaleUnixPrivate(this, catalog, config, language, country))
 #endif
 {
 }
--- trunk/KDE/kdelibs/kdecore/localization/klocale.h #1166796:1166797
@@ -1130,8 +1130,15 @@
 
     /**
      * Returns the country code of the country where the user lives.
-     * defaultCountry() is returned by default, if no other available.
      *
+     * The returned code complies with the ISO 3166-1 alpha-2 standard,
+     * except by KDE convention it is returned in lowercase whereas the
+     * official standard is uppercase.
+     * See http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 for details.
+     *
+     * defaultCountry() is returned by default, if no other available,
+     * this will always be uppercase 'C'.
+     *
      * Use countryCodeToName(country) to get human readable, localized
      * country names.
      *
@@ -1667,8 +1674,13 @@
     static QString defaultLanguage();
 
     /**
-     * Returns the name of the default country.
+     * Returns the code of the default country, i.e. "C"
      *
+     * This function will not provide a sensible value to use in your app,
+     * please use country() instead.
+     *
+     * @see country
+     *
      * @return Name of the default country
      */
     static QString defaultCountry();
--- trunk/KDE/kdelibs/kdecore/localization/klocale_kde.cpp #1166796:1166797
@@ -100,7 +100,8 @@
     return debug << cn.name << cn.loadCount;
 }
 
-KLocalePrivate::KLocalePrivate(KLocale *q_ptr, const QString &catalog, KConfig *config, const QString &language, const QString &country)
+KLocalePrivate::KLocalePrivate(KLocale *q_ptr, const QString &catalog, KConfig *config,
+                               const QString &language, const QString &country )
                : q(q_ptr),
                  m_country(country),
                  m_language(language),
@@ -110,21 +111,8 @@
                  m_currency(0),
                  m_codecForEncoding(0)
 {
-    initEncoding();
-    initFileNameEncoding();
-
-    if (config) {
-        initLanguageList(config, false);
-    } else {
-        config = KGlobal::config().data();
-        initLanguageList(config, true);
     }
 
-    initMainCatalogs();
-
-    initFormat(config);
-}
-
 KLocalePrivate::KLocalePrivate(const KLocalePrivate &rhs)
 {
     copy(rhs);
@@ -216,6 +204,29 @@
     delete m_languages;
 }
 
+void KLocalePrivate::init( KConfig *config ) {
+    initEncoding();
+    initFileNameEncoding();
+
+    bool useEnv = false;
+    if ( !config ) {
+        useEnv = true;
+        config = KGlobal::config().data();
+    }
+
+    KConfigGroup localeSettings( config, "Locale" );
+
+    if ( m_country.isEmpty() ) {
+        initCountry( localeSettings );
+    }
+
+    initLanguageList( localeSettings, useEnv );
+
+    initMainCatalogs();
+
+    initFormat( config );
+}
+
 void KLocalePrivate::initMainCatalogs()
 {
     KLocaleStaticData *s = staticData;
@@ -278,20 +289,57 @@
     }
 }
 
-void KLocalePrivate::initLanguageList(KConfig *config, bool useEnv)
+void KLocalePrivate::initCountry( KConfigGroup localeSettings )
 {
-    KConfigGroup cg(config, "Locale");
+    // First check if the constructor passed in a country and if that is valid
+    QString putativeCountry = m_country;
+    // Cache the valid countries list to save re-reading from disk
+    QStringList validCountries = allCountriesList();
+    // Add the default C as it is valid to use but is not in the list
+    validCountries.append( defaultCountry() );
 
-    // Set the country as specified by the KDE config or use default,
-    // do not consider environment variables.
-    if (m_country.isEmpty()) {
-        m_country = cg.readEntry("Country");
+    // If the constructor country is not valid
+    if ( putativeCountry.isEmpty() || !validCountries.contains( putativeCountry, Qt::CaseInsensitive ) ) {
+
+        // Default to country as set in config (in priorty order as sorted out by KConfig):
+        // * Application level setting, i.e. from the kapprc
+        // * User level setting, i.e. from kdeglobals
+        // * Any group policy setting
+        putativeCountry = localeSettings.readEntry( "Country" );
+
+        // If the config country is not valid
+        if ( putativeCountry.isEmpty() || !validCountries.contains( putativeCountry, Qt::CaseInsensitive ) ) {
+
+            // Try obtain a sensible default based on current host system settings
+            putativeCountry = systemCountry();
+
+            if ( putativeCountry.isEmpty() || !validCountries.contains( putativeCountry, Qt::CaseInsensitive ) ) {
+                // Only if no other option, resort to the default C
+                putativeCountry = defaultCountry();
     }
+        }
+    }
 
-    if (m_country.isEmpty()) {
-        m_country = KLocalePrivate::defaultCountry();
+    // Always save as lowercase, unless it's C when we want it uppercase
+    if ( putativeCountry.toLower() == defaultCountry().toLower() ) {
+        m_country = defaultCountry();
+    } else {
+        m_country = putativeCountry.toLower();
     }
+}
 
+
+QString KLocalePrivate::systemCountry() const
+{
+    // Use QLocale for now as it supposedly provides a sensible default most times,
+    // e.g. if locale is only "de" it is assumed to mean country of "DE"
+    QString systemCountry, s1, s2, s3;
+    splitLocale( QLocale::system().name(), s1, systemCountry, s2, s3 );
+    return systemCountry.toLower();
+}
+
+void KLocalePrivate::initLanguageList(KConfigGroup localeSettings, bool useEnv)
+{
     // Collect possible languages by decreasing priority.
     // The priority is as follows:
     // - the internally set language, if any
@@ -309,7 +357,7 @@
         getLanguagesFromVariable(list, "KDE_LANG", true);
     }
 
-    QString languages(cg.readEntry("Language", QString()));
+    QString languages(localeSettings.readEntry("Language", QString()));
     if (!languages.isEmpty()) {
         list += languages.split(':');
     }
@@ -447,11 +495,21 @@
 
 bool KLocalePrivate::setCountry(const QString &country, KConfig *config)
 {
-    // Check if the file exists too??
-    if (country.isEmpty()) {
+    QStringList validCountries = allCountriesList();
+    // Add the default C as it is valid to use but is not in the list
+    validCountries.append( defaultCountry() );
+
+    if ( country.isEmpty() || !validCountries.contains( country, Qt::CaseInsensitive ) ) {
         return false;
     }
-    m_country = country;
+
+    // Always save as lowercase, unless it's C when we want it uppercase
+    if ( country.toLower() == defaultCountry().toLower() ) {
+        m_country = defaultCountry();
+    } else {
+        m_country = country.toLower();
+    }
+
     initFormat(config);
     return true;
 }
--- trunk/KDE/kdelibs/kdecore/localization/klocale_mac.cpp #1166796:1166797
@@ -23,17 +23,22 @@
                                       const QString &language, const QString &country )
                   :KLocalePrivate( q_ptr, catalog, config, language, country )
 {
+    // Lock in the current Mac Locale settings
+    m_macLocale = CFLocaleCopyCurrent();
+    init( config );
 }
 
 KLocaleMacPrivate::KLocaleMacPrivate( const KLocaleMacPrivate &rhs )
                   :KLocalePrivate( rhs )
 {
     KLocalePrivate::copy( rhs );
+    m_macLocale = rhs.m_macLocale;
 }
 
 KLocaleMacPrivate &KLocaleMacPrivate::operator=( const KLocaleMacPrivate &rhs )
 {
     KLocalePrivate::copy( rhs );
+    m_macLocale = rhs.m_macLocale;
     return *this;
 }
 
@@ -41,6 +46,16 @@
 {
 }
 
+QString KLocaleMacPrivate::macLocaleValue( CFStringRef key ) const
+{
+    return QCFString::toQString( CFStringRef( CFLocaleGetValue( m_macLocale, key ) ) );
+}
+
+QString KLocaleMacPrivate::systemCountry() const
+{
+    return macLocaleValue( kCFLocaleCountryCode );
+}
+
 QByteArray KLocaleMacPrivate::systemCodeset() const
 {
     return QByteArray( "UTF-8" );
--- trunk/KDE/kdelibs/kdecore/localization/klocale_mac_p.h #1166796:1166797
@@ -22,6 +22,8 @@
 
 #include "klocale_p.h"
 
+#include <CoreFoundation/CoreFoundation.h>
+
 class KLocaleMacPrivate : public KLocalePrivate
 {
 public:
@@ -34,12 +36,23 @@
 
     virtual ~KLocaleMacPrivate();
 
+protected:
+
+    /**************************
+     **   Country settings   **
+     **************************/
+
+    virtual QString systemCountry() const;
+
     /***************************
      **   Encoding settings   **
      ***************************/
 
     virtual QByteArray systemCodeset() const;
 
+private:
+    QString macLocaleValue( CFStringRef key ) const;
+    CFLocaleRef m_macLocale;
 };
 
 #endif // KLOCALE_MAC_P_H
--- trunk/KDE/kdelibs/kdecore/localization/klocale_p.h #1166796:1166797
@@ -33,6 +33,14 @@
 class KLocalePrivate
 {
 public:
+    /**
+     * Constructor
+     *
+     * This class should not be instantited directly, it is intended as a base class for each
+     * platform to provide a common KDE fallback implemenation.  Instead use the relevent
+     * derived system class for Unix, Win, or Mac which will prefer the local platform settings
+     * where possible.
+     */
     KLocalePrivate(KLocale *q, const QString &catalog, KConfig *config,
                    const QString &language = QString(), const QString &country = QString());
 
@@ -67,10 +75,32 @@
      */
     virtual void initFormat(KConfig *config);
 
+    /**
+     * @internal Main init function, needs to be called by appropriate child constructor.
+     */
+    void init( KConfig *config );
+
     /**************************
      **   Country settings   **
      **************************/
 
+protected:
+
+    /**
+     * @internal Initializes the country if not already explicity set when calling the constructor
+     * Will default to any value set in the config, otherwise will attempt to use the host system
+     * country, or finally fall back to the default C.
+     *
+     * @param config The configuration object used for init
+     */
+    virtual void initCountry( KConfigGroup localeSettings );
+
+    /**
+     * @internal Returns the host system country ISO code
+     * If country could not be determined then may return an empty string or "C"
+     */
+    virtual QString systemCountry() const;
+
 public:
 
     /**
@@ -127,7 +157,7 @@
      * @param config The configuration object used for init
      * @param useEnv True if we should use environment variables
      */
-    virtual void initLanguageList(KConfig *config, bool useEnv);
+    virtual void initLanguageList(KConfigGroup localeSettings, bool useEnv);
 
     /**
      * @internal function used to determine if we are using the en_US translation
--- trunk/KDE/kdelibs/kdecore/localization/klocale_win.cpp #1166796:1166797
@@ -20,8 +20,6 @@
 
 #include "klocale_win_p.h"
 
-#include <windows.h>
-
 #include <QtCore/QLocale>
 #include <QtCore/QTextCodec>
 
@@ -29,18 +27,24 @@
                                               const QString &language, const QString &country )
                       :KLocalePrivate( q_ptr, catalog, config, language, country )
 {
+    // Lock in the current Windows Locale ID
+    // Can we also lock in the actual settings like we do for Mac?
+    m_winLocaleId = GetUserDefaultLCID();
+    init( config );
 }
 
 KLocaleWindowsPrivate::KLocaleWindowsPrivate( const KLocaleWindowsPrivate &rhs )
                       :KLocalePrivate( rhs )
 {
     KLocalePrivate::copy( rhs );
+    m_winLocaleId = rhs.m_winLocaleId;
     strcpy( m_win32SystemEncoding, rhs.m_win32SystemEncoding );
 }
 
 KLocaleWindowsPrivate &KLocaleWindowsPrivate::operator=( const KLocaleWindowsPrivate &rhs )
 {
     KLocalePrivate::copy( rhs );
+    m_winLocaleId = rhs.m_winLocaleId;
     strcpy( m_win32SystemEncoding, rhs.m_win32SystemEncoding );
     return *this;
 }
@@ -49,6 +53,23 @@
 {
 }
 
+QString KLocaleWindowsPrivate::windowsLocaleValue( LCTYPE key ) const
+{
+    // Find out how big the buffer needs to be
+    int size = GetLocaleInfo( m_winLocaleId, key, 0, 0 );
+    wchar_t buffer[size];
+    if ( GetLocaleInfo( m_winLocaleId, key, buffer, size ) ) {
+        return QString::fromWCharArray( buffer );
+    } else {
+        return QString();
+    }
+}
+
+QString KLocaleWindowsPrivate::systemCountry() const
+{
+    return windowsLocaleValue( LOCALE_SISO3166CTRYNAME );
+}
+
 QStringList KLocaleWindowsPrivate::systemLanguageList() const
 {
     QStringList list;
--- trunk/KDE/kdelibs/kdecore/localization/klocale_win_p.h #1166796:1166797
@@ -22,6 +22,8 @@
 
 #include "klocale_p.h"
 
+#include <windows.h>
+
 class KLocaleWindowsPrivate : public KLocalePrivate
 {
 public:
@@ -34,7 +36,15 @@
 
     virtual ~KLocaleWindowsPrivate();
 
+protected:
+
     /**************************
+     **   Country settings   **
+     **************************/
+
+    virtual QString systemCountry() const;
+
+    /**************************
      **  Language settings   **
      **************************/
 
@@ -49,6 +59,8 @@
     virtual const QByteArray encoding();
 
 private:
+    QString windowsLocaleValue( LCTYPE key ) const;
+    LCID m_winLocaleId;
     // Encoding settings
     char m_win32SystemEncoding[3+7]; //"cp " + lang ID
 };


More information about the kde-mac mailing list