#include "servatrice.h" #include "servatrice_database_interface.h" #include "passwordhasher.h" #include "serversocketinterface.h" #include "settingscache.h" #include "decklist.h" #include "pb/game_replay.pb.h" #include #include #include #include #include Servatrice_DatabaseInterface::Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server) : instanceId(_instanceId), sqlDatabase(QSqlDatabase()), server(_server) { } Servatrice_DatabaseInterface::~Servatrice_DatabaseInterface() { // reset all prepared statements qDeleteAll(preparedStatements); preparedStatements.clear(); sqlDatabase.close(); } void Servatrice_DatabaseInterface::initDatabase(const QSqlDatabase &_sqlDatabase) { if (_sqlDatabase.isValid()) { sqlDatabase = QSqlDatabase::cloneDatabase(_sqlDatabase, "pool_" + QString::number(instanceId)); openDatabase(); } } bool Servatrice_DatabaseInterface::initDatabase(const QString &type, const QString &hostName, const QString &databaseName, const QString &userName, const QString &password) { sqlDatabase = QSqlDatabase::addDatabase(type, "main"); sqlDatabase.setHostName(hostName); sqlDatabase.setDatabaseName(databaseName); sqlDatabase.setUserName(userName); sqlDatabase.setPassword(password); return openDatabase(); } bool Servatrice_DatabaseInterface::openDatabase() { if (sqlDatabase.isOpen()) sqlDatabase.close(); const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId); qDebug() << QString("[%1] Opening database...").arg(poolStr); if (!sqlDatabase.open()) { qCritical() << QString("[%1] Error opening database: %2").arg(poolStr).arg(sqlDatabase.lastError().text()); return false; } QSqlQuery *versionQuery = prepareQuery("select version from {prefix}_schema_version limit 1"); if (!execSqlQuery(versionQuery)) { qCritical() << QString("[%1] Error opening database: unable to load database schema version (hint: ensure the cockatrice_schema_version exists)").arg(poolStr); return false; } if (versionQuery->next()) { const int dbversion = versionQuery->value(0).toInt(); const int expectedversion = DATABASE_SCHEMA_VERSION; if(dbversion != expectedversion) { qCritical() << QString("[%1] Error opening database: the database schema version is too old, you need to run the migrations to update it from version %2 to version %3").arg(poolStr).arg(dbversion).arg(expectedversion); return false; } } else { qCritical() << QString("[%1] Error opening database: unable to load database schema version (hint: ensure the cockatrice_schema_version contains a single record)").arg(poolStr); return false; } // reset all prepared statements qDeleteAll(preparedStatements); preparedStatements.clear(); return true; } bool Servatrice_DatabaseInterface::checkSql() { if (!sqlDatabase.isValid()) return false; if (!sqlDatabase.exec("select 1").isActive()) return openDatabase(); return true; } QSqlQuery * Servatrice_DatabaseInterface::prepareQuery(const QString &queryText) { if(preparedStatements.contains(queryText)) return preparedStatements.value(queryText); QString prefixedQueryText = queryText; prefixedQueryText.replace("{prefix}", server->getDbPrefix()); QSqlQuery * query = new QSqlQuery(sqlDatabase); query->prepare(prefixedQueryText); preparedStatements.insert(queryText, query); return query; } bool Servatrice_DatabaseInterface::execSqlQuery(QSqlQuery *query) { if (query->exec()) return true; const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId); qCritical() << QString("[%1] Error executing query: %2").arg(poolStr).arg(query->lastError().text()); return false; } bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString & error) { int minNameLength = settingsCache->value("users/minnamelength", 6).toInt(); int maxNameLength = settingsCache->value("users/maxnamelength", 12).toInt(); bool allowLowercase = settingsCache->value("users/allowlowercase", true).toBool(); bool allowUppercase = settingsCache->value("users/allowuppercase", true).toBool(); bool allowNumerics = settingsCache->value("users/allownumerics", true).toBool(); bool allowPunctuationPrefix = settingsCache->value("users/allowpunctuationprefix", false).toBool(); QString allowedPunctuation = settingsCache->value("users/allowedpunctuation", "_").toString(); error = QString("%1|%2|%3|%4|%5|%6|%7").arg(minNameLength).arg(maxNameLength).arg(allowLowercase).arg(allowUppercase).arg(allowNumerics).arg(allowPunctuationPrefix).arg(allowedPunctuation); if (user.length() < minNameLength || user.length() > maxNameLength) return false; if (!allowPunctuationPrefix && allowedPunctuation.contains(user.at(0))) return false; QString regEx("["); if (allowLowercase) regEx.append("a-z"); if (allowUppercase) regEx.append("A-Z"); if(allowNumerics) regEx.append("0-9"); regEx.append(QRegExp::escape(allowedPunctuation)); regEx.append("]+"); static QRegExp re = QRegExp(regEx); return re.exactMatch(user); } // TODO move this to Server bool Servatrice_DatabaseInterface::getRequireRegistration() { return settingsCache->value("authentication/regonly", 0).toBool(); } bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active) { if (!checkSql()) return false; QString passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt()); token = active ? QString() : PasswordHasher::generateActivationToken(); QSqlQuery *query = prepareQuery("insert into {prefix}_users " "(name, realname, gender, password_sha512, email, country, registrationDate, active, token) " "values " "(:userName, :realName, :gender, :password_sha512, :email, :country, UTC_TIMESTAMP(), :active, :token)"); query->bindValue(":userName", userName); query->bindValue(":realName", realName); query->bindValue(":gender", getGenderChar(gender)); query->bindValue(":password_sha512", passwordSha512); query->bindValue(":email", emailAddress); query->bindValue(":country", country); query->bindValue(":active", active ? 1 : 0); query->bindValue(":token", token); if (!execSqlQuery(query)) { qDebug() << "Failed to insert user: " << query->lastError() << " sql: " << query->lastQuery(); return false; } return true; } bool Servatrice_DatabaseInterface::activateUser(const QString &userName, const QString &token) { if (!checkSql()) return false; QSqlQuery *activateQuery = prepareQuery("select name from {prefix}_users where active=0 and name=:username and token=:token"); activateQuery->bindValue(":username", userName); activateQuery->bindValue(":token", token); if (!execSqlQuery(activateQuery)) { qDebug() << "Account activation failed: SQL error." << activateQuery->lastError()<< " sql: " << activateQuery->lastQuery(); return false; } if (activateQuery->next()) { const QString name = activateQuery->value(0).toString(); // redundant check if(name == userName) { QSqlQuery *query = prepareQuery("update {prefix}_users set active=1 where name = :userName"); query->bindValue(":userName", userName); if (!execSqlQuery(query)) { qDebug() << "Failed to activate user: " << query->lastError() << " sql: " << query->lastQuery(); return false; } return true; } } return false; } QChar Servatrice_DatabaseInterface::getGenderChar(ServerInfo_User_Gender const &gender) { switch (gender) { case ServerInfo_User_Gender_GenderUnknown: return QChar('u'); case ServerInfo_User_Gender_Male: return QChar('m'); case ServerInfo_User_Gender_Female: return QChar('f'); default: return QChar('u'); } } AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &banSecondsLeft) { switch (server->getAuthenticationMethod()) { case Servatrice::AuthenticationNone: return UnknownUser; case Servatrice::AuthenticationPassword: { QString configPassword = settingsCache->value("authentication/password").toString(); if (configPassword == password) return PasswordRight; return NotLoggedIn; } case Servatrice::AuthenticationSql: { if (!checkSql()) return UnknownUser; if (!usernameIsValid(user, reasonStr)) return UsernameInvalid; if (checkUserIsBanned(handler->getAddress(), user, reasonStr, banSecondsLeft)) return UserIsBanned; QSqlQuery *passwordQuery = prepareQuery("select password_sha512, active from {prefix}_users where name = :name"); passwordQuery->bindValue(":name", user); if (!execSqlQuery(passwordQuery)) { qDebug("Login denied: SQL error"); return NotLoggedIn; } if (passwordQuery->next()) { const QString correctPassword = passwordQuery->value(0).toString(); const bool userIsActive = passwordQuery->value(1).toBool(); if(!userIsActive) { qDebug("Login denied: user not active"); return UserIsInactive; } if (correctPassword == PasswordHasher::computeHash(password, correctPassword.left(16))) { qDebug("Login accepted: password right"); return PasswordRight; } else { qDebug("Login denied: password wrong"); return NotLoggedIn; } } else { qDebug("Login accepted: unknown user"); return UnknownUser; } } } return UnknownUser; } bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, const QString &userName, QString &banReason, int &banSecondsRemaining) { if (server->getAuthenticationMethod() != Servatrice::AuthenticationSql) return false; if (!checkSql()) { qDebug("Failed to check if user is banned. Database invalid."); return false; } return checkUserIsIpBanned(ipAddress, banReason, banSecondsRemaining) || checkUserIsNameBanned(userName, banReason, banSecondsRemaining); } bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName, QString &banReason, int &banSecondsRemaining) { QSqlQuery *nameBanQuery = prepareQuery("select time_to_sec(timediff(now(), date_add(b.time_from, interval b.minutes minute))), b.minutes <=> 0, b.visible_reason from {prefix}_bans b where b.time_from = (select max(c.time_from) from {prefix}_bans c where c.user_name = :name2) and b.user_name = :name1"); nameBanQuery->bindValue(":name1", userName); nameBanQuery->bindValue(":name2", userName); if (!execSqlQuery(nameBanQuery)) { qDebug() << "Name ban check failed: SQL error" << nameBanQuery->lastError(); return false; } if (nameBanQuery->next()) { const int secondsLeft = -nameBanQuery->value(0).toInt(); const bool permanentBan = nameBanQuery->value(1).toInt(); if ((secondsLeft > 0) || permanentBan) { banReason = nameBanQuery->value(2).toString(); banSecondsRemaining = permanentBan ? 0 : secondsLeft; qDebug() << "Username" << userName << "is banned by name"; return true; } } return false; } bool Servatrice_DatabaseInterface::checkUserIsIpBanned(const QString &ipAddress, QString &banReason, int &banSecondsRemaining) { QSqlQuery *ipBanQuery = prepareQuery( "select" " time_to_sec(timediff(now(), date_add(b.time_from, interval b.minutes minute)))," " b.minutes <=> 0," " b.visible_reason" " from {prefix}_bans b" " where" " b.time_from = (select max(c.time_from)" " from {prefix}_bans c" " where c.ip_address = :address)" " and b.ip_address = :address2"); ipBanQuery->bindValue(":address", ipAddress); ipBanQuery->bindValue(":address2", ipAddress); if (!execSqlQuery(ipBanQuery)) { qDebug() << "IP ban check failed: SQL error." << ipBanQuery->lastError(); return false; } if (ipBanQuery->next()) { const int secondsLeft = -ipBanQuery->value(0).toInt(); const bool permanentBan = ipBanQuery->value(1).toInt(); if ((secondsLeft > 0) || permanentBan) { banReason = ipBanQuery->value(2).toString(); banSecondsRemaining = permanentBan ? 0 : secondsLeft; qDebug() << "User is banned by address" << ipAddress; return true; } } return false; } bool Servatrice_DatabaseInterface::activeUserExists(const QString &user) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { checkSql(); QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name and active = 1"); query->bindValue(":name", user); if (!execSqlQuery(query)) return false; return query->next(); } return false; } bool Servatrice_DatabaseInterface::userExists(const QString &user) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { checkSql(); QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name"); query->bindValue(":name", user); if (!execSqlQuery(query)) return false; return query->next(); } return false; } int Servatrice_DatabaseInterface::getUserIdInDB(const QString &name) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { QSqlQuery *query = prepareQuery("select id from {prefix}_users where name = :name and active = 1"); query->bindValue(":name", name); if (!execSqlQuery(query)) return -1; if (!query->next()) return -1; return query->value(0).toInt(); } return -1; } bool Servatrice_DatabaseInterface::isInBuddyList(const QString &whoseList, const QString &who) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) return false; if (!checkSql()) return false; int id1 = getUserIdInDB(whoseList); int id2 = getUserIdInDB(who); QSqlQuery *query = prepareQuery("select 1 from {prefix}_buddylist where id_user1 = :id_user1 and id_user2 = :id_user2"); query->bindValue(":id_user1", id1); query->bindValue(":id_user2", id2); if (!execSqlQuery(query)) return false; return query->next(); } bool Servatrice_DatabaseInterface::isInIgnoreList(const QString &whoseList, const QString &who) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) return false; if (!checkSql()) return false; int id1 = getUserIdInDB(whoseList); int id2 = getUserIdInDB(who); QSqlQuery *query = prepareQuery("select 1 from {prefix}_ignorelist where id_user1 = :id_user1 and id_user2 = :id_user2"); query->bindValue(":id_user1", id1); query->bindValue(":id_user2", id2); if (!execSqlQuery(query)) return false; return query->next(); } ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuery *query, bool complete, bool withId) { ServerInfo_User result; if (withId) result.set_id(query->value(0).toInt()); result.set_name(query->value(1).toString().toStdString()); const int is_admin = query->value(2).toInt(); int userLevel = ServerInfo_User::IsUser | ServerInfo_User::IsRegistered; if (is_admin == 1) userLevel |= ServerInfo_User::IsAdmin | ServerInfo_User::IsModerator; else if (is_admin == 2) userLevel |= ServerInfo_User::IsModerator; result.set_user_level(userLevel); const QString country = query->value(3).toString(); if (!country.isEmpty()) result.set_country(country.toStdString()); if (complete) { const QString genderStr = query->value(4).toString(); if (genderStr == "m") result.set_gender(ServerInfo_User::Male); else if (genderStr == "f") result.set_gender(ServerInfo_User::Female); const QString realName = query->value(5).toString(); if (!realName.isEmpty()) result.set_real_name(realName.toStdString()); const QByteArray avatarBmp = query->value(6).toByteArray(); if (avatarBmp.size()) result.set_avatar_bmp(avatarBmp.data(), avatarBmp.size()); const QDateTime regDate = query->value(7).toDateTime(); if(!regDate.toString(Qt::ISODate).isEmpty()) { qint64 accountAgeInSeconds = regDate.secsTo(QDateTime::currentDateTime()); result.set_accountage_secs(accountAgeInSeconds); } const QString email = query->value(8).toString(); if (!email.isEmpty()) result.set_email(email.toStdString()); } return result; } ServerInfo_User Servatrice_DatabaseInterface::getUserData(const QString &name, bool withId) { ServerInfo_User result; result.set_name(name.toStdString()); result.set_user_level(ServerInfo_User::IsUser); if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { if (!checkSql()) return result; QSqlQuery *query = prepareQuery("select id, name, admin, country, gender, realname, avatar_bmp, registrationDate, email from {prefix}_users where name = :name and active = 1"); query->bindValue(":name", name); if (!execSqlQuery(query)) return result; if (query->next()) return evalUserQueryResult(query, true, withId); else return result; } else return result; } void Servatrice_DatabaseInterface::clearSessionTables() { lockSessionTables(); QSqlQuery *query = prepareQuery("update {prefix}_sessions set end_time=now() where end_time is null and id_server = :id_server"); query->bindValue(":id_server", server->getServerId()); execSqlQuery(query); unlockSessionTables(); } void Servatrice_DatabaseInterface::lockSessionTables() { QSqlQuery *query = prepareQuery("lock tables {prefix}_sessions write, {prefix}_users read"); execSqlQuery(query); } void Servatrice_DatabaseInterface::unlockSessionTables() { QSqlQuery *query = prepareQuery("unlock tables"); execSqlQuery(query); } bool Servatrice_DatabaseInterface::userSessionExists(const QString &userName) { // Call only after lockSessionTables(). QSqlQuery *query = prepareQuery("select 1 from {prefix}_sessions where user_name = :user_name and id_server = :id_server and end_time is null"); query->bindValue(":id_server", server->getServerId()); query->bindValue(":user_name", userName); execSqlQuery(query); return query->next(); } qint64 Servatrice_DatabaseInterface::startSession(const QString &userName, const QString &address) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) return -1; if (!checkSql()) return -1; QSqlQuery *query = prepareQuery("insert into {prefix}_sessions (user_name, id_server, ip_address, start_time) values(:user_name, :id_server, :ip_address, NOW())"); query->bindValue(":user_name", userName); query->bindValue(":id_server", server->getServerId()); query->bindValue(":ip_address", address); if (execSqlQuery(query)) return query->lastInsertId().toInt(); return -1; } void Servatrice_DatabaseInterface::endSession(qint64 sessionId) { if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone) return; if (!checkSql()) return; QSqlQuery *query = prepareQuery("lock tables {prefix}_sessions write"); execSqlQuery(query); query = prepareQuery("update {prefix}_sessions set end_time=NOW() where id = :id_session"); query->bindValue(":id_session", sessionId); execSqlQuery(query); query = prepareQuery("unlock tables"); execSqlQuery(query); } QMap Servatrice_DatabaseInterface::getBuddyList(const QString &name) { QMap result; if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { checkSql(); QSqlQuery *query = prepareQuery("select a.id, a.name, a.admin, a.country from {prefix}_users a left join {prefix}_buddylist b on a.id = b.id_user2 left join {prefix}_users c on b.id_user1 = c.id where c.name = :name"); query->bindValue(":name", name); if (!execSqlQuery(query)) return result; while (query->next()) { const ServerInfo_User &temp = evalUserQueryResult(query, false); result.insert(QString::fromStdString(temp.name()), temp); } } return result; } QMap Servatrice_DatabaseInterface::getIgnoreList(const QString &name) { QMap result; if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) { checkSql(); QSqlQuery *query = prepareQuery("select a.id, a.name, a.admin, a.country from {prefix}_users a left join {prefix}_ignorelist b on a.id = b.id_user2 left join {prefix}_users c on b.id_user1 = c.id where c.name = :name"); query->bindValue(":name", name); if (!execSqlQuery(query)) return result; while (query->next()) { ServerInfo_User temp = evalUserQueryResult(query, false); result.insert(QString::fromStdString(temp.name()), temp); } } return result; } int Servatrice_DatabaseInterface::getNextGameId() { if (!sqlDatabase.isValid()) return server->getNextLocalGameId(); if (!checkSql()) return -1; QSqlQuery *query = prepareQuery("insert into {prefix}_games (time_started) values (now())"); execSqlQuery(query); return query->lastInsertId().toInt(); } int Servatrice_DatabaseInterface::getNextReplayId() { if (!checkSql()) return -1; QSqlQuery *query = prepareQuery("insert into {prefix}_replays () values ()"); execSqlQuery(query); return query->lastInsertId().toInt(); } void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const QList &replayList) { if (!checkSql()) return; QVariantList gameIds1, playerNames, gameIds2, userIds, replayNames; QSetIterator playerIterator(allPlayersEver); while (playerIterator.hasNext()) { gameIds1.append(gameInfo.game_id()); const QString &playerName = playerIterator.next(); playerNames.append(playerName); } QSet allUsersInGame = allPlayersEver + allSpectatorsEver; QSetIterator allUsersIterator(allUsersInGame); while (allUsersIterator.hasNext()) { int id = getUserIdInDB(allUsersIterator.next()); if (id == -1) continue; gameIds2.append(gameInfo.game_id()); userIds.append(id); replayNames.append(QString::fromStdString(gameInfo.description())); } QVariantList replayIds, replayGameIds, replayDurations, replayBlobs; for (int i = 0; i < replayList.size(); ++i) { QByteArray blob; const unsigned int size = replayList[i]->ByteSize(); blob.resize(size); replayList[i]->SerializeToArray(blob.data(), size); replayIds.append(QVariant((qulonglong) replayList[i]->replay_id())); replayGameIds.append(gameInfo.game_id()); replayDurations.append(replayList[i]->duration_seconds()); replayBlobs.append(blob); } { QSqlQuery *query = prepareQuery("update {prefix}_games set room_name=:room_name, descr=:descr, creator_name=:creator_name, password=:password, game_types=:game_types, player_count=:player_count, time_finished=now() where id=:id_game"); query->bindValue(":room_name", roomName); query->bindValue(":id_game", gameInfo.game_id()); query->bindValue(":descr", QString::fromStdString(gameInfo.description())); query->bindValue(":creator_name", QString::fromStdString(gameInfo.creator_info().name())); query->bindValue(":password", gameInfo.with_password() ? 1 : 0); query->bindValue(":game_types", roomGameTypes.isEmpty() ? QString("") : roomGameTypes.join(", ")); query->bindValue(":player_count", gameInfo.max_players()); if (!execSqlQuery(query)) return; } { QSqlQuery *query = prepareQuery("insert into {prefix}_games_players (id_game, player_name) values (:id_game, :player_name)"); query->bindValue(":id_game", gameIds1); query->bindValue(":player_name", playerNames); query->execBatch(); } { QSqlQuery *query = prepareQuery("update {prefix}_replays set id_game=:id_game, duration=:duration, replay=:replay where id=:id_replay"); query->bindValue(":id_replay", replayIds); query->bindValue(":id_game", replayGameIds); query->bindValue(":duration", replayDurations); query->bindValue(":replay", replayBlobs); query->execBatch(); } { QSqlQuery *query = prepareQuery("insert into {prefix}_replays_access (id_game, id_player, replay_name) values (:id_game, :id_player, :replay_name)"); query->bindValue(":id_game", gameIds2); query->bindValue(":id_player", userIds); query->bindValue(":replay_name", replayNames); query->execBatch(); } } DeckList *Servatrice_DatabaseInterface::getDeckFromDatabase(int deckId, int userId) { checkSql(); QSqlQuery *query = prepareQuery("select content from {prefix}_decklist_files where id = :id and id_user = :id_user"); query->bindValue(":id", deckId); query->bindValue(":id_user", userId); execSqlQuery(query); if (!query->next()) throw Response::RespNameNotFound; DeckList *deck = new DeckList; deck->loadFromString_Native(query->value(0).toString()); return deck; } void Servatrice_DatabaseInterface::logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, LogMessage_TargetType targetType, const int targetId, const QString &targetName) { QString targetTypeString; switch(targetType) { case MessageTargetRoom: if(!settingsCache->value("logging/log_user_msg_room", 0).toBool()) return; targetTypeString = "room"; break; case MessageTargetGame: if(!settingsCache->value("logging/log_user_msg_game", 0).toBool()) return; targetTypeString = "game"; break; case MessageTargetChat: if(!settingsCache->value("logging/log_user_msg_chat", 0).toBool()) return; targetTypeString = "chat"; break; case MessageTargetIslRoom: if(!settingsCache->value("logging/log_user_msg_isl", 0).toBool()) return; targetTypeString = "room"; break; default: return; } QSqlQuery *query = prepareQuery("insert into {prefix}_log (log_time, sender_id, sender_name, sender_ip, log_message, target_type, target_id, target_name) values (now(), :sender_id, :sender_name, :sender_ip, :log_message, :target_type, :target_id, :target_name)"); query->bindValue(":sender_id", senderId < 1 ? QVariant() : senderId); query->bindValue(":sender_name", senderName); query->bindValue(":sender_ip", senderIp); query->bindValue(":log_message", logMessage); query->bindValue(":target_type", targetTypeString); query->bindValue(":target_id", (targetType == MessageTargetChat && targetId < 1) ? QVariant() : targetId); query->bindValue(":target_name", targetName); execSqlQuery(query); } bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword) { if(server->getAuthenticationMethod() != Servatrice::AuthenticationSql) return true; if (!checkSql()) return true; QString error; if (!usernameIsValid(user, error)) return true; QSqlQuery *passwordQuery = prepareQuery("select password_sha512 from {prefix}_users where name = :name"); passwordQuery->bindValue(":name", user); if (!execSqlQuery(passwordQuery)) { qDebug("Change password denied: SQL error"); return true; } if (!passwordQuery->next()) return true; const QString correctPassword = passwordQuery->value(0).toString(); if (correctPassword != PasswordHasher::computeHash(oldPassword, correctPassword.left(16))) return true; QString passwordSha512 = PasswordHasher::computeHash(newPassword, PasswordHasher::generateRandomSalt()); passwordQuery = prepareQuery("update {prefix}_users set password_sha512=:password where name = :name"); passwordQuery->bindValue(":password", passwordSha512); passwordQuery->bindValue(":name", user); if (!execSqlQuery(passwordQuery)) { qDebug("Change password denied: SQL error"); return true; } return false; } int Servatrice_DatabaseInterface::getActiveUserCount() { int userCount = 0; if (!checkSql()) return userCount; QSqlQuery *query = prepareQuery("select count(*) from {prefix}_sessions where id_server = :serverid AND end_time is NULL"); query->bindValue(":serverid", server->getServerId()); if (!execSqlQuery(query)){ return userCount; } if (query->next()){ userCount = query->value(0).toInt(); } return userCount; }