OSDN Git Service

simplify ktimezoned and its KCM helper
authorIvailo Monev <xakepa10@gmail.com>
Sat, 6 Dec 2014 01:58:16 +0000 (03:58 +0200)
committerIvailo Monev <xakepa10@gmail.com>
Sat, 6 Dec 2014 01:58:16 +0000 (03:58 +0200)
kcontrol/dateandtime/helper.cpp
ktimezoned/ktimezoned.cpp
ktimezoned/ktimezoned.h

index 21fc51a..c257194 100644 (file)
@@ -39,6 +39,7 @@
 #include <kstandarddirs.h>
 #include <kprocess.h>
 #include <QFile>
+#include <QDir>
 #include <QDebug>
 
 #if defined(USE_SOLARIS)
@@ -119,7 +120,6 @@ int ClockHelper::date( const QString& newdate, const QString& olddate )
     return 0;
 }
 
-// on non-Solaris systems which do not use /etc/timezone?
 int ClockHelper::tz( const QString& selectedzone )
 {
     int ret = 0;
@@ -129,92 +129,31 @@ int ClockHelper::tz( const QString& selectedzone )
     if (!QRegExp("[a-zA-Z0-9-_+/]*").exactMatch(selectedzone)) {
         return ret;
     }
-
-#if defined(USE_SOLARIS)       // MARCO
-
-        KTemporaryFile tf;
-        tf.setPrefix("kde-tzone");
-        tf.open();
-        QTextStream ts(&tf);
-
-        QFile fTimezoneFile(INITFILE);
-        bool updatedFile = false;
-
-        if (fTimezoneFile.open(QIODevice::ReadOnly))
-        {
-            bool found = false;
-
-            QTextStream is(&fTimezoneFile);
-
-            for (QString line = is.readLine(); !line.isNull();
-                 line = is.readLine())
-            {
-                if (line.find("TZ=") == 0)
-                {
-                    ts << "TZ=" << selectedzone << endl;
-                    found = true;
-                }
-                else
-                {
-                    ts << line << endl;
-                }
-            }
-
-            if (!found)
-            {
-                ts << "TZ=" << selectedzone << endl;
-            }
-
-            updatedFile = true;
-            fTimezoneFile.close();
-        }
-
-        if (updatedFile)
-        {
-            ts.device()->reset();
-            fTimezoneFile.remove();
-
-            if (fTimezoneFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
-            {
-                QTextStream os(&fTimezoneFile);
-
-                for (QString line = ts->readLine(); !line.isNull();
-                     line = ts->readLine())
-                {
-                    os << line << endl;
-                }
-
-                fchmod(fTimezoneFile.handle(),
-                       S_IXUSR | S_IRUSR | S_IRGRP | S_IXGRP |
-                       S_IROTH | S_IXOTH);
-                fTimezoneFile.close();
-            }
+        // from ktimezoned
+        QString ZONE_INFO_DIR;
+
+        if (QDir("/usr/share/zoneinfo").exists()) {
+            ZONE_INFO_DIR = "/usr/share/zoneinfo/";
+        } else if (QDir("/usr/lib/zoneinfo").exists()) {
+            ZONE_INFO_DIR = "/usr/lib/zoneinfo/";
+        } else if (QDir("/share/zoneinfo").exists()) {
+            ZONE_INFO_DIR = "/share/zoneinfo/";
+        } else if (QDir("/lib/zoneinfo").exists()) {
+            ZONE_INFO_DIR = "/lib/zoneinfo/";
+        } else {
+            // /usr is kind of standard
+            ZONE_INFO_DIR = "/usr/share/zoneinfo/";
         }
+        QString tz = ZONE_INFO_DIR + selectedzone;
 
-
-        QString val = selectedzone;
-#else
-        QString tz = "/usr/share/zoneinfo/" + selectedzone;
-
-        QString zic = KStandardDirs::findExe("zic", exePath);
-        if (!zic.isEmpty()) {
-            KProcess::execute(zic, QStringList() << "-l" << selectedzone);
-        } else if (!QFile::remove("/etc/localtime")) {
-          ret |= TimezoneError;
-        } else if (!QFile::copy(tz, "/etc/localtime")) {
+        if (!QFile::remove("/etc/localtime")) {
           ret |= TimezoneError;
         }
-
-        QFile fTimezoneFile("/etc/timezone");
-
-        if (fTimezoneFile.exists() && fTimezoneFile.open(QIODevice::WriteOnly | QIODevice::Truncate) ) {
-            QTextStream t(&fTimezoneFile);
-            t << selectedzone;
-            fTimezoneFile.close();
+        if (!QFile::link(tz, "/etc/localtime")) {
+          ret |= TimezoneError;
         }
 
         QString val = ':' + tz;
-#endif // !USE_SOLARIS
 
         setenv("TZ", val.toAscii(), 1);
         tzset();
@@ -224,13 +163,10 @@ int ClockHelper::tz( const QString& selectedzone )
 
 int ClockHelper::tzreset()
 {
-#if !defined(USE_SOLARIS) // Do not update the System!
-        unlink( "/etc/timezone" );
-        unlink( "/etc/localtime" );
-
-        setenv("TZ", "", 1);
-        tzset();
-#endif // !USE SOLARIS
+//     unlink( "/etc/localtime" );
+// 
+//     setenv("TZ", "", 1);
+//     tzset();
     return 0;
 }
 
index 5ae22ed..c435762 100644 (file)
@@ -103,39 +103,12 @@ void KTimeZoned::init(bool restart)
     if (mZoneinfoDir.length() > 1 && mZoneinfoDir.endsWith('/'))
         mZoneinfoDir.truncate(mZoneinfoDir.length() - 1);   // strip trailing '/'
 
-    // For Unix, read zone.tab.
-
-    QString oldZoneinfoDir = mZoneinfoDir;
-    QString oldZoneTab     = mZoneTab;
-
     // Open zone.tab if we already know where it is
     QFile f;
-    if (!mZoneTab.isEmpty() && !mZoneinfoDir.isEmpty())
-    {
-        f.setFileName(mZoneTab);
-        if (!f.open(QIODevice::ReadOnly)) {
-            mZoneTab.clear();
-        }
-    }
-
-    if (mZoneTab.isEmpty() || mZoneinfoDir.isEmpty())
-    {
-        // Search for zone.tab
-        if (!findZoneTab(f))
-            return;
-        mZoneTab = f.fileName();
-
-        if (mZoneinfoDir != oldZoneinfoDir
-        ||  mZoneTab != oldZoneTab)
-        {
-            // Update config file and notify interested applications
-            group.writeEntry(ZONEINFO_DIR, mZoneinfoDir);
-            group.writeEntry(ZONE_TAB, mZoneTab);
-            group.sync();
-            QDBusMessage message = QDBusMessage::createSignal("/Daemon", "org.kde.KTimeZoned", "configChanged");
-            QDBusConnection::sessionBus().send(message);
-        }
-    }
+    // Search for zone.tab
+    if (!findZoneTab(f))
+        return;
+    mZoneTab = f.fileName();
 
     // Read zone.tab and create a collection of KTimeZone instances
     readZoneTab(f);
@@ -144,25 +117,34 @@ void KTimeZoned::init(bool restart)
     mZonetabWatch->addFile(mZoneTab);
     connect(mZonetabWatch, SIGNAL(dirty(const QString&)), SLOT(zonetab_Changed(const QString&)));
 
+    // Respect the config above all
+    if (!mConfigLocalZone.isEmpty()) {
+        // We know the local zone from last time.
+        // Check whether the file still matches it.
+        KTimeZone local = mZones.zone(mConfigLocalZone);
+        if (local.isValid())
+        {
+            mLocalZone = mConfigLocalZone;
+            mLocalZoneDataFile = mZoneinfoDir + "/" + mConfigLocalZone;
+        }
+    }
+
     // Find the local system time zone and set up file monitors to detect changes
     findLocalZone();
 }
 
-// Check if the local zone has been updated, and if so, write the new
-// zone to the config file and notify interested parties.
+// Check if the local zone has been updated, and if so, notify interested parties.
 void KTimeZoned::updateLocalZone()
 {
-    if (mConfigLocalZone != mLocalZone)
-    {
-        KConfig config(QLatin1String("ktimezonedrc"));
-        KConfigGroup group(&config, "TimeZones");
-        mConfigLocalZone = mLocalZone;
-        group.writeEntry(LOCAL_ZONE, mConfigLocalZone);
-        group.sync();
-
-        QDBusMessage message = QDBusMessage::createSignal("/Daemon", "org.kde.KTimeZoned", "configChanged");
-        QDBusConnection::sessionBus().send(message);
-    }
+    qDebug() << "updating timezone" << mLocalZone;
+    KConfig config(QLatin1String("ktimezonedrc"));
+    KConfigGroup group(&config, "TimeZones");
+    mConfigLocalZone = mLocalZone;
+    group.writeEntry(LOCAL_ZONE, mConfigLocalZone);
+    group.sync();
+
+    QDBusMessage message = QDBusMessage::createSignal("/Daemon", "org.kde.KTimeZoned", "configChanged");
+    QDBusConnection::sessionBus().send(message);
 }
 
 /*
@@ -196,6 +178,7 @@ bool KTimeZoned::findZoneTab(QFile& f)
         mZoneinfoDir = zoneinfoDir;
         f.setFileName(zoneinfoDir + ZONE_TAB_FILE);
         if (f.open(QIODevice::ReadOnly))
+            mZoneTab = zoneinfoDir + ZONE_TAB_FILE;
             return true;
         kDebug(1221) << "Can't open " << f.fileName();
     }
@@ -206,6 +189,7 @@ bool KTimeZoned::findZoneTab(QFile& f)
         mZoneinfoDir = zoneinfoDir;
         f.setFileName(zoneinfoDir + ZONE_TAB_FILE);
         if (f.open(QIODevice::ReadOnly))
+            mZoneTab = zoneinfoDir + ZONE_TAB_FILE;
             return true;
         kDebug(1221) << "Can't open " << f.fileName();
     }
@@ -217,6 +201,7 @@ bool KTimeZoned::findZoneTab(QFile& f)
 // Note that only data needed by this module is specified to KSystemTimeZone.
 void KTimeZoned::readZoneTab(QFile &f)
 {
+    qDebug() << "reading zone.tab";
     // Parse the already open real or fake zone.tab.
     QRegExp lineSeparator("[ \t]");
     if (!mSource)
@@ -254,122 +239,49 @@ void KTimeZoned::findLocalZone()
     delete mDirWatch;
     mDirWatch = 0;
     mLocalZone.clear();
-    mLocalIdFile.clear();
-    mLocalIdFile2.clear();
     mLocalZoneDataFile.clear();
 
-    // SOLUTION 1: DEFINITIVE.
-    // First try the simplest solution of checking for well-formed TZ setting.
+    // First try the checking for TZ envinomental variable.
     const char *envtz = ::getenv("TZ");
-    if (checkTZ(envtz))
-    {
-        mSavedTZ = envtz;
-        if (!mLocalZone.isEmpty()) kDebug(1221)<<"TZ: "<<mLocalZone;
-    }
+    checkEnv(envtz);
 
-    if (mLocalZone.isEmpty())
-    {
-        // SOLUTION 2: DEFINITIVE.
-        // BSD & Linux support: local time zone id in /etc/timezone.
-        checkTimezone();
-    }
-    if (mLocalZone.isEmpty() && !mZoneinfoDir.isEmpty())
-    {
-        // SOLUTION 3: DEFINITIVE.
-        // Try to follow any /etc/localtime symlink to a zoneinfo file.
-        // SOLUTION 4: DEFINITIVE.
+    if (mLocalZone.isEmpty() && !mZoneinfoDir.isEmpty()) {
         // Try to match /etc/localtime against the list of zoneinfo files.
-        matchZoneFile(QLatin1String("/etc/localtime"));
-    }
-    if (mLocalZone.isEmpty())
-    {
-        // SOLUTION 5: DEFINITIVE.
-        // Look for setting in /etc/rc.conf or /etc/rc.local.
-        checkRcFile();
-    }
-    if (mLocalZone.isEmpty())
-    {
-        // SOLUTION 6: DEFINITIVE.
-        // Solaris support using /etc/default/init.
-        checkDefaultInit();
+        // Resolve symlink
+        QFileInfo fi(QFile("/etc/localtime"));
+        if (fi.isSymLink()) {
+            checkLocaltime(fi.canonicalFilePath());
+        } else {
+            checkLocaltime(QLatin1String("/etc/localtime"));
+        }
     }
 
-    if (mLocalZone.isEmpty())
-    {
-        // The local time zone is not defined by a file.
-        // Watch for creation of /etc/localtime in case it gets created later.
-        // TODO: If under BSD it is possible for /etc/timezone to be missing but
-        //       created later, we should also watch for its creation.
-        mLocalIdFile = QLatin1String("/etc/localtime");
+    // Failsafe, fallback to UTC
+    if (mLocalZone.isEmpty()) {
+        mLocalZone = KTimeZone::utc().name();
+        kDebug(1221) << "Failsafe: " << mLocalZone;
     }
-    // Watch for changes in the file defining the local time zone so as to be
-    // notified of any change in it.
+
+    // Watch for changes in the file defining the local time zone
     mDirWatch = new KDirWatch(this);
-    mDirWatch->addFile(mLocalIdFile);
-    if (!mLocalIdFile2.isEmpty())
-        mDirWatch->addFile(mLocalIdFile2);
+    mDirWatch->addFile("/etc/localtime");
     if (!mLocalZoneDataFile.isEmpty())
         mDirWatch->addFile(mLocalZoneDataFile);
+        qDebug() << "watching" << mLocalZoneDataFile;
     connect(mDirWatch, SIGNAL(dirty(const QString&)), SLOT(localChanged(const QString&)));
     connect(mDirWatch, SIGNAL(deleted(const QString&)), SLOT(localChanged(const QString&)));
     connect(mDirWatch, SIGNAL(created(const QString&)), SLOT(localChanged(const QString&)));
+    mDirWatch->startScan();
 
-    if (mLocalZone.isEmpty() && !mZoneinfoDir.isEmpty())
-    {
-        // SOLUTION 7: HEURISTIC.
-        // None of the deterministic stuff above has worked: try a heuristic. We
-        // try to find a pair of matching time zone abbreviations...that way, we'll
-        // likely return a value in the user's own country.
-        tzset();
-        QByteArray tzname0(tzname[0]);   // store copies, because zone.parse() will change them
-        QByteArray tzname1(tzname[1]);
-        int bestOffset = INT_MAX;
-        KSystemTimeZoneSource::startParseBlock();
-        const KTimeZones::ZoneMap zmap = mZones.zones();
-        for (KTimeZones::ZoneMap::ConstIterator it = zmap.constBegin(), end = zmap.constEnd();  it != end;  ++it)
-        {
-            KTimeZone zone = it.value();
-            int candidateOffset = qAbs(zone.currentOffset(Qt::LocalTime));
-            if (candidateOffset < bestOffset
-            &&  zone.parse())
-            {
-                QList<QByteArray> abbrs = zone.abbreviations();
-                if (abbrs.contains(tzname0)  &&  abbrs.contains(tzname1))
-                {
-                    // kDebug(1221) << "local=" << zone.name();
-                    mLocalZone = zone.name();
-                    bestOffset = candidateOffset;
-                    if (!bestOffset)
-                        break;
-                }
-            }
-        }
-        KSystemTimeZoneSource::endParseBlock();
-        if (!mLocalZone.isEmpty())
-        {
-            mLocalMethod = TzName;
-            kDebug(1221)<<"tzname: "<<mLocalZone;
-        }
-    }
-    if (mLocalZone.isEmpty())
-    {
-        // SOLUTION 8: FAILSAFE.
-        mLocalZone = KTimeZone::utc().name();
-        mLocalMethod = Utc;
-        if (!mLocalZone.isEmpty()) kDebug(1221)<<"Failsafe: "<<mLocalZone;
-    }
-
-    // Finally, if the local zone identity has changed, store
-    // the new one in the config file.
+    // Finally, if the local zone identity has changed, let the world know
     updateLocalZone();
 }
 
 // Called when KDirWatch detects a change in zone.tab
 void KTimeZoned::zonetab_Changed(const QString& path)
 {
-    kDebug(1221) << "zone.tab changed";
-    if (path != mZoneTab)
-    {
+    qDebug() << "zone.tab changed";
+    if (path != mZoneTab) {
         kError(1221) << "Wrong path (" << path << ") for zone.tab";
         return;
     }
@@ -384,497 +296,69 @@ void KTimeZoned::zonetab_Changed(const QString& path)
     // subsequently becomes the local zone.
     QFile f;
     f.setFileName(mZoneTab);
-    if (!f.open(QIODevice::ReadOnly))
+    if (!f.open(QIODevice::ReadOnly)) {
         kError(1221) << "Could not open zone.tab (" << mZoneTab << ") to reread";
-    else
+    } else {
         readZoneTab(f);
+    }
 }
 
 // Called when KDirWatch detects a change
 void KTimeZoned::localChanged(const QString& path)
 {
-    if (path == mLocalZoneDataFile)
-    {
-        // Only need to update the definition of the local zone,
-        // not its identity.
-        QDBusMessage message = QDBusMessage::createSignal("/Daemon", "org.kde.KTimeZoned", "zoneDefinitionChanged");
-        QList<QVariant> args;
-        args += mLocalZone;
-        message.setArguments(args);
-        QDBusConnection::sessionBus().send(message);
-        return;
-    }
-    QString oldDataFile = mLocalZoneDataFile;
-    switch (mLocalMethod)
-    {
-        case EnvTzLink:
-        case EnvTzFile:
-        {
-            const char *envtz = ::getenv("TZ");
-            if (mSavedTZ != envtz)
-            {
-                // TZ has changed - start from scratch again
-                findLocalZone();
-                return;
-            }
-            // The contents of the file pointed to by TZ has changed.
-        }
-            // Fall through to LocaltimeLink
-        case LocaltimeLink:
-        case LocaltimeCopy:
-        // The fallback methods below also set a watch for /etc/localtime in
-        // case it gets created.
-        case TzName:
-        case Utc:
-            matchZoneFile(mLocalIdFile);
-            break;
-        case Timezone:
-            checkTimezone();
-            break;
-        case RcFile:
-            checkRcFile();
-            break;
-        case DefaultInit:
-            checkDefaultInit();
-            break;
-        default:
-            return;
-    }
-    if (oldDataFile != mLocalZoneDataFile)
-    {
-        if (!oldDataFile.isEmpty())
-            mDirWatch->removeFile(oldDataFile);
-        if (!mLocalZoneDataFile.isEmpty())
-            mDirWatch->addFile(mLocalZoneDataFile);
-    }
-    updateLocalZone();
-}
-
-bool KTimeZoned::checkTZ(const char *envZone)
-{
-    // SOLUTION 1: DEFINITIVE.
-    // First try the simplest solution of checking for well-formed TZ setting.
-    if (envZone)
-    {
-        if (envZone[0] == '\0')
-        {
-            mLocalMethod = EnvTz;
-            mLocalZone = KTimeZone::utc().name();
-            mLocalIdFile.clear();
-            mLocalZoneDataFile.clear();
-            return true;
-        }
-        if (envZone[0] == ':')
-        {
-            // TZ specifies a file name, either relative to zoneinfo/ or absolute.
-            QString TZfile = QFile::decodeName(envZone + 1);
-            if (TZfile.startsWith(mZoneinfoDir))
-            {
-                // It's an absolute file name in the zoneinfo directory.
-                // Convert it to a file name relative to zoneinfo/.
-                TZfile = TZfile.mid(mZoneinfoDir.length());
-            }
-            if (TZfile.startsWith(QLatin1Char('/')))
-            {
-                // It's an absolute file name.
-                QString symlink;
-                if (matchZoneFile(TZfile))
-                {
-                    mLocalMethod = static_cast<LocalMethod>(EnvTz | (mLocalMethod & TypeMask));
-                    return true;
-                }
-            }
-            else if (!TZfile.isEmpty())
-            {
-                // It's a file name relative to zoneinfo/
-                mLocalZone = TZfile;
-                if (!mLocalZone.isEmpty())
-                {
-                    mLocalMethod = EnvTz;
-                    mLocalZoneDataFile = mZoneinfoDir + '/' + TZfile;
-                    mLocalIdFile.clear();
-                    return true;
-                }
-            }
-        }
-    }
-    return false;
-}
-
-bool KTimeZoned::checkTimezone()
-{
-    // SOLUTION 2: DEFINITIVE.
-    // BSD support.
-    QFile f;
-    f.setFileName(QLatin1String("/etc/timezone"));
-    if (!f.open(QIODevice::ReadOnly))
-        return false;
-    // Read the first line of the file.
-    QTextStream ts(&f);
-    ts.setCodec("ISO-8859-1");
-    QString zoneName;
-    if (!ts.atEnd())
-        zoneName = ts.readLine();
-    f.close();
-    if (zoneName.isEmpty())
-        return false;
-    if (!setLocalZone(zoneName))
-        return false;
-    mLocalMethod = Timezone;
-    mLocalIdFile = f.fileName();
-    kDebug(1221)<<"/etc/timezone: "<<mLocalZone;
-    return true;
+    kDebug(1221) << path << "changed";
+    findLocalZone();
 }
 
-bool KTimeZoned::matchZoneFile(const QString &path)
+bool KTimeZoned::checkEnv(const char *envZone)
 {
-    // SOLUTION 3: DEFINITIVE.
-    // Try to follow any symlink to a zoneinfo file.
-    // Get the path of the file which the symlink points to.
-    QFile f;
-    f.setFileName(path);
-    QFileInfo fi(f);
-    if (fi.isSymLink())
-    {
-        // The file is a symlink.
-        QString zoneInfoFileName = fi.canonicalFilePath();
-        QFileInfo fiz(zoneInfoFileName);
-        if (fiz.exists() && fiz.isReadable())
-        {
-            if (zoneInfoFileName.startsWith(mZoneinfoDir))
-            {
-                // We've got the zoneinfo file path.
-                // The time zone name is the part of the path after the zoneinfo directory.
-                // Note that some systems (e.g. Gentoo) have zones under zoneinfo which
-                // are not in zone.tab, so don't validate against mZones.
-                mLocalZone = zoneInfoFileName.mid(mZoneinfoDir.length() + 1);
-                // kDebug(1221) << "local=" << mLocalZone;
-            }
-            else
-            {
-                // It isn't a zoneinfo file or a copy thereof.
-                // Use the absolute path as the time zone name.
-                mLocalZone = f.fileName();
-            }
-            mLocalMethod = LocaltimeLink;
-            mLocalIdFile = f.fileName();
-            mLocalZoneDataFile = zoneInfoFileName;
-            kDebug(1221)<<mLocalIdFile<<": "<<mLocalZone;
-            return true;
-        }
+    if (!mLocalZone.isEmpty()) {
+        // It has most likely already been set by preferences in the config
+        return true;
     }
-    else if (f.open(QIODevice::ReadOnly))
-    {
-        // SOLUTION 4: DEFINITIVE.
-        // Try to match the file against the list of zoneinfo files.
-
-        // Compute the file's MD5 sum.
-        KMD5 context("");
-        context.reset();
-        context.update(f);
-        qlonglong referenceSize = f.size();
-        QString referenceMd5Sum = context.hexDigest();
-        MD5Map::ConstIterator it5, end5;
-        KTimeZone local;
-        QString zoneName;
-
-        if (!mConfigLocalZone.isEmpty())
-        {
-            // We know the local zone from last time.
-            // Check whether the file still matches it.
-            KTimeZone tzone = mZones.zone(mConfigLocalZone);
-            if (tzone.isValid())
-            {
-                local = compareChecksum(tzone, referenceMd5Sum, referenceSize);
-                if (local.isValid())
-                    zoneName = local.name();
+    if (envZone) {
+        if (envZone[0] == ':') {
+            // TZ specified, either relative to zoneinfo/ or absolute.
+            QString tz = QFile::decodeName(envZone + 1);
+            KTimeZone local = mZones.zone(tz);
+            if (!local.isValid()) {
+                return false;
             }
-        }
-
-        if (!local.isValid() && mHaveCountryCodes)
-        {
-            /* Look for time zones with the user's country code.
-             * This has two advantages: 1) it shortens the search;
-             * 2) it increases the chance of the correctly titled time zone
-             * being found, since multiple time zones can have identical
-             * definitions. For example, Europe/Guernsey is identical to
-             * Europe/London, but the latter is more likely to be the right
-             * zone name for a user with 'gb' country code.
-             */
-            QString country = KGlobal::locale()->country().toUpper();
-            const KTimeZones::ZoneMap zmap = mZones.zones();
-            for (KTimeZones::ZoneMap::ConstIterator zit = zmap.constBegin(), zend = zmap.constEnd();  zit != zend;  ++zit)
-            {
-                KTimeZone tzone = zit.value();
-                if (tzone.countryCode() == country)
-                {
-                    local = compareChecksum(tzone, referenceMd5Sum, referenceSize);
-                    if (local.isValid())
-                    {
-                        zoneName = local.name();
-                        break;
-                    }
-                }
+            mLocalZone = tz;
+            if (QFile(tz).exists()) {
+                // Full path.
+                mLocalZoneDataFile = tz;
+            } else {
+                // Relative path.
+                mLocalZoneDataFile = mZoneinfoDir + "/" + tz;
             }
         }
-
-        if (!local.isValid())
-        {
-            // Look for a checksum match with the cached checksum values
-            MD5Map oldChecksums = mMd5Sums;   // save a copy of the existing checksums
-            for (it5 = mMd5Sums.constBegin(), end5 = mMd5Sums.constEnd();  it5 != end5;  ++it5)
-            {
-                if (it5.value() == referenceMd5Sum)
-                {
-                    // The cached checksum matches. Ensure that the file hasn't changed.
-                    if (compareChecksum(it5, referenceMd5Sum, referenceSize))
-                    {
-                        zoneName = it5.key();
-                        local = mZones.zone(zoneName);
-                        if (local.isValid())
-                            break;
-                    }
-                    oldChecksums.clear();    // the cache has been cleared
-                    break;
-                }
-            }
-
-            if (!local.isValid())
-            {
-                // The checksum didn't match any in the cache.
-                // Continue building missing entries in the cache on the assumption that
-                // we haven't previously looked at the zoneinfo file which matches.
-                const KTimeZones::ZoneMap zmap = mZones.zones();
-                for (KTimeZones::ZoneMap::ConstIterator zit = zmap.constBegin(), zend = zmap.constEnd();  zit != zend;  ++zit)
-                {
-                    KTimeZone zone = zit.value();
-                    zoneName = zone.name();
-                    if (!mMd5Sums.contains(zoneName))
-                    {
-                        QString candidateMd5Sum = calcChecksum(zoneName, referenceSize);
-                        if (candidateMd5Sum == referenceMd5Sum)
-                        {
-                            // kDebug(1221) << "local=" << zone.name();
-                            local = zone;
-                            break;
-                        }
-                    }
-                }
-            }
-
-            if (!local.isValid())
-            {
-                // Didn't find the file, so presumably a previously cached checksum must
-                // have changed. Delete all the old checksums.
-                MD5Map::ConstIterator mit;
-                MD5Map::ConstIterator mend = oldChecksums.constEnd();
-                for (mit = oldChecksums.constBegin();  mit != mend;  ++mit)
-                    mMd5Sums.remove(mit.key());
-
-                // And recalculate the old checksums
-                for (mit = oldChecksums.constBegin(); mit != mend; ++mit)
-                {
-                    zoneName = mit.key();
-                    QString candidateMd5Sum = calcChecksum(zoneName, referenceSize);
-                    if (candidateMd5Sum == referenceMd5Sum)
-                    {
-                        // kDebug(1221) << "local=" << zoneName;
-                        local = mZones.zone(zoneName);
-                        break;
-                    }
-                }
-            }
-        }
-        bool success = false;
-        if (local.isValid())
-        {
-            // The file matches a zoneinfo file
-            mLocalZone = zoneName;
-            mLocalZoneDataFile = mZoneinfoDir + '/' + zoneName;
-            success = true;
-        }
-        else
-        {
-            // The file doesn't match a zoneinfo file. If it's a TZfile, use it directly.
-            // Read the file type identifier.
-            char buff[4];
-            f.reset();
-            QDataStream str(&f);
-            if (str.readRawData(buff, 4) == 4
-            &&  buff[0] == 'T' && buff[1] == 'Z' && buff[2] == 'i' && buff[3] == 'f')
-            {
-                // Use its absolute path as the zone name.
-                mLocalZone = f.fileName();
-                mLocalZoneDataFile.clear();
-                success = true;
-            }
-        }
-        f.close();
-        if (success)
-        {
-            mLocalMethod = LocaltimeCopy;
-            mLocalIdFile = f.fileName();
-            kDebug(1221)<<mLocalIdFile<<": "<<mLocalZone;
-            return true;
-        }
     }
     return false;
 }
 
-bool KTimeZoned::checkRcFile()
+bool KTimeZoned::checkLocaltime(const QString &path)
 {
-    // SOLUTION 5: DEFINITIVE.
-    // Look for setting in /etc/rc.conf or /etc/rc.local,
-    // with priority to /etc/rc.local.
-    if (findKey(QLatin1String("/etc/rc.local"), "TIMEZONE"))
-    {
-        mLocalIdFile2.clear();
-        kDebug(1221)<<"/etc/rc.local: "<<mLocalZone;
-    }
-    else
-    {
-        if (!findKey(QLatin1String("/etc/rc.conf"), "TIMEZONE"))
-            return false;
-        mLocalIdFile2 = mLocalIdFile;
-        mLocalIdFile  = QLatin1String("/etc/rc.local");
-        kDebug(1221)<<"/etc/rc.conf: "<<mLocalZone;
+    if (!mLocalZone.isEmpty()) {
+        // It has most likely already been set by preferences in the config
+        return true;
     }
-    mLocalMethod = RcFile;
-    return true;
-}
 
-bool KTimeZoned::checkDefaultInit()
-{
-    // SOLUTION 6: DEFINITIVE.
-    // Solaris support using /etc/default/init.
-    if (!findKey(QLatin1String("/etc/default/init"), "TZ"))
+    if (mZoneinfoDir.isEmpty() && !path.indexOf(mZoneinfoDir)) {
+        qDebug() << "relative path passed to checkLocaltime()";
         return false;
-    mLocalMethod = DefaultInit;
-    kDebug(1221)<<"/etc/default/init: "<<mLocalZone;
-    return true;
-}
-
-bool KTimeZoned::findKey(const QString &path, const QString &key)
-{
-    QFile f;
-    f.setFileName(path);
-    if (!f.open(QIODevice::ReadOnly))
-        return false;
-    QString line;
-    QString zoneName;
-    QRegExp keyexp('^' + key + "\\s*=\\s*");
-    QTextStream ts(&f);
-    ts.setCodec("ISO-8859-1");
-    while (!ts.atEnd())
-    {
-        line = ts.readLine();
-        if (keyexp.indexIn(line) == 0)
-        {
-            zoneName = line.mid(keyexp.matchedLength());
-            break;
-        }
     }
-    f.close();
-    if (zoneName.isEmpty())
-        return false;
-    if (!setLocalZone(zoneName))
-        return false;
-    kDebug(1221) << "Key:" << key << "->" << zoneName;
-    mLocalIdFile = f.fileName();
-    return true;
-}
 
-// Check whether the zone name is valid, either as a zone in zone.tab or
-// as another file in the zoneinfo directory.
-// If valid, set the local zone information.
-bool KTimeZoned::setLocalZone(const QString &zoneName)
-{
-    KTimeZone local = mZones.zone(zoneName);
-    if (!local.isValid())
-    {
-        // It isn't a recognised zone in zone.tab.
-        // Note that some systems (e.g. Gentoo) have zones under zoneinfo which
-        // are not in zone.tab, so check if it points to another zone file.
-        if (mZoneinfoDir.isEmpty())
-            return false;
-        QString path = mZoneinfoDir + '/' + zoneName;
-        QFile qf;
-        qf.setFileName(path);
-        QFileInfo fi(qf);
-        if (fi.isSymLink())
-            fi.setFile(fi.canonicalFilePath());
-        if (!fi.exists() || !fi.isReadable())
-            return false;
-    }
-    mLocalZone = zoneName;
-    mLocalZoneDataFile = mZoneinfoDir.isEmpty() ? QString() : mZoneinfoDir + '/' + zoneName;
-    return true;
-}
+    // The path could be full path, remove zoneinfo directory reference
+    QString rel = path.mid(mZoneinfoDir.length() + 1);
 
-// Check whether the checksum for a time zone matches a given saved checksum.
-KTimeZone KTimeZoned::compareChecksum(const KTimeZone &zone, const QString &referenceMd5Sum, qlonglong size)
-{
-    MD5Map::ConstIterator it5 = mMd5Sums.constFind(zone.name());
-    if (it5 == mMd5Sums.constEnd())
-    {
-        // No checksum has been computed yet for this zone file.
-        // Compute it now.
-        QString candidateMd5Sum = calcChecksum(zone.name(), size);
-        if (candidateMd5Sum == referenceMd5Sum)
-        {
-            // kDebug(1221) << "local=" << zone.name();
-            return zone;
-        }
-        return KTimeZone();
-    }
-    if (it5.value() == referenceMd5Sum)
-    {
-        // The cached checksum matches. Ensure that the file hasn't changed.
-        if (compareChecksum(it5, referenceMd5Sum, size))
-            return mZones.zone(it5.key());
+    KTimeZone local = mZones.zone(rel);
+    if (!local.isValid()) {
+        return false;
     }
-    return KTimeZone();
-}
-
-// Check whether a checksum matches a given saved checksum.
-// Returns false if the file no longer matches and cache was cleared.
-bool KTimeZoned::compareChecksum(MD5Map::ConstIterator it5, const QString &referenceMd5Sum, qlonglong size)
-{
-    // The cached checksum matches. Ensure that the file hasn't changed.
-    QString zoneName = it5.key();
-    QString candidateMd5Sum = calcChecksum(zoneName, size);
-    if (candidateMd5Sum.isNull())
-        mMd5Sums.remove(zoneName);    // no match - wrong file size
-    else if (candidateMd5Sum == referenceMd5Sum)
-        return true;
 
-    // File(s) have changed, so clear the cache
-    mMd5Sums.clear();
-    mMd5Sums[zoneName] = candidateMd5Sum;    // reinsert the newly calculated checksum
-    return false;
-}
-
-// Calculate the MD5 checksum for the given zone file, provided that its size matches.
-// The calculated checksum is cached.
-QString KTimeZoned::calcChecksum(const QString &zoneName, qlonglong size)
-{
-    QString path = mZoneinfoDir + '/' + zoneName;
-    QFileInfo fi(path);
-    if (static_cast<qlonglong>(fi.size()) == size)
-    {
-        // Only do the heavy lifting for file sizes which match.
-        QFile f;
-        f.setFileName(path);
-        if (f.open(QIODevice::ReadOnly))
-        {
-            KMD5 context("");
-            context.reset();
-            context.update(f);
-            QString candidateMd5Sum = context.hexDigest();
-            f.close();
-            mMd5Sums[zoneName] = candidateMd5Sum;    // cache the new checksum
-            return candidateMd5Sum;
-        }
-    }
-    return QString();
+    mLocalZone = rel;
+    mLocalZoneDataFile = path;
+    kDebug(1221) << mLocalZone;
+    return true;
 }
index 21cb25d..9b1feee 100644 (file)
@@ -48,56 +48,28 @@ class KTimeZoned : public KTimeZonedBase
         // How the local time zone is specified
         enum LocalMethod
         {
-            TypeMask = 0x30,
-            Link = 0x10,    // specified by a symlink
-            File = 0x20,    // specified by a normal file
-
             Utc,            // use UTC default: no local zone spec was found
             EnvTz,          // specified in TZ environment variable
-            TzName,         // specified in tzname via tzset()
             Localtime,      // specified in /etc/localtime
-            Timezone,       // specified in /etc/timezone
-            RcFile,         // specified in /etc/rc.conf or /etc/rc.local
-            DefaultInit,    // specified in /etc/default/init
-            EnvTzFile = EnvTz | File,
-            EnvTzLink = EnvTz | Link,
-            LocaltimeCopy = Localtime | File,
-            LocaltimeLink = Localtime | Link
         };
-        typedef QMap<QString, QString> MD5Map;    // zone name, checksum
 
         /** reimp */
         void  init(bool restart);
         bool  findZoneTab(QFile& f);
         void  readZoneTab(QFile& f);
         void  findLocalZone();
-        bool  checkTZ(const char *envZone);
-        bool  checkLocaltimeLink();
-        bool  checkLocaltimeFile();
-        bool  checkTimezone();
-        bool  checkRcFile();
-        bool  checkDefaultInit();
+        bool  checkEnv(const char *envZone);
+        bool  checkLocaltime(const QString &path);
         void  updateLocalZone();
-        bool  matchZoneFile(const QString &path);
-        bool  findKey(const QString &path, const QString &key);
-        bool  setLocalZone(const QString &zoneName);
-        KTimeZone compareChecksum(const KTimeZone&, const QString &referenceMd5Sum, qlonglong size);
-        bool  compareChecksum(MD5Map::ConstIterator, const QString &referenceMd5Sum, qlonglong size);
-        QString calcChecksum(const QString &zoneName, qlonglong size);
+        bool  setLocalZone(const QString &path);
 
         QString     mZoneinfoDir;       // path to zoneinfo directory
         QString     mZoneTab;           // path to zone.tab file
-        QByteArray  mSavedTZ;           // last value of TZ if it's used to set local zone
         KSystemTimeZoneSource *mSource;
         KTimeZones  mZones;             // time zones collection
-        QString     mLocalIdFile;       // file containing pointer to local time zone definition
-        QString     mLocalIdFile2;      // file containing pointer 2 to local time zone definition
         QString     mLocalZoneDataFile; // zoneinfo file containing local time zone definition
-        QString     mLocaltimeMd5Sum;   // MD5 checksum of /etc/localtime
-        LocalMethod mLocalMethod;       // how the local time zone is specified
         KDirWatch  *mZonetabWatch;      // watch for zone.tab file changes
         KDirWatch  *mDirWatch;          // watch for time zone definition file changes
-        MD5Map      mMd5Sums;           // MD5 checksums of zoneinfo files
         bool        mHaveCountryCodes;  // true if zone.tab contains any country codes
 };