Response::ResponseCode Server_ProtocolHandler::cmdJoinRoom(const Command_JoinRoom &cmd, ResponseContainer &rc)
{
	if (authState == NotLoggedIn)
		return Response::RespLoginNeeded;
	
	if (rooms.contains(cmd.room_id()))
		return Response::RespContextError;
	
	QReadLocker serverLocker(&server->roomsLock);
	Server_Room *r = server->getRooms().value(cmd.room_id(), 0);
	if (!r)
		return Response::RespNameNotFound;
	
	r->addClient(this);
	rooms.insert(r->getId(), r);
	
	Event_RoomSay joinMessageEvent;
	joinMessageEvent.set_message(r->getJoinMessage().toStdString());
	rc.enqueuePostResponseItem(ServerMessage::ROOM_EVENT, r->prepareRoomEvent(joinMessageEvent));
	
	Response_JoinRoom *re = new Response_JoinRoom;
	r->getInfo(*re->mutable_room_info(), true);
	
	rc.setResponseExtension(re);
	return Response::RespOk;
}
示例#2
0
void Server_Game::createGameJoinedEvent(Server_Player *player, ResponseContainer &rc, bool resuming)
{
	Event_GameJoined event1;
	getInfo(*event1.mutable_game_info());
	event1.set_host_id(hostId);
	event1.set_player_id(player->getPlayerId());
	event1.set_spectator(player->getSpectator());
	event1.set_resuming(resuming);
	if (resuming) {
		const QStringList &allGameTypes = room->getGameTypes();
		for (int i = 0; i < allGameTypes.size(); ++i) {
			ServerInfo_GameType *newGameType = event1.add_game_types();
			newGameType->set_game_type_id(i);
			newGameType->set_description(allGameTypes[i].toStdString());
		}
	}
	rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, Server_AbstractUserInterface::prepareSessionEvent(event1));
	
	Event_GameStateChanged event2;
	event2.set_seconds_elapsed(secondsElapsed);
	event2.set_game_started(gameStarted);
	event2.set_active_player_id(activePlayer);
	event2.set_active_phase(activePhase);
	
	QMapIterator<int, Server_Player *> playerIterator(players);
	while (playerIterator.hasNext())
		playerIterator.next().value()->getInfo(event2.add_player_list(), player, player->getSpectator() && spectatorsSeeEverything, true);

	rc.enqueuePostResponseItem(ServerMessage::GAME_EVENT_CONTAINER, prepareGameEvent(event2, -1));
}
Response::ResponseCode ServerSocketInterface::cmdDeckUpload(const Command_DeckUpload &cmd, ResponseContainer &rc)
{
    if (authState != PasswordRight)
        return Response::RespFunctionNotAllowed;

    if (!cmd.has_deck_list())
        return Response::RespInvalidData;

    sqlInterface->checkSql();

    QString deckStr = QString::fromStdString(cmd.deck_list());
    DeckList deck(deckStr);

    QString deckName = deck.getName();
    if (deckName.isEmpty())
        deckName = "Unnamed deck";

    if (cmd.has_path()) {
        int folderId = getDeckPathId(QString::fromStdString(cmd.path()));
        if (folderId == -1)
            return Response::RespNameNotFound;

        QSqlQuery query(sqlInterface->getDatabase());
        query.prepare("insert into " + servatrice->getDbPrefix() + "_decklist_files (id_folder, id_user, name, upload_time, content) values(:id_folder, :id_user, :name, NOW(), :content)");
        query.bindValue(":id_folder", folderId);
        query.bindValue(":id_user", userInfo->id());
        query.bindValue(":name", deckName);
        query.bindValue(":content", deckStr);
        sqlInterface->execSqlQuery(query);

        Response_DeckUpload *re = new Response_DeckUpload;
        ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file();
        fileInfo->set_id(query.lastInsertId().toInt());
        fileInfo->set_name(deckName.toStdString());
        fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toTime_t());
        rc.setResponseExtension(re);
    } else if (cmd.has_deck_id()) {
        QSqlQuery query(sqlInterface->getDatabase());
        query.prepare("update " + servatrice->getDbPrefix() + "_decklist_files set name=:name, upload_time=NOW(), content=:content where id = :id_deck and id_user = :id_user");
        query.bindValue(":id_deck", cmd.deck_id());
        query.bindValue(":id_user", userInfo->id());
        query.bindValue(":name", deckName);
        query.bindValue(":content", deckStr);
        sqlInterface->execSqlQuery(query);

        if (query.numRowsAffected() == 0)
            return Response::RespNameNotFound;

        Response_DeckUpload *re = new Response_DeckUpload;
        ServerInfo_DeckStorage_TreeItem *fileInfo = re->mutable_new_file();
        fileInfo->set_id(cmd.deck_id());
        fileInfo->set_name(deckName.toStdString());
        fileInfo->mutable_file()->set_creation_time(QDateTime::currentDateTime().toTime_t());
        rc.setResponseExtension(re);
    } else
        return Response::RespInvalidData;

    return Response::RespOk;
}
Response::ResponseCode Server_ProtocolHandler::cmdJoinRoom(const Command_JoinRoom &cmd, ResponseContainer &rc)
{
    if (authState == NotLoggedIn)
        return Response::RespLoginNeeded;

    if (rooms.contains(cmd.room_id()))
        return Response::RespContextError;

    QReadLocker serverLocker(&server->roomsLock);
    Server_Room *r = server->getRooms().value(cmd.room_id(), 0);
    if (!r)
        return Response::RespNameNotFound;

    QString roomPermission = r->getRoomPermission().toLower();
    if (roomPermission != "none"){
        if (roomPermission == "registered") {
            if (!(userInfo->user_level() & ServerInfo_User::IsRegistered))
                return Response::RespUserLevelTooLow;
        }

        if (roomPermission == "moderator"){
            if (!(userInfo->user_level() & ServerInfo_User::IsModerator))
                return Response::RespUserLevelTooLow;
        }

        if (roomPermission == "administrator"){
            if (!(userInfo->user_level() & ServerInfo_User::IsAdmin))
                return Response::RespUserLevelTooLow;
        }
    }

    r->addClient(this);
    rooms.insert(r->getId(), r);

    Event_RoomSay joinMessageEvent;
    joinMessageEvent.set_message(r->getJoinMessage().toStdString());
    joinMessageEvent.set_message_type(Event_RoomSay::Welcome);
    rc.enqueuePostResponseItem(ServerMessage::ROOM_EVENT, r->prepareRoomEvent(joinMessageEvent));

    QReadLocker chatHistoryLocker(&r->historyLock);
    QList<ServerInfo_ChatMessage> chatHistory = r->getChatHistory();
    ServerInfo_ChatMessage chatMessage;
    for (int i = 0; i < chatHistory.size(); ++i) {
        chatMessage = chatHistory.at(i);
        qDebug() << QString::fromStdString(chatMessage.message()).simplified();
        Event_RoomSay roomChatHistory;
        roomChatHistory.set_message(chatMessage.sender_name() + ": " + chatMessage.message());
        roomChatHistory.set_message_type(Event_RoomSay::ChatHistory);
        roomChatHistory.set_time_of(QDateTime::fromString(QString::fromStdString(chatMessage.time())).toMSecsSinceEpoch());
        rc.enqueuePostResponseItem(ServerMessage::ROOM_EVENT, r->prepareRoomEvent(roomChatHistory));
    }

    Response_JoinRoom *re = new Response_JoinRoom;
    r->getInfo(*re->mutable_room_info(), true);

    rc.setResponseExtension(re);
    return Response::RespOk;
}
Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd, ResponseContainer &rc)
{
    QString userName = QString::fromStdString(cmd.user_name()).simplified();
    QString clientId = QString::fromStdString(cmd.clientid()).simplified();
    
    if (userName.isEmpty() || (userInfo != 0))
        return Response::RespContextError;

    QString reasonStr;
    int banSecondsLeft = 0;
    AuthenticationResult res = server->loginUser(this, userName, QString::fromStdString(cmd.password()), reasonStr, banSecondsLeft, clientId);
    switch (res) {
        case UserIsBanned: {
            Response_Login *re = new Response_Login;
            re->set_denied_reason_str(reasonStr.toStdString());
            if (banSecondsLeft != 0)
                re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsLeft).toTime_t());
            rc.setResponseExtension(re);
            return Response::RespUserIsBanned;
        }
        case NotLoggedIn: return Response::RespWrongPassword;
        case WouldOverwriteOldSession: return Response::RespWouldOverwriteOldSession;
        case UsernameInvalid: {
            Response_Login *re = new Response_Login;
            re->set_denied_reason_str(reasonStr.toStdString());
            rc.setResponseExtension(re);
            return Response::RespUsernameInvalid;
        }
        case RegistrationRequired: return Response::RespRegistrationRequired;
        case ClientIdRequired: return Response::RespClientIdRequired;
        case UserIsInactive: return Response::RespAccountNotActivated;
        default: authState = res;
    }

    userName = QString::fromStdString(userInfo->name());
    Event_ServerMessage event;
    event.set_message(server->getLoginMessage().toStdString());
    rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event));

    Response_Login *re = new Response_Login;
    re->mutable_user_info()->CopyFrom(copyUserInfo(true));

    if (authState == PasswordRight) {
        QMapIterator<QString, ServerInfo_User> buddyIterator(databaseInterface->getBuddyList(userName));
        while (buddyIterator.hasNext())
            re->add_buddy_list()->CopyFrom(buddyIterator.next().value());

        QMapIterator<QString, ServerInfo_User> ignoreIterator(databaseInterface->getIgnoreList(userName));
        while (ignoreIterator.hasNext())
            re->add_ignore_list()->CopyFrom(ignoreIterator.next().value());
    }

    joinPersistentGames(rc);

    rc.setResponseExtension(re);
    return Response::RespOk;
}
Response::ResponseCode Server_ProtocolHandler::cmdGetUserInfo(const Command_GetUserInfo &cmd, ResponseContainer &rc)
{
    if (authState == NotLoggedIn)
        return Response::RespLoginNeeded;

    QString userName = QString::fromStdString(cmd.user_name());
    Response_GetUserInfo *re = new Response_GetUserInfo;
    if (userName.isEmpty())
        re->mutable_user_info()->CopyFrom(*userInfo);
    else {

        QReadLocker locker(&server->clientsLock);

        ServerInfo_User_Container *infoSource = server->findUser(userName);
        if (!infoSource) {
            re->mutable_user_info()->CopyFrom(databaseInterface->getUserData(userName,true));
        } else {
            re->mutable_user_info()->CopyFrom(infoSource->copyUserInfo(true, false, userInfo->user_level() & ServerInfo_User::IsModerator));
        }
    }


    rc.setResponseExtension(re);
    return Response::RespOk;
}
Response::ResponseCode Server_ProtocolHandler::cmdGetGamesOfUser(const Command_GetGamesOfUser &cmd, ResponseContainer &rc)
{
	if (authState == NotLoggedIn)
		return Response::RespLoginNeeded;
	
	// We don't need to check whether the user is logged in; persistent games should also work.
	// The client needs to deal with an empty result list.
	
	Response_GetGamesOfUser *re = new Response_GetGamesOfUser;
	server->roomsLock.lockForRead();
	QMapIterator<int, Server_Room *> roomIterator(server->getRooms());
	while (roomIterator.hasNext()) {
		Server_Room *room = roomIterator.next().value();
		room->gamesLock.lockForRead();
		room->getInfo(*re->add_room_list(), false, true);
		QListIterator<ServerInfo_Game> gameIterator(room->getGamesOfUser(QString::fromStdString(cmd.user_name())));
		while (gameIterator.hasNext())
			re->add_game_list()->CopyFrom(gameIterator.next());
		room->gamesLock.unlock();
	}
	server->roomsLock.unlock();
	
	rc.setResponseExtension(re);
	return Response::RespOk;
}
Response::ResponseCode ServerSocketInterface::cmdReplayDownload(const Command_ReplayDownload &cmd, ResponseContainer &rc)
{
    if (authState != PasswordRight)
        return Response::RespFunctionNotAllowed;

    {
        QSqlQuery *query = sqlInterface->prepareQuery("select 1 from {prefix}_replays_access a left join {prefix}_replays b on a.id_game = b.id_game where b.id = :id_replay and a.id_player = :id_player");
        query->bindValue(":id_replay", cmd.replay_id());
        query->bindValue(":id_player", userInfo->id());
        if (!sqlInterface->execSqlQuery(query))
            return Response::RespInternalError;
        if (!query->next())
            return Response::RespAccessDenied;
    }

    QSqlQuery *query = sqlInterface->prepareQuery("select replay from {prefix}_replays where id = :id_replay");
    query->bindValue(":id_replay", cmd.replay_id());
    if (!sqlInterface->execSqlQuery(query))
        return Response::RespInternalError;
    if (!query->next())
        return Response::RespNameNotFound;

    QByteArray data = query->value(0).toByteArray();

    Response_ReplayDownload *re = new Response_ReplayDownload;
    re->set_replay_data(data.data(), data.size());
    rc.setResponseExtension(re);

    return Response::RespOk;
}
Response::ResponseCode Server_ProtocolHandler::cmdMessage(const Command_Message &cmd, ResponseContainer &rc)
{
	if (authState == NotLoggedIn)
		return Response::RespLoginNeeded;
	
	QReadLocker locker(&server->clientsLock);
	
	QString receiver = QString::fromStdString(cmd.user_name());
	Server_AbstractUserInterface *userInterface = server->findUser(receiver);
	if (!userInterface)
		return Response::RespNameNotFound;
	if (databaseInterface->isInIgnoreList(receiver, QString::fromStdString(userInfo->name())))
		return Response::RespInIgnoreList;
	
	Event_UserMessage event;
	event.set_sender_name(userInfo->name());
	event.set_receiver_name(cmd.user_name());
	event.set_message(cmd.message());
	
	SessionEvent *se = prepareSessionEvent(event);
	userInterface->sendProtocolItem(*se);
	rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, se);
	
	return Response::RespOk;
}
Response::ResponseCode Server_ProtocolHandler::cmdJoinRoom(const Command_JoinRoom &cmd, ResponseContainer &rc)
{
    if (authState == NotLoggedIn)
        return Response::RespLoginNeeded;

    if (rooms.contains(cmd.room_id()))
        return Response::RespContextError;

    QReadLocker serverLocker(&server->roomsLock);
    Server_Room *r = server->getRooms().value(cmd.room_id(), 0);
    if (!r)
        return Response::RespNameNotFound;

    QString roomPermission = r->getRoomPermission().toLower();
    if (roomPermission != "none"){
        if (roomPermission == "registered") {
            if (!(userInfo->user_level() & ServerInfo_User::IsRegistered))
                return Response::RespUserLevelTooLow;
        }

        if (roomPermission == "moderator"){
            if (!(userInfo->user_level() & ServerInfo_User::IsModerator))
                return Response::RespUserLevelTooLow;
        }

        if (roomPermission == "administrator"){
            if (!(userInfo->user_level() & ServerInfo_User::IsAdmin))
                return Response::RespUserLevelTooLow;
        }
    }

    r->addClient(this);
    rooms.insert(r->getId(), r);

    Event_RoomSay joinMessageEvent;
    joinMessageEvent.set_message(r->getJoinMessage().toStdString());
    rc.enqueuePostResponseItem(ServerMessage::ROOM_EVENT, r->prepareRoomEvent(joinMessageEvent));

    Response_JoinRoom *re = new Response_JoinRoom;
    r->getInfo(*re->mutable_room_info(), true);

    rc.setResponseExtension(re);
    return Response::RespOk;
}
void Server_AbstractUserInterface::sendResponseContainer(const ResponseContainer &responseContainer, Response::ResponseCode responseCode)
{
    const QList<QPair<ServerMessage::MessageType, ::google::protobuf::Message *> > &preResponseQueue = responseContainer.getPreResponseQueue();
    for (int i = 0; i < preResponseQueue.size(); ++i)
        sendProtocolItemByType(preResponseQueue[i].first, *preResponseQueue[i].second);
    
    if (responseCode != Response::RespNothing) {
        Response response;
        response.set_cmd_id(responseContainer.getCmdId());
        response.set_response_code(responseCode);
        ::google::protobuf::Message *responseExtension = responseContainer.getResponseExtension();
        if (responseExtension)
            response.GetReflection()->MutableMessage(&response, responseExtension->GetDescriptor()->FindExtensionByName("ext"))->CopyFrom(*responseExtension);
        sendProtocolItem(response);
    }
    
    const QList<QPair<ServerMessage::MessageType, ::google::protobuf::Message *> > &postResponseQueue = responseContainer.getPostResponseQueue();
    for (int i = 0; i < postResponseQueue.size(); ++i)
        sendProtocolItemByType(postResponseQueue[i].first, *postResponseQueue[i].second);
}
Response::ResponseCode Server_ProtocolHandler::cmdListRooms(const Command_ListRooms & /*cmd*/, ResponseContainer &rc)
{
	if (authState == NotLoggedIn)
		return Response::RespLoginNeeded;
	
	Event_ListRooms event;
	QMapIterator<int, Server_Room *> roomIterator(server->getRooms());
	while (roomIterator.hasNext())
		roomIterator.next().value()->getInfo(*event.add_room_list(), false);
	rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event));
	
	acceptsRoomListChanges = true;
	return Response::RespOk;
}
Response::ResponseCode ServerSocketInterface::cmdReplayList(const Command_ReplayList & /*cmd*/, ResponseContainer &rc)
{
    if (authState != PasswordRight)
        return Response::RespFunctionNotAllowed;

    Response_ReplayList *re = new Response_ReplayList;

    QSqlQuery query1(sqlInterface->getDatabase());
    query1.prepare("select a.id_game, a.replay_name, b.room_name, b.time_started, b.time_finished, b.descr, a.do_not_hide from cockatrice_replays_access a left join cockatrice_games b on b.id = a.id_game where a.id_player = :id_player and (a.do_not_hide = 1 or date_add(b.time_started, interval 7 day) > now())");
    query1.bindValue(":id_player", userInfo->id());
    sqlInterface->execSqlQuery(query1);
    while (query1.next()) {
        ServerInfo_ReplayMatch *matchInfo = re->add_match_list();

        const int gameId = query1.value(0).toInt();
        matchInfo->set_game_id(gameId);
        matchInfo->set_room_name(query1.value(2).toString().toStdString());
        const int timeStarted = query1.value(3).toDateTime().toTime_t();
        const int timeFinished = query1.value(4).toDateTime().toTime_t();
        matchInfo->set_time_started(timeStarted);
        matchInfo->set_length(timeFinished - timeStarted);
        matchInfo->set_game_name(query1.value(5).toString().toStdString());
        const QString replayName = query1.value(1).toString();
        matchInfo->set_do_not_hide(query1.value(6).toBool());

        {
            QSqlQuery query2(sqlInterface->getDatabase());
            query2.prepare("select player_name from cockatrice_games_players where id_game = :id_game");
            query2.bindValue(":id_game", gameId);
            sqlInterface->execSqlQuery(query2);
            while (query2.next())
                matchInfo->add_player_names(query2.value(0).toString().toStdString());
        }
        {
            QSqlQuery query3(sqlInterface->getDatabase());
            query3.prepare("select id, duration from " + servatrice->getDbPrefix() + "_replays where id_game = :id_game");
            query3.bindValue(":id_game", gameId);
            sqlInterface->execSqlQuery(query3);
            while (query3.next()) {
                ServerInfo_Replay *replayInfo = matchInfo->add_replay_list();
                replayInfo->set_replay_id(query3.value(0).toInt());
                replayInfo->set_replay_name(replayName.toStdString());
                replayInfo->set_duration(query3.value(1).toInt());
            }
        }
    }

    rc.setResponseExtension(re);
    return Response::RespOk;
}
Response::ResponseCode ServerSocketInterface::cmdDeckList(const Command_DeckList & /*cmd*/, ResponseContainer &rc)
{
    if (authState != PasswordRight)
        return Response::RespFunctionNotAllowed;

    sqlInterface->checkSql();

    Response_DeckList *re = new Response_DeckList;
    ServerInfo_DeckStorage_Folder *root = re->mutable_root();

    if (!deckListHelper(0, root))
        return Response::RespContextError;

    rc.setResponseExtension(re);
    return Response::RespOk;
}
Response::ResponseCode ServerSocketInterface::cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc)
{
    if (authState != PasswordRight)
        return Response::RespFunctionNotAllowed;

    DeckList *deck;
    try {
        deck = sqlInterface->getDeckFromDatabase(cmd.deck_id(), userInfo->id());
    } catch(Response::ResponseCode r) {
        return r;
    }

    Response_DeckDownload *re = new Response_DeckDownload;
    re->set_deck(deck->writeToString_Native().toStdString());
    rc.setResponseExtension(re);
    delete deck;

    return Response::RespOk;
}
Response::ResponseCode Server_ProtocolHandler::cmdListUsers(const Command_ListUsers & /*cmd*/, ResponseContainer &rc)
{
	if (authState == NotLoggedIn)
		return Response::RespLoginNeeded;
	
	Response_ListUsers *re = new Response_ListUsers;
	server->clientsLock.lockForRead();
	QMapIterator<QString, Server_ProtocolHandler *> userIterator = server->getUsers();
	while (userIterator.hasNext())
		re->add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(false));
	QMapIterator<QString, Server_AbstractUserInterface *> extIterator = server->getExternalUsers();
	while (extIterator.hasNext())
		re->add_user_list()->CopyFrom(extIterator.next().value()->copyUserInfo(false));
	
	acceptsUserListChanges = true;
	server->clientsLock.unlock();
	
	rc.setResponseExtension(re);
	return Response::RespOk;
}
Response::ResponseCode ServerSocketInterface::cmdAddToList(const Command_AddToList &cmd, ResponseContainer &rc)
{
    if (authState != PasswordRight)
        return Response::RespFunctionNotAllowed;

    QString list = QString::fromStdString(cmd.list());
    QString user = QString::fromStdString(cmd.user_name());

    if ((list != "buddy") && (list != "ignore"))
        return Response::RespContextError;

    if (list == "buddy")
        if (databaseInterface->isInBuddyList(QString::fromStdString(userInfo->name()), user))
            return Response::RespContextError;
    if (list == "ignore")
        if (databaseInterface->isInIgnoreList(QString::fromStdString(userInfo->name()), user))
            return Response::RespContextError;

    int id1 = userInfo->id();
    int id2 = sqlInterface->getUserIdInDB(user);
    if (id2 < 0)
        return Response::RespNameNotFound;
    if (id1 == id2)
        return Response::RespContextError;

    QSqlQuery query(sqlInterface->getDatabase());
    query.prepare("insert into " + servatrice->getDbPrefix() + "_" + list + "list (id_user1, id_user2) values(:id1, :id2)");
    query.bindValue(":id1", id1);
    query.bindValue(":id2", id2);
    if (!sqlInterface->execSqlQuery(query))
        return Response::RespInternalError;

    Event_AddToList event;
    event.set_list_name(cmd.list());
    event.mutable_user_info()->CopyFrom(databaseInterface->getUserData(user));
    rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event));

    return Response::RespOk;
}
Response::ResponseCode ServerSocketInterface::cmdRemoveFromList(const Command_RemoveFromList &cmd, ResponseContainer &rc)
{
    if (authState != PasswordRight)
        return Response::RespFunctionNotAllowed;

    QString list = QString::fromStdString(cmd.list());
    QString user = QString::fromStdString(cmd.user_name());

    if ((list != "buddy") && (list != "ignore"))
        return Response::RespContextError;

    if (list == "buddy")
        if (!databaseInterface->isInBuddyList(QString::fromStdString(userInfo->name()), user))
            return Response::RespContextError;
    if (list == "ignore")
        if (!databaseInterface->isInIgnoreList(QString::fromStdString(userInfo->name()), user))
            return Response::RespContextError;

    int id1 = userInfo->id();
    int id2 = sqlInterface->getUserIdInDB(user);
    if (id2 < 0)
        return Response::RespNameNotFound;

    QSqlQuery *query = sqlInterface->prepareQuery("delete from {prefix}_" + list + "list where id_user1 = :id1 and id_user2 = :id2");
    query->bindValue(":id1", id1);
    query->bindValue(":id2", id2);
    if (!sqlInterface->execSqlQuery(query))
        return Response::RespInternalError;

    Event_RemoveFromList event;
    event.set_list_name(cmd.list());
    event.set_user_name(cmd.user_name());
    rc.enqueuePreResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event));

    return Response::RespOk;
}
Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd, ResponseContainer &rc)
{
    QString userName = QString::fromStdString(cmd.user_name()).simplified();
    QString clientId = QString::fromStdString(cmd.clientid()).simplified();
    QString clientVersion = QString::fromStdString(cmd.clientver()).simplified();

    if (userName.isEmpty() || (userInfo != 0))
        return Response::RespContextError;

    // check client feature set against server feature set
    FeatureSet features;
    QMap<QString, bool> receivedClientFeatures;
    QMap<QString, bool> missingClientFeatures;

    for (int i = 0; i < cmd.clientfeatures().size(); ++i)
        receivedClientFeatures.insert(QString::fromStdString(cmd.clientfeatures(i)).simplified(), false);

    missingClientFeatures = features.identifyMissingFeatures(receivedClientFeatures, server->getServerRequiredFeatureList());

    if (!missingClientFeatures.isEmpty()) {
        if (features.isRequiredFeaturesMissing(missingClientFeatures, server->getServerRequiredFeatureList())) {
            Response_Login *re = new Response_Login;
            re->set_denied_reason_str("Client upgrade required");
            QMap<QString, bool>::iterator i;
            for (i = missingClientFeatures.begin(); i != missingClientFeatures.end(); ++i)
                re->add_missing_features(i.key().toStdString().c_str());
            rc.setResponseExtension(re);
            return Response::RespClientUpdateRequired;
        }
    }

    QString reasonStr;
    int banSecondsLeft = 0;
    AuthenticationResult res = server->loginUser(this, userName, QString::fromStdString(cmd.password()), reasonStr, banSecondsLeft, clientId, clientVersion);
    switch (res) {
        case UserIsBanned: {
            Response_Login *re = new Response_Login;
            re->set_denied_reason_str(reasonStr.toStdString());
            if (banSecondsLeft != 0)
                re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsLeft).toTime_t());
            rc.setResponseExtension(re);
            return Response::RespUserIsBanned;
        }
        case NotLoggedIn: return Response::RespWrongPassword;
        case WouldOverwriteOldSession: return Response::RespWouldOverwriteOldSession;
        case UsernameInvalid: {
            Response_Login *re = new Response_Login;
            re->set_denied_reason_str(reasonStr.toStdString());
            rc.setResponseExtension(re);
            return Response::RespUsernameInvalid;
        }
        case RegistrationRequired: return Response::RespRegistrationRequired;
        case ClientIdRequired: return Response::RespClientIdRequired;
        case UserIsInactive: return Response::RespAccountNotActivated;
        default: authState = res;
    }

    userName = QString::fromStdString(userInfo->name());
    Event_ServerMessage event;
    event.set_message(server->getLoginMessage().toStdString());
    rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event));

    Response_Login *re = new Response_Login;
    re->mutable_user_info()->CopyFrom(copyUserInfo(true));

    if (authState == PasswordRight) {
        QMapIterator<QString, ServerInfo_User> buddyIterator(databaseInterface->getBuddyList(userName));
        while (buddyIterator.hasNext())
            re->add_buddy_list()->CopyFrom(buddyIterator.next().value());

        QMapIterator<QString, ServerInfo_User> ignoreIterator(databaseInterface->getIgnoreList(userName));
        while (ignoreIterator.hasNext())
            re->add_ignore_list()->CopyFrom(ignoreIterator.next().value());
    }

    // return to client any missing features the server has that the client does not
    if (!missingClientFeatures.isEmpty()) {
        QMap<QString, bool>::iterator i;
        for (i = missingClientFeatures.begin(); i != missingClientFeatures.end(); ++i)
            re->add_missing_features(i.key().toStdString().c_str());
    }

    joinPersistentGames(rc);

    rc.setResponseExtension(re);
    return Response::RespOk;
}
Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc)
{
    QString userName = QString::fromStdString(cmd.user_name());
    qDebug() << "Got register command: " << userName;

    bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool();
    if (!registrationEnabled)
        return Response::RespRegistrationDisabled;

    QString emailAddress = QString::fromStdString(cmd.email());
    bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool();
    if (requireEmailForRegistration)
    {
        QRegExp rx("\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}\\b");
        if(emailAddress.isEmpty() || !rx.exactMatch(emailAddress))
            return Response::RespEmailRequiredToRegister;
    }

    // TODO: Move this method outside of the db interface
    QString errorString;
    if (!sqlInterface->usernameIsValid(userName, errorString))
    {
        Response_Register *re = new Response_Register;
        re->set_denied_reason_str(errorString.toStdString());
        rc.setResponseExtension(re);
        return Response::RespUsernameInvalid;
    }

    if(sqlInterface->userExists(userName))
        return Response::RespUserAlreadyExists;

    QString banReason;
    int banSecondsRemaining;
    if (sqlInterface->checkUserIsBanned(this->getAddress(), userName, banReason, banSecondsRemaining))
    {
        Response_Register *re = new Response_Register;
        re->set_denied_reason_str(banReason.toStdString());
        if (banSecondsRemaining != 0)
            re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsRemaining).toTime_t());
        rc.setResponseExtension(re);
        return Response::RespUserIsBanned;
    }

    if (tooManyRegistrationAttempts(this->getAddress()))
        return Response::RespTooManyRequests;

    QString realName = QString::fromStdString(cmd.real_name());
    ServerInfo_User_Gender gender = cmd.gender();
    QString country = QString::fromStdString(cmd.country());
    QString password = QString::fromStdString(cmd.password());

    // TODO make this configurable?
    if(password.length() < 6)
        return Response::RespPasswordTooShort;

    QString token;
    bool regSucceeded = sqlInterface->registerUser(userName, realName, gender, password, emailAddress, country, token, !requireEmailForRegistration);

    if(regSucceeded)
    {
        qDebug() << "Accepted register command for user: "******"insert into {prefix}_activation_emails (name) values(:name)");
            query->bindValue(":name", userName);
            if (!sqlInterface->execSqlQuery(query))
                return Response::RespRegistrationFailed;

            return Response::RespRegistrationAcceptedNeedsActivation;
        } else {
            return Response::RespRegistrationAccepted;
        }
    } else {
        return Response::RespRegistrationFailed;
    }
}