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::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;
}