Warning, file /games/kreversi/src/kexthighscore_internal.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2001-2004 Nicolas Hadacek <hadacek@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include "kexthighscore_internal.h" 0008 0009 #include <sys/types.h> 0010 0011 #include <QCryptographicHash> 0012 #include <QDomDocument> 0013 #include <QTemporaryFile> 0014 0015 #include <KIO/FileCopyJob> 0016 #include <KIO/SimpleJob> 0017 #include <KJobWidgets> 0018 #include <KMessageBox> 0019 #include <KUser> 0020 0021 #include "kexthighscore_gui.h" 0022 #include <KEMailSettings> 0023 0024 // TODO Decide if want to support 0025 // a build time HIGHSCORE_DIRECTORY or not 0026 // #include <config-highscore.h> 0027 0028 namespace KExtHighscore 0029 { 0030 0031 //----------------------------------------------------------------------------- 0032 const char ItemContainer::ANONYMOUS[] = "_"; 0033 const KLazyLocalizedString ItemContainer::ANONYMOUS_LABEL = kli18n("anonymous"); 0034 0035 ItemContainer::ItemContainer() 0036 : _item(nullptr) 0037 {} 0038 0039 ItemContainer::~ItemContainer() 0040 { 0041 delete _item; 0042 } 0043 0044 void ItemContainer::setItem(Item *item) 0045 { 0046 delete _item; 0047 _item = item; 0048 } 0049 0050 QString ItemContainer::entryName() const 0051 { 0052 if ( _subGroup.isEmpty() ) return _name; 0053 return _name + QLatin1Char( '_' ) + _subGroup; 0054 } 0055 0056 QVariant ItemContainer::read(uint i) const 0057 { 0058 Q_ASSERT(_item); 0059 0060 QVariant v = _item->defaultValue(); 0061 if ( isStored() ) { 0062 internal->hsConfig().setHighscoreGroup(_group); 0063 v = internal->hsConfig().readPropertyEntry(i+1, entryName(), v); 0064 } 0065 return _item->read(i, v); 0066 } 0067 0068 QString ItemContainer::pretty(uint i) const 0069 { 0070 Q_ASSERT(_item); 0071 return _item->pretty(i, read(i)); 0072 } 0073 0074 void ItemContainer::write(uint i, const QVariant &value) const 0075 { 0076 Q_ASSERT( isStored() ); 0077 Q_ASSERT( internal->hsConfig().isLocked() ); 0078 internal->hsConfig().setHighscoreGroup(_group); 0079 internal->hsConfig().writeEntry(i+1, entryName(), value); 0080 } 0081 0082 uint ItemContainer::increment(uint i) const 0083 { 0084 uint v = read(i).toUInt() + 1; 0085 write(i, v); 0086 return v; 0087 } 0088 0089 //----------------------------------------------------------------------------- 0090 ItemArray::ItemArray() 0091 : _group(QLatin1String( "" )), _subGroup(QLatin1String( "" )) // no null groups 0092 {} 0093 0094 ItemArray::~ItemArray() 0095 { 0096 for (int i=0; i<size(); i++) delete at(i); 0097 } 0098 0099 int ItemArray::findIndex(const QString &name) const 0100 { 0101 for (int i=0; i<size(); i++) 0102 if ( at(i)->name()==name ) return i; 0103 return -1; 0104 } 0105 0106 const ItemContainer *ItemArray::item(const QString &name) const 0107 { 0108 QLoggingCategory::setFilterRules(QStringLiteral("games.highscore.debug = true")); 0109 0110 int i = findIndex(name); 0111 if ( i==-1 ) qCCritical(GAMES_EXTHIGHSCORE) << "no item named \"" << name 0112 << "\""; 0113 return at(i); 0114 } 0115 0116 ItemContainer *ItemArray::item(const QString &name) 0117 { 0118 QLoggingCategory::setFilterRules(QStringLiteral("games.highscore.debug = true")); 0119 0120 int i = findIndex(name); 0121 if ( i==-1 ) qCCritical(GAMES_EXTHIGHSCORE) << "no item named \"" << name 0122 << "\""; 0123 return at(i); 0124 } 0125 0126 void ItemArray::setItem(const QString &name, Item *item) 0127 { 0128 QLoggingCategory::setFilterRules(QStringLiteral("games.highscore.debug = true")); 0129 0130 int i = findIndex(name); 0131 if ( i==-1 ) qCCritical(GAMES_EXTHIGHSCORE) << "no item named \"" << name 0132 << "\""; 0133 bool stored = at(i)->isStored(); 0134 bool canHaveSubGroup = at(i)->canHaveSubGroup(); 0135 _setItem(i, name, item, stored, canHaveSubGroup); 0136 } 0137 0138 void ItemArray::addItem(const QString &name, Item *item, 0139 bool stored, bool canHaveSubGroup) 0140 { 0141 QLoggingCategory::setFilterRules(QStringLiteral("games.highscore.debug = true")); 0142 0143 if ( findIndex(name)!=-1 ) 0144 qCCritical(GAMES_EXTHIGHSCORE) << "item already exists \"" << name << "\""; 0145 0146 append(new ItemContainer); 0147 //at(i) = new ItemContainer; 0148 _setItem(size()-1, name, item, stored, canHaveSubGroup); 0149 } 0150 0151 void ItemArray::_setItem(uint i, const QString &name, Item *item, 0152 bool stored, bool canHaveSubGroup) 0153 { 0154 at(i)->setItem(item); 0155 at(i)->setName(name); 0156 at(i)->setGroup(stored ? _group : QString()); 0157 at(i)->setSubGroup(canHaveSubGroup ? _subGroup : QString()); 0158 } 0159 0160 void ItemArray::setGroup(const QString &group) 0161 { 0162 Q_ASSERT( !group.isNull() ); 0163 _group = group; 0164 for (int i=0; i<size(); i++) 0165 if ( at(i)->isStored() ) at(i)->setGroup(group); 0166 } 0167 0168 void ItemArray::setSubGroup(const QString &subGroup) 0169 { 0170 Q_ASSERT( !subGroup.isNull() ); 0171 _subGroup = subGroup; 0172 for (int i=0; i<size(); i++) 0173 if ( at(i)->canHaveSubGroup() ) at(i)->setSubGroup(subGroup); 0174 } 0175 0176 void ItemArray::read(uint k, Score &data) const 0177 { 0178 for (int i=0; i<size(); i++) { 0179 if ( !at(i)->isStored() ) continue; 0180 data.setData(at(i)->name(), at(i)->read(k)); 0181 } 0182 } 0183 0184 void ItemArray::write(uint k, const Score &data, uint nb) const 0185 { 0186 for (int i=0; i<size(); i++) { 0187 if ( !at(i)->isStored() ) continue; 0188 for (uint j=nb-1; j>k; j--) at(i)->write(j, at(i)->read(j-1)); 0189 at(i)->write(k, data.data(at(i)->name())); 0190 } 0191 } 0192 0193 void ItemArray::exportToText(QTextStream &s) const 0194 { 0195 for (uint k=0; k<nbEntries()+1; k++) { 0196 for (int i=0; i<size(); i++) { 0197 const Item *item = at(i)->item(); 0198 if ( item->isVisible() ) { 0199 if ( i!=0 ) s << '\t'; 0200 if ( k==0 ) s << item->label(); 0201 else s << at(i)->pretty(k-1); 0202 } 0203 } 0204 s << Qt::endl; 0205 } 0206 } 0207 0208 //----------------------------------------------------------------------------- 0209 class ScoreNameItem : public NameItem 0210 { 0211 public: 0212 ScoreNameItem(const ScoreInfos &score, const PlayerInfos &infos) 0213 : _score(score), _infos(infos) {} 0214 0215 QString pretty(uint i, const QVariant &v) const override { 0216 uint id = _score.item(QStringLiteral( "id" ))->read(i).toUInt(); 0217 if ( id==0 ) return NameItem::pretty(i, v); 0218 return _infos.prettyName(id-1); 0219 } 0220 0221 private: 0222 const ScoreInfos &_score; 0223 const PlayerInfos &_infos; 0224 }; 0225 0226 //----------------------------------------------------------------------------- 0227 ScoreInfos::ScoreInfos(uint maxNbEntries, const PlayerInfos &infos) 0228 : _maxNbEntries(maxNbEntries) 0229 { 0230 addItem(QStringLiteral( "id" ), new Item((uint)0)); 0231 addItem(QStringLiteral( "rank" ), new RankItem, false); 0232 addItem(QStringLiteral( "name" ), new ScoreNameItem(*this, infos)); 0233 addItem(QStringLiteral( "score" ), Manager::createItem(Manager::ScoreDefault)); 0234 addItem(QStringLiteral( "date" ), new DateItem); 0235 } 0236 0237 uint ScoreInfos::nbEntries() const 0238 { 0239 uint i = 0; 0240 for (; i<_maxNbEntries; i++) 0241 if ( item(QStringLiteral( "score" ))->read(i)==item(QStringLiteral( "score" ))->item()->defaultValue() ) 0242 break; 0243 return i; 0244 } 0245 0246 //----------------------------------------------------------------------------- 0247 const char *HS_ID = "player id"; 0248 const char *HS_REGISTERED_NAME = "registered name"; 0249 const char *HS_KEY = "player key"; 0250 const char *HS_WW_ENABLED = "ww hs enabled"; 0251 0252 PlayerInfos::PlayerInfos() 0253 { 0254 setGroup(QStringLiteral( "players" )); 0255 0256 // standard items 0257 addItem(QStringLiteral( "name" ), new NameItem); 0258 Item *it = new Item((uint)0, i18n("Games Count"),Qt::AlignRight); 0259 addItem(QStringLiteral( "nb games" ), it, true, true); 0260 it = Manager::createItem(Manager::MeanScoreDefault); 0261 addItem(QStringLiteral( "mean score" ), it, true, true); 0262 it = Manager::createItem(Manager::BestScoreDefault); 0263 addItem(QStringLiteral( "best score" ), it, true, true); 0264 addItem(QStringLiteral( "date" ), new DateItem, true, true); 0265 it = new Item(QString(), i18n("Comment"), Qt::AlignLeft); 0266 addItem(QStringLiteral( "comment" ), it); 0267 0268 // statistics items 0269 addItem(QStringLiteral( "nb black marks" ), new Item((uint)0), true, true); // legacy 0270 addItem(QStringLiteral( "nb lost games" ), new Item((uint)0), true, true); 0271 addItem(QStringLiteral( "nb draw games" ), new Item((uint)0), true, true); 0272 addItem(QStringLiteral( "current trend" ), new Item((int)0), true, true); 0273 addItem(QStringLiteral( "max lost trend" ), new Item((uint)0), true, true); 0274 addItem(QStringLiteral( "max won trend" ), new Item((uint)0), true, true); 0275 0276 QString username = KUser().loginName(); 0277 0278 #ifdef HIGHSCORE_DIRECTORY 0279 internal->hsConfig().setHighscoreGroup("players"); 0280 for (uint i=0; ;i++) { 0281 if ( !internal->hsConfig().hasEntry(i+1, "username") ) { 0282 _newPlayer = true; 0283 _id = i; 0284 break; 0285 } 0286 if ( internal->hsConfig().readEntry(i+1, "username")==username ) { 0287 _newPlayer = false; 0288 _id = i; 0289 return; 0290 } 0291 } 0292 #endif 0293 internal->hsConfig().lockForWriting(); 0294 KEMailSettings emailConfig; 0295 emailConfig.setProfile(emailConfig.defaultProfileName()); 0296 QString name = emailConfig.getSetting(KEMailSettings::RealName); 0297 if ( name.isEmpty() || isNameUsed(name) ) name = username; 0298 if ( isNameUsed(name) ) name= QLatin1String(ItemContainer::ANONYMOUS); 0299 #ifdef HIGHSCORE_DIRECTORY 0300 internal->hsConfig().writeEntry(_id+1, "username", username); 0301 item("name")->write(_id, name); 0302 #endif 0303 0304 ConfigGroup cg; 0305 _oldLocalPlayer = cg.hasKey(HS_ID); 0306 _oldLocalId = cg.readEntry(HS_ID).toUInt(); 0307 #ifdef HIGHSCORE_DIRECTORY 0308 if (_oldLocalPlayer) { // player already exists in local config file 0309 // copy player data 0310 QString prefix = QString::fromLatin1( "%1_").arg(_oldLocalId+1); 0311 #ifdef __GNUC__ 0312 #warning "kde4 port g.config()->entryMap"; 0313 #endif 0314 #if 0 0315 QMap<QString, QString> entries = 0316 cg.config()->entryMap("KHighscore_players"); 0317 QMap<QString, QString>::const_iterator it; 0318 for (it=entries.begin(); it!=entries.end(); ++it) { 0319 QString key = it.key(); 0320 if ( key.find(prefix)==0 ) { 0321 QString name = key.right(key.length()-prefix.length()); 0322 if ( name!="name" || !isNameUsed(it.data()) ) 0323 internal->hsConfig().writeEntry(_id+1, name, it.data()); 0324 } 0325 } 0326 #endif 0327 } 0328 #else 0329 _newPlayer = !_oldLocalPlayer; 0330 if (_oldLocalPlayer) _id = _oldLocalId; 0331 else { 0332 _id = nbEntries(); 0333 cg.writeEntry(HS_ID, _id); 0334 item(QStringLiteral( "name" ))->write(_id, name); 0335 } 0336 #endif 0337 _bound = true; 0338 internal->hsConfig().writeAndUnlock(); 0339 } 0340 0341 void PlayerInfos::createHistoItems(const QList<uint> &scores, bool bound) 0342 { 0343 Q_ASSERT( _histogram.size()==0 ); 0344 _bound = bound; 0345 _histogram = scores; 0346 for (int i=1; i<histoSize(); i++) 0347 addItem(histoName(i), new Item((uint)0), true, true); 0348 } 0349 0350 bool PlayerInfos::isAnonymous() const 0351 { 0352 return ( name()==QLatin1String( ItemContainer::ANONYMOUS ) ); 0353 } 0354 0355 uint PlayerInfos::nbEntries() const 0356 { 0357 internal->hsConfig().setHighscoreGroup(QStringLiteral( "players" )); 0358 const QStringList list = internal->hsConfig().readList(QStringLiteral( "name" ), -1); 0359 return list.count(); 0360 } 0361 0362 QString PlayerInfos::key() const 0363 { 0364 ConfigGroup cg; 0365 return cg.readEntry(HS_KEY, QString()); 0366 } 0367 0368 bool PlayerInfos::isWWEnabled() const 0369 { 0370 ConfigGroup cg; 0371 return cg.readEntry(HS_WW_ENABLED, false); 0372 } 0373 0374 QString PlayerInfos::histoName(int i) const 0375 { 0376 const QList<uint> &sh = _histogram; 0377 Q_ASSERT( i<sh.size() || (_bound || i==sh.size()) ); 0378 if ( i==sh.size() ) 0379 return QStringLiteral( "nb scores greater than %1").arg(sh[sh.size()-1]); 0380 return QStringLiteral( "nb scores less than %1").arg(sh[i]); 0381 } 0382 0383 int PlayerInfos::histoSize() const 0384 { 0385 return _histogram.size() + (_bound ? 0 : 1); 0386 } 0387 0388 void PlayerInfos::submitScore(const Score &score) const 0389 { 0390 // update counts 0391 uint nbGames = item(QStringLiteral( "nb games" ))->increment(_id); 0392 switch (score.type()) { 0393 case Lost: 0394 item(QStringLiteral( "nb lost games" ))->increment(_id); 0395 break; 0396 case Won: break; 0397 case Draw: 0398 item(QStringLiteral( "nb draw games" ))->increment(_id); 0399 break; 0400 }; 0401 0402 // update mean 0403 if ( score.type()==Won ) { 0404 uint nbWonGames = nbGames - item(QStringLiteral( "nb lost games" ))->read(_id).toUInt() 0405 - item(QStringLiteral( "nb draw games" ))->read(_id).toUInt() 0406 - item(QStringLiteral( "nb black marks" ))->read(_id).toUInt(); // legacy 0407 double mean = (nbWonGames==1 ? 0.0 0408 : item(QStringLiteral( "mean score" ))->read(_id).toDouble()); 0409 mean += (double(score.score()) - mean) / nbWonGames; 0410 item(QStringLiteral( "mean score" ))->write(_id, mean); 0411 } 0412 0413 // update best score 0414 Score best = score; // copy optional fields (there are not taken into account here) 0415 best.setScore( item(QStringLiteral( "best score" ))->read(_id).toUInt() ); 0416 if ( best<score ) { 0417 item(QStringLiteral( "best score" ))->write(_id, score.score()); 0418 item(QStringLiteral( "date" ))->write(_id, score.data(QStringLiteral( "date" )).toDateTime()); 0419 } 0420 0421 // update trends 0422 int current = item(QStringLiteral( "current trend" ))->read(_id).toInt(); 0423 switch (score.type()) { 0424 case Won: { 0425 if ( current<0 ) current = 0; 0426 current++; 0427 uint won = item(QStringLiteral( "max won trend" ))->read(_id).toUInt(); 0428 if ( (uint)current>won ) item(QStringLiteral( "max won trend" ))->write(_id, current); 0429 break; 0430 } 0431 case Lost: { 0432 if ( current>0 ) current = 0; 0433 current--; 0434 uint lost = item(QStringLiteral( "max lost trend" ))->read(_id).toUInt(); 0435 uint clost = -current; 0436 if ( clost>lost ) item(QStringLiteral( "max lost trend" ))->write(_id, clost); 0437 break; 0438 } 0439 case Draw: 0440 current = 0; 0441 break; 0442 } 0443 item(QStringLiteral( "current trend" ))->write(_id, current); 0444 0445 // update histogram 0446 if ( score.type()==Won ) { 0447 const QList<uint> &sh = _histogram; 0448 for (int i=1; i<histoSize(); i++) 0449 if ( i==sh.size() || score.score()<sh[i] ) { 0450 item(histoName(i))->increment(_id); 0451 break; 0452 } 0453 } 0454 } 0455 0456 bool PlayerInfos::isNameUsed(const QString &newName) const 0457 { 0458 if ( newName==name() ) return false; // own name... 0459 for (uint i=0; i<nbEntries(); i++) 0460 if ( newName.toLower()==item(QStringLiteral( "name" ))->read(i).toString().toLower() ) return true; 0461 if ( newName==ItemContainer::ANONYMOUS_LABEL.toString() ) return true; 0462 return false; 0463 } 0464 0465 void PlayerInfos::modifyName(const QString &newName) const 0466 { 0467 item(QStringLiteral( "name" ))->write(_id, newName); 0468 } 0469 0470 void PlayerInfos::modifySettings(const QString &newName, 0471 const QString &comment, bool WWEnabled, 0472 const QString &newKey) const 0473 { 0474 modifyName(newName); 0475 item(QStringLiteral( "comment" ))->write(_id, comment); 0476 ConfigGroup cg; 0477 cg.writeEntry(HS_WW_ENABLED, WWEnabled); 0478 if ( !newKey.isEmpty() ) cg.writeEntry(HS_KEY, newKey); 0479 if (WWEnabled) cg.writeEntry(HS_REGISTERED_NAME, newName); 0480 } 0481 0482 QString PlayerInfos::registeredName() const 0483 { 0484 ConfigGroup cg; 0485 return cg.readEntry(HS_REGISTERED_NAME, QString()); 0486 } 0487 0488 void PlayerInfos::removeKey() 0489 { 0490 ConfigGroup cg; 0491 0492 // save old key/nickname 0493 uint i = 0; 0494 QString str = QStringLiteral( "%1 old #%2" ); 0495 QString sk; 0496 do { 0497 i++; 0498 sk = str.arg(QLatin1String( HS_KEY )).arg(i); 0499 } while ( !cg.readEntry(sk, QString()).isEmpty() ); 0500 cg.writeEntry(sk, key()); 0501 cg.writeEntry(str.arg(QLatin1String( HS_REGISTERED_NAME )).arg(i), 0502 registeredName()); 0503 0504 // clear current key/nickname 0505 cg.deleteEntry(HS_KEY); 0506 cg.deleteEntry(HS_REGISTERED_NAME); 0507 cg.writeEntry(HS_WW_ENABLED, false); 0508 } 0509 0510 //----------------------------------------------------------------------------- 0511 ManagerPrivate::ManagerPrivate(uint nbGameTypes, Manager &m) 0512 : manager(m), showStatistics(false), showDrawGames(false), 0513 trackLostGames(false), trackDrawGames(false), 0514 showMode(Manager::ShowForHigherScore), 0515 _first(true), _nbGameTypes(nbGameTypes), _gameType(0) 0516 {} 0517 0518 void ManagerPrivate::init(uint maxNbEntries) 0519 { 0520 _hsConfig = new KGameHighscore(false, nullptr); 0521 _playerInfos = new PlayerInfos; 0522 _scoreInfos = new ScoreInfos(maxNbEntries, *_playerInfos); 0523 } 0524 0525 ManagerPrivate::~ManagerPrivate() 0526 { 0527 delete _scoreInfos; 0528 delete _playerInfos; 0529 delete _hsConfig; 0530 } 0531 0532 QUrl ManagerPrivate::queryUrl(QueryType type, const QString &newName) const 0533 { 0534 QUrl url = serverURL; 0535 QString nameItem = QStringLiteral( "nickname" ); 0536 QString name = _playerInfos->registeredName(); 0537 bool withVersion = true; 0538 bool key = false; 0539 bool level = false; 0540 0541 switch (type) { 0542 case Submit: 0543 url = url.adjusted(QUrl::StripTrailingSlash); 0544 url.setPath(url.path() + QLatin1Char('/') + QStringLiteral( "submit.php" )); 0545 level = true; 0546 key = true; 0547 break; 0548 case Register: 0549 url = url.adjusted(QUrl::StripTrailingSlash); 0550 url.setPath(url.path() + QLatin1Char('/') + QStringLiteral( "register.php" )); 0551 name = newName; 0552 break; 0553 case Change: 0554 url = url.adjusted(QUrl::StripTrailingSlash); 0555 url.setPath(url.path() + QLatin1Char('/') + QStringLiteral( "change.php" )); 0556 key = true; 0557 if ( newName!=name ) 0558 Manager::addToQueryURL(url, QStringLiteral( "new_nickname" ), newName); 0559 break; 0560 case Players: 0561 url = url.adjusted(QUrl::StripTrailingSlash); 0562 url.setPath(url.path() + QLatin1Char('/') + QStringLiteral( "players.php" )); 0563 nameItem = QStringLiteral( "highlight" ); 0564 withVersion = false; 0565 break; 0566 case Scores: 0567 url = url.adjusted(QUrl::StripTrailingSlash); 0568 url.setPath(url.path() + QLatin1Char('/') + QStringLiteral( "highscores.php" )); 0569 withVersion = false; 0570 if ( _nbGameTypes>1 ) level = true; 0571 break; 0572 } 0573 0574 if (withVersion) Manager::addToQueryURL(url, QStringLiteral( "version" ), version); 0575 if ( !name.isEmpty() ) Manager::addToQueryURL(url, nameItem, name); 0576 if (key) Manager::addToQueryURL(url, QStringLiteral( "key" ), _playerInfos->key()); 0577 if (level) { 0578 QString label = manager.gameTypeLabel(_gameType, Manager::WW); 0579 if ( !label.isEmpty() ) Manager::addToQueryURL(url, QStringLiteral( "level" ), label); 0580 } 0581 0582 return url; 0583 } 0584 0585 // strings that needs to be translated (coming from the highscores server) 0586 const KLazyLocalizedString DUMMY_STRINGS[] = { 0587 kli18n("Undefined error."), 0588 kli18n("Missing argument(s)."), 0589 kli18n("Invalid argument(s)."), 0590 0591 kli18n("Unable to connect to MySQL server."), 0592 kli18n("Unable to select database."), 0593 kli18n("Error on database query."), 0594 kli18n("Error on database insert."), 0595 0596 kli18n("Nickname already registered."), 0597 kli18n("Nickname not registered."), 0598 kli18n("Invalid key."), 0599 kli18n("Invalid submit key."), 0600 0601 kli18n("Invalid level."), 0602 kli18n("Invalid score.") 0603 }; 0604 0605 const KLazyLocalizedString UNABLE_TO_CONTACT = 0606 kli18n("Unable to contact world-wide highscore server"); 0607 0608 bool ManagerPrivate::doQuery(const QUrl &url, QWidget *parent, 0609 QDomNamedNodeMap *map) 0610 { 0611 KIO::http_update_cache(url, true, QDateTime::fromSecsSinceEpoch(0)); // remove cache ! 0612 0613 QTemporaryFile tmpFile; 0614 if ( !tmpFile.open() ) { 0615 QString details = i18n("Unable to open temporary file."); 0616 KMessageBox::detailedError(parent, UNABLE_TO_CONTACT.toString(), details); 0617 return false; 0618 } 0619 0620 auto copyJob = KIO::file_copy(url, QUrl::fromLocalFile(tmpFile.fileName())); 0621 KJobWidgets::setWindow(copyJob, parent); 0622 copyJob->exec(); 0623 if( copyJob->error() ) { 0624 QString details = i18n("Server URL: %1", url.host()); 0625 KMessageBox::detailedError(parent, UNABLE_TO_CONTACT.toString(), details); 0626 return false; 0627 } 0628 0629 QTextStream t(&tmpFile); 0630 QString content = t.readAll().trimmed(); 0631 tmpFile.close(); 0632 0633 QDomDocument doc; 0634 if ( doc.setContent(content) ) { 0635 QDomElement root = doc.documentElement(); 0636 QDomElement element = root.firstChild().toElement(); 0637 if ( element.tagName()==QLatin1String( "success" ) ) { 0638 if (map) *map = element.attributes(); 0639 return true; 0640 } 0641 if ( element.tagName()==QLatin1String( "error" ) ) { 0642 QDomAttr attr = element.attributes().namedItem(QStringLiteral( "label" )).toAttr(); 0643 if ( !attr.isNull() ) { 0644 // see DUMMY_STRINGS 0645 QString msg = i18n(attr.value().toLatin1().constData()); 0646 QString caption = i18n("Message from world-wide highscores " 0647 "server"); 0648 KMessageBox::error(parent, msg, caption); 0649 return false; 0650 } 0651 } 0652 } 0653 QString msg = i18n("Invalid answer from world-wide highscores server."); 0654 QString details = i18n("Raw message: %1", content); 0655 KMessageBox::detailedError(parent, msg, details); 0656 return false; 0657 } 0658 0659 bool ManagerPrivate::getFromQuery(const QDomNamedNodeMap &map, 0660 const QString &name, QString &value, 0661 QWidget *parent) 0662 { 0663 QDomAttr attr = map.namedItem(name).toAttr(); 0664 if ( attr.isNull() ) { 0665 KMessageBox::error(parent, 0666 i18n("Invalid answer from world-wide " 0667 "highscores server (missing item: %1).", name)); 0668 return false; 0669 } 0670 value = attr.value(); 0671 return true; 0672 } 0673 0674 Score ManagerPrivate::readScore(uint i) const 0675 { 0676 Score score(Won); 0677 _scoreInfos->read(i, score); 0678 return score; 0679 } 0680 0681 int ManagerPrivate::rank(const Score &score) const 0682 { 0683 uint nb = _scoreInfos->nbEntries(); 0684 uint i = 0; 0685 for (; i<nb; i++) 0686 if ( readScore(i)<score ) break; 0687 return (i<_scoreInfos->maxNbEntries() ? (int)i : -1); 0688 } 0689 0690 bool ManagerPrivate::modifySettings(const QString &newName, 0691 const QString &comment, bool WWEnabled, 0692 QWidget *widget) 0693 { 0694 QString newKey; 0695 bool newPlayer = false; 0696 0697 if (WWEnabled) { 0698 newPlayer = _playerInfos->key().isEmpty() 0699 || _playerInfos->registeredName().isEmpty(); 0700 QUrl url = queryUrl((newPlayer ? Register : Change), newName); 0701 Manager::addToQueryURL(url, QStringLiteral( "comment" ), comment); 0702 0703 QDomNamedNodeMap map; 0704 bool ok = doQuery(url, widget, &map); 0705 if ( !ok || (newPlayer && !getFromQuery(map, QStringLiteral( "key" ), newKey, widget)) ) 0706 return false; 0707 } 0708 0709 bool ok = _hsConfig->lockForWriting(widget); // no GUI when locking 0710 if (ok) { 0711 // check again name in case the config file has been changed... 0712 // if it has, it is unfortunate because the WWW name is already 0713 // committed but should be very rare and not really problematic 0714 ok = ( !_playerInfos->isNameUsed(newName) ); 0715 if (ok) 0716 _playerInfos->modifySettings(newName, comment, WWEnabled, newKey); 0717 _hsConfig->writeAndUnlock(); 0718 } 0719 return ok; 0720 } 0721 0722 void ManagerPrivate::convertToGlobal() 0723 { 0724 // read old highscores 0725 KGameHighscore *tmp = _hsConfig; 0726 _hsConfig = new KGameHighscore(true, nullptr); 0727 QList<Score> scores(_scoreInfos->nbEntries()); 0728 for (int i=0; i<scores.count(); i++) 0729 scores[i] = readScore(i); 0730 0731 // commit them 0732 delete _hsConfig; 0733 _hsConfig = tmp; 0734 _hsConfig->lockForWriting(); 0735 for (int i=0; i<scores.count(); i++) 0736 if ( scores[i].data(QStringLiteral( "id" )).toUInt()==_playerInfos->oldLocalId()+1 ) 0737 submitLocal(scores[i]); 0738 _hsConfig->writeAndUnlock(); 0739 } 0740 0741 void ManagerPrivate::setGameType(uint type) 0742 { 0743 if (_first) { 0744 _first = false; 0745 if ( _playerInfos->isNewPlayer() ) { 0746 // convert legacy highscores 0747 for (uint i=0; i<_nbGameTypes; i++) { 0748 setGameType(i); 0749 manager.convertLegacy(i); 0750 } 0751 0752 #ifdef HIGHSCORE_DIRECTORY 0753 if ( _playerInfos->isOldLocalPlayer() ) { 0754 // convert local to global highscores 0755 for (uint i=0; i<_nbGameTypes; i++) { 0756 setGameType(i); 0757 convertToGlobal(); 0758 } 0759 } 0760 #endif 0761 } 0762 } 0763 0764 Q_ASSERT( type<_nbGameTypes ); 0765 _gameType = qMin(type, _nbGameTypes-1); 0766 QString str = QStringLiteral( "scores" ); 0767 QString lab = manager.gameTypeLabel(_gameType, Manager::Standard); 0768 if ( !lab.isEmpty() ) { 0769 _playerInfos->setSubGroup(lab); 0770 str += QLatin1Char( '_' ) + lab; 0771 } 0772 _scoreInfos->setGroup(str); 0773 } 0774 0775 void ManagerPrivate::checkFirst() 0776 { 0777 if (_first) setGameType(0); 0778 } 0779 0780 int ManagerPrivate::submitScore(const Score &ascore, 0781 QWidget *widget, bool askIfAnonymous) 0782 { 0783 checkFirst(); 0784 0785 Score score = ascore; 0786 score.setData(QStringLiteral( "id" ), _playerInfos->id() + 1); 0787 score.setData(QStringLiteral( "date" ), QDateTime::currentDateTime()); 0788 0789 // ask new name if anonymous and winner 0790 const QLatin1String dontAskAgainName = QLatin1String( "highscore_ask_name_dialog" ); 0791 QString newName; 0792 KMessageBox::ButtonCode dummy; 0793 if ( score.type()==Won && askIfAnonymous && _playerInfos->isAnonymous() 0794 && KMessageBox::shouldBeShownTwoActions(dontAskAgainName, dummy) ) { 0795 AskNameDialog d(widget); 0796 if ( d.exec()==QDialog::Accepted ) newName = d.name(); 0797 if ( d.dontAskAgain() ) 0798 KMessageBox::saveDontShowAgainTwoActions(dontAskAgainName, 0799 KMessageBox::SecondaryAction); 0800 } 0801 0802 int rank = -1; 0803 if ( _hsConfig->lockForWriting(widget) ) { // no GUI when locking 0804 // check again new name in case the config file has been changed... 0805 if ( !newName.isEmpty() && !_playerInfos->isNameUsed(newName) ) 0806 _playerInfos->modifyName(newName); 0807 0808 // commit locally 0809 _playerInfos->submitScore(score); 0810 if ( score.type()==Won ) rank = submitLocal(score); 0811 _hsConfig->writeAndUnlock(); 0812 } 0813 0814 if ( _playerInfos->isWWEnabled() ) 0815 submitWorldWide(score, widget); 0816 0817 return rank; 0818 } 0819 0820 int ManagerPrivate::submitLocal(const Score &score) 0821 { 0822 int r = rank(score); 0823 if ( r!=-1 ) { 0824 uint nb = _scoreInfos->nbEntries(); 0825 if ( nb<_scoreInfos->maxNbEntries() ) nb++; 0826 _scoreInfos->write(r, score, nb); 0827 } 0828 return r; 0829 } 0830 0831 bool ManagerPrivate::submitWorldWide(const Score &score, 0832 QWidget *widget) const 0833 { 0834 if ( score.type()==Lost && !trackLostGames ) return true; 0835 if ( score.type()==Draw && !trackDrawGames ) return true; 0836 0837 QUrl url = queryUrl(Submit); 0838 manager.additionalQueryItems(url, score); 0839 int s = (score.type()==Won ? score.score() : (int)score.type()); 0840 QString str = QString::number(s); 0841 Manager::addToQueryURL(url, QStringLiteral( "score" ), str); 0842 QCryptographicHash context(QCryptographicHash::Md5); 0843 context.addData(QString(_playerInfos->registeredName() + str).toLatin1()); 0844 Manager::addToQueryURL(url, QStringLiteral( "check" ), QLatin1String( context.result().toHex() )); 0845 0846 return doQuery(url, widget); 0847 } 0848 0849 void ManagerPrivate::exportHighscores(QTextStream &s) 0850 { 0851 uint tmp = _gameType; 0852 0853 for (uint i=0; i<_nbGameTypes; i++) { 0854 setGameType(i); 0855 if ( _nbGameTypes>1 ) { 0856 if ( i!=0 ) s << Qt::endl; 0857 s << "--------------------------------" << Qt::endl; 0858 s << "Game type: " 0859 << manager.gameTypeLabel(_gameType, Manager::I18N) << Qt::endl; 0860 s << Qt::endl; 0861 } 0862 s << "Players list:" << Qt::endl; 0863 _playerInfos->exportToText(s); 0864 s << Qt::endl; 0865 s << "Highscores list:" << Qt::endl; 0866 _scoreInfos->exportToText(s); 0867 } 0868 0869 setGameType(tmp); 0870 } 0871 0872 } // namespace