File indexing completed on 2024-05-12 04:06:05

0001 /*
0002     This file is part of the KDE games library
0003     SPDX-FileCopyrightText: 2001 Andreas Beckermann <b_mann@gmx.de>
0004     SPDX-FileCopyrightText: 2003 Nicolas Hadacek <hadacek@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-only
0007 */
0008 
0009 #include "kgamehighscore.h"
0010 
0011 // own
0012 #include <config-highscore.h>
0013 #include <kdegames_highscore_logging.h>
0014 // KF
0015 #include <KConfig>
0016 #include <KConfigGroup>
0017 #include <KLocalizedString>
0018 #include <KMessageBox>
0019 #include <KSharedConfig>
0020 #include <KStandardGuiItem>
0021 // Qt
0022 #include <QFile>
0023 #include <QLockFile>
0024 
0025 #ifndef WIN32
0026 #include <unistd.h> // sleep
0027 #else
0028 #include <qt_windows.h>
0029 #endif
0030 
0031 #define GROUP "KHighscore"
0032 
0033 class KGameHighscorePrivate
0034 {
0035 public:
0036     KGameHighscorePrivate() = default;
0037 
0038 public:
0039     QString group;
0040     bool global;
0041 };
0042 
0043 class KGameHighscoreLockedConfig
0044 {
0045 public:
0046     ~KGameHighscoreLockedConfig();
0047     QLockFile *lock;
0048     KConfig *config;
0049 };
0050 
0051 KGameHighscoreLockedConfig::~KGameHighscoreLockedConfig()
0052 {
0053     delete lock;
0054     delete config;
0055 }
0056 
0057 Q_GLOBAL_STATIC(KGameHighscoreLockedConfig, lockedConfig)
0058 
0059 KGameHighscore::KGameHighscore(bool forceLocal, QObject *parent)
0060     : QObject(parent)
0061     , d_ptr(new KGameHighscorePrivate)
0062 {
0063     init(forceLocal);
0064 }
0065 
0066 void KGameHighscore::init(bool forceLocal)
0067 {
0068     Q_D(KGameHighscore);
0069 
0070 #ifdef HIGHSCORE_DIRECTORY
0071     d->global = !forceLocal;
0072     if (d->global && lockedConfig->lock == 0) // If we're doing global highscores but not KFileLock has been set up yet
0073     {
0074         qCWarning(KDEGAMES_HIGHSCORE_LOG) << "KGameHighscore::init should be called before!!";
0075         abort();
0076     }
0077 #else
0078     d->global = false;
0079     Q_UNUSED(forceLocal);
0080 #endif
0081     readCurrentConfig();
0082 }
0083 
0084 bool KGameHighscore::isLocked() const
0085 {
0086     Q_D(const KGameHighscore);
0087 
0088     return (d->global ? lockedConfig->lock->isLocked() : true);
0089 }
0090 
0091 void KGameHighscore::readCurrentConfig()
0092 {
0093     Q_D(KGameHighscore);
0094 
0095     if (d->global)
0096         lockedConfig->config->reparseConfiguration();
0097 }
0098 
0099 void KGameHighscore::init(const char *appname)
0100 {
0101 #ifdef HIGHSCORE_DIRECTORY
0102     const QString filename = QString::fromLocal8Bit("%1/%2.scores").arg(HIGHSCORE_DIRECTORY).arg(appname);
0103 
0104     // int fd = fopen(filename.toLocal8Bit(), O_RDWR);
0105     /*QFile file(filename);
0106     if ( !file.open(QIODevice::ReadWrite) )
0107     {
0108       qCWarning(KDEGAMES_HIGHSCORE_LOG) << "cannot open global highscore file \""
0109                                << filename << "\"";
0110       abort();
0111     }*/
0112 
0113     /*if (!(QFile::permissions(filename) & QFile::WriteOwner))
0114       {
0115     qCWarning(KDEGAMES_HIGHSCORE_LOG) << "cannot write to global highscore file \""
0116                 << filename << "\"";
0117     abort();
0118       }*/
0119 
0120     qCDebug(KDEGAMES_HIGHSCORE_LOG) << "Global highscore file \"" << filename << "\"";
0121     lockedConfig->lock = new QLockFile(filename);
0122     lockedConfig->config = new KConfig(filename, KConfig::NoGlobals); // read-only   (matt-?)
0123 
0124     // drop the effective gid
0125 #ifdef __GNUC__
0126 #warning not portable yet. Unix only. Actually it does not even work there yet.
0127 #endif
0128     int gid = getgid();
0129     setregid(gid, gid);
0130 #else
0131     Q_UNUSED(appname);
0132 #endif
0133 }
0134 
0135 bool KGameHighscore::lockForWriting(QWidget *widget)
0136 {
0137     if (isLocked())
0138         return true;
0139 
0140     bool first = true;
0141     for (;;) {
0142         qCDebug(KDEGAMES_HIGHSCORE_LOG) << "try locking";
0143         // lock the highscore file (it should exist)
0144         int result = lockedConfig->lock->lock();
0145         bool ok = (result == 0);
0146         qCDebug(KDEGAMES_HIGHSCORE_LOG) << "locking system-wide highscore file res=" << result << " (ok=" << ok << ")";
0147         if (ok) {
0148             readCurrentConfig();
0149             return true;
0150         }
0151 
0152         if (!first) {
0153             KGuiItem item = KStandardGuiItem::cont();
0154             item.setText(i18n("Retry"));
0155             int res = KMessageBox::warningContinueCancel(widget,
0156                                                          i18n("Cannot access the highscore file. Another user is probably currently writing to it."),
0157                                                          QString(),
0158                                                          item,
0159                                                          KStandardGuiItem::cancel(),
0160                                                          QStringLiteral("ask_lock_global_highscore_file"));
0161             if (res == KMessageBox::Cancel)
0162                 break;
0163         } else {
0164 #ifdef WIN32
0165             Sleep(1000);
0166 #else
0167             sleep(1);
0168 #endif
0169         }
0170         first = false;
0171     }
0172     return false;
0173 }
0174 
0175 void KGameHighscore::writeAndUnlock()
0176 {
0177     Q_D(KGameHighscore);
0178 
0179     if (!d->global) {
0180         KSharedConfig::openConfig()->sync();
0181         return;
0182     }
0183     if (!isLocked())
0184         return;
0185 
0186     qCDebug(KDEGAMES_HIGHSCORE_LOG) << "unlocking";
0187     lockedConfig->config->sync(); // write config
0188     lockedConfig->lock->unlock();
0189 }
0190 
0191 KGameHighscore::~KGameHighscore()
0192 {
0193     writeAndUnlock();
0194 }
0195 
0196 KConfig *KGameHighscore::config() const
0197 {
0198     Q_D(const KGameHighscore);
0199 
0200     return (d->global ? lockedConfig->config : static_cast<KConfig *>(KSharedConfig::openConfig().data()));
0201 }
0202 
0203 void KGameHighscore::writeEntry(int entry, const QString &key, const QVariant &value)
0204 {
0205     Q_ASSERT(isLocked());
0206     KConfigGroup cg(config(), group());
0207     QString confKey = QStringLiteral("%1_%2").arg(entry).arg(key);
0208     cg.writeEntry(confKey, value);
0209 }
0210 
0211 void KGameHighscore::writeEntry(int entry, const QString &key, int value)
0212 {
0213     Q_ASSERT(isLocked());
0214     KConfigGroup cg(config(), group());
0215     QString confKey = QStringLiteral("%1_%2").arg(entry).arg(key);
0216     cg.writeEntry(confKey, value);
0217 }
0218 
0219 void KGameHighscore::writeEntry(int entry, const QString &key, const QString &value)
0220 {
0221     Q_ASSERT(isLocked());
0222     KConfigGroup cg(config(), group());
0223     QString confKey = QStringLiteral("%1_%2").arg(entry).arg(key);
0224     cg.writeEntry(confKey, value);
0225 }
0226 
0227 QVariant KGameHighscore::readPropertyEntry(int entry, const QString &key, const QVariant &pDefault) const
0228 {
0229     KConfigGroup cg(config(), group());
0230     QString confKey = QStringLiteral("%1_%2").arg(entry).arg(key);
0231     return cg.readEntry(confKey, pDefault);
0232 }
0233 
0234 QString KGameHighscore::readEntry(int entry, const QString &key, const QString &pDefault) const
0235 {
0236     KConfigGroup cg(config(), group());
0237     QString confKey = QStringLiteral("%1_%2").arg(entry).arg(key);
0238     return cg.readEntry(confKey, pDefault);
0239 }
0240 
0241 int KGameHighscore::readNumEntry(int entry, const QString &key, int pDefault) const
0242 {
0243     KConfigGroup cg(config(), group());
0244     QString confKey = QStringLiteral("%1_%2").arg(entry).arg(key);
0245     return cg.readEntry(confKey, pDefault);
0246 }
0247 
0248 bool KGameHighscore::hasEntry(int entry, const QString &key) const
0249 {
0250     KConfigGroup cg(config(), group());
0251     QString confKey = QStringLiteral("%1_%2").arg(entry).arg(key);
0252     return cg.hasKey(confKey);
0253 }
0254 
0255 QStringList KGameHighscore::readList(const QString &key, int lastEntry) const
0256 {
0257     QStringList list;
0258     if (lastEntry > 0) {
0259         list.reserve(lastEntry);
0260     }
0261     for (int i = 1; hasEntry(i, key) && ((lastEntry > 0) ? (i <= lastEntry) : true); i++) {
0262         list.append(readEntry(i, key));
0263     }
0264     return list;
0265 }
0266 
0267 void KGameHighscore::writeList(const QString &key, const QStringList &list)
0268 {
0269     for (int i = 1; i <= list.count(); i++) {
0270         writeEntry(i, key, list[i - 1]);
0271     }
0272 }
0273 
0274 void KGameHighscore::setHighscoreGroup(const QString &group)
0275 {
0276     Q_D(KGameHighscore);
0277 
0278     d->group = group;
0279 }
0280 
0281 QString KGameHighscore::highscoreGroup() const
0282 {
0283     Q_D(const KGameHighscore);
0284 
0285     return d->group;
0286 }
0287 
0288 QStringList KGameHighscore::groupList() const
0289 {
0290     const QStringList groupList = config()->groupList();
0291     QStringList highscoreGroupList;
0292     for (QString group : groupList) {
0293         if (group.contains(QLatin1String("KHighscore"))) // If it's one of _our_ groups (KHighscore or KHighscore_xxx)
0294         {
0295             if (group == QLatin1String("KHighscore"))
0296                 group.remove(QStringLiteral("KHighscore")); // Set to blank
0297             else
0298                 group.remove(QStringLiteral("KHighscore_")); // Remove the KHighscore_ prefix
0299             highscoreGroupList << group;
0300         }
0301     }
0302 
0303     return highscoreGroupList;
0304 }
0305 
0306 QString KGameHighscore::group() const
0307 {
0308     Q_D(const KGameHighscore);
0309 
0310     if (highscoreGroup().isEmpty())
0311         return (d->global ? QString() : QStringLiteral(GROUP));
0312     return (d->global ? highscoreGroup() : QStringLiteral("%1_%2").arg(QStringLiteral(GROUP), highscoreGroup()));
0313 }
0314 
0315 bool KGameHighscore::hasTable() const
0316 {
0317     return config()->hasGroup(group());
0318 }
0319 
0320 #include "moc_kgamehighscore.cpp"