#include "users.h"
// This file is part of MRCI.
// MRCI is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// MRCI is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with MRCI under the LICENSE.md file. If not, see
// .
ListUsers::ListUsers(QObject *parent) : TableViewer(parent)
{
setParams(TABLE_USERS, false);
addTableColumn(TABLE_USERS, COLUMN_TIME);
addTableColumn(TABLE_USERS, COLUMN_USERNAME);
addTableColumn(TABLE_USERS, COLUMN_HOST_RANK);
addTableColumn(TABLE_USERS, COLUMN_USER_ID);
}
LockUser::LockUser(QObject *parent) : CmdObject(parent) {}
CreateUser::CreateUser(QObject *parent) : CmdObject(parent) {}
RemoveUser::RemoveUser(QObject *parent) : CmdObject(parent) {}
ChangeUserRank::ChangeUserRank(QObject *parent) : CmdObject(parent) {}
ChangePassword::ChangePassword(QObject *parent) : CmdObject(parent) {}
ChangeDispName::ChangeDispName(QObject *parent) : CmdObject(parent) {}
ChangeUsername::ChangeUsername(QObject *parent) : CmdObject(parent) {}
OverWriteEmail::OverWriteEmail(QObject *parent) : CmdObject(parent) {}
ChangeEmail::ChangeEmail(QObject *parent) : OverWriteEmail(parent) {}
PasswordChangeRequest::PasswordChangeRequest(QObject *parent) : CmdObject(parent) {}
NameChangeRequest::NameChangeRequest(QObject *parent) : PasswordChangeRequest(parent) {}
QString ListUsers::cmdName() {return "ls_users";}
QString LockUser::cmdName() {return "lock_acct";}
QString CreateUser::cmdName() {return "add_acct";}
QString RemoveUser::cmdName() {return "rm_acct";}
QString ChangeUserRank::cmdName() {return "set_user_rank";}
QString ChangePassword::cmdName() {return "set_pw";}
QString ChangeDispName::cmdName() {return "set_disp_name";}
QString ChangeUsername::cmdName() {return "set_user_name";}
QString OverWriteEmail::cmdName() {return "force_set_email";}
QString ChangeEmail::cmdName() {return "set_email";}
QString PasswordChangeRequest::cmdName() {return "request_new_pw";}
QString NameChangeRequest::cmdName() {return "request_new_user_name";}
bool canModifyUser(const QByteArray &uId, quint32 myRank, bool equalAcceptable)
{
Query db;
db.setType(Query::PULL, TABLE_USERS);
db.addColumn(COLUMN_HOST_RANK);
db.addCondition(COLUMN_USER_ID, uId);
db.exec();
if (equalAcceptable)
{
return myRank <= db.getData(COLUMN_HOST_RANK).toUInt();
}
else
{
return myRank < db.getData(COLUMN_HOST_RANK).toUInt();
}
}
void LockUser::procIn(const QByteArray &binIn, quint8 dType)
{
if (dType == TEXT)
{
auto args = parseArgs(binIn, 4);
auto uName = getParam("-user", args);
auto state = getParam("-state", args);
QByteArray uId;
retCode = INVALID_PARAMS;
if (uName.isEmpty())
{
errTxt("err: User name (-user) argument not found or is empty.\n");
}
else if (state.isEmpty())
{
errTxt("err: State (-state) argument not found or is empty.\n");
}
else if (!validUserName(uName))
{
errTxt("err: Invalid user name.\n");
}
else if (!isBool(state))
{
errTxt("err: The state bool value (-state) must be a 0 or 1.\n");
}
else if (!userExists(uName, &uId))
{
errTxt("err: The requested user name does not exists.\n");
}
else if (!canModifyUser(uId, rd32BitFromBlock(hostRank), false))
{
errTxt("err: The target user account out ranks you or is equal to your own rank. access denied.\n");
}
else
{
retCode = NO_ERRORS;
Query db(this);
db.setType(Query::UPDATE, TABLE_USERS);
db.addColumn(COLUMN_LOCKED, static_cast(state.toInt()));
db.addCondition(COLUMN_USER_ID, uId);
db.exec();
}
}
}
void CreateUser::clear()
{
flags = 0;
email.clear();
newName.clear();
}
void CreateUser::procIn(const QByteArray &binIn, quint8 dType)
{
if (dType == TEXT)
{
if (flags & MORE_INPUT)
{
auto password = fromTEXT(binIn);
QString errMsg;
if (password.isEmpty())
{
retCode = ABORTED;
clear();
}
else if (!acceptablePw(password, newName, dispName, email, &errMsg))
{
errTxt(errMsg + "\n");
privTxt("Enter a new password (leave blank to cancel): ");
}
else if (!createUser(newName, email, dispName, password))
{
retCode = INVALID_PARAMS;
errTxt("err: The requested User name already exists.\n");
clear();
}
else
{
clear();
}
}
else
{
auto args = parseArgs(binIn, 6);
dispName = getParam("-disp", args);
newName = getParam("-name", args);
email = getParam("-email", args);
retCode = INVALID_PARAMS;
if (newName.isEmpty())
{
errTxt("err: Username (-name) argument not found or is empty.\n");
}
else if (email.isEmpty())
{
errTxt("err: Email (-email) argument not found or is empty.\n");
}
else if (!validUserName(newName))
{
errTxt("err: Invalid username. it must be 2-24 chars long and contain no spaces.\n");
}
else if (noCaseMatch(DEFAULT_ROOT_USER, newName))
{
errTxt("err: '" + QString(DEFAULT_ROOT_USER) + "' is a reserved keyword. invalid for use as a username.\n");
}
else if (validEmailAddr(newName))
{
errTxt("err: Invaild username. it looks like an email address.\n");
}
else if (!validEmailAddr(email))
{
errTxt("err: Invalid email address. it must contain a '@' symbol along with a vaild host address and user name that contain no spaces. it must also be less than 64 chars long.\n");
}
else if (!validDispName(dispName))
{
errTxt("err: The display name is too large or contains a newline char. char limit: 32.\n");
}
else if (userExists(newName))
{
errTxt("err: The requested User name already exists.\n");
}
else if (emailExists(email))
{
errTxt("err: The requested email address is already in use.\n");
}
else
{
retCode = NO_ERRORS;
flags |= MORE_INPUT;
privTxt("Enter a new password (leave blank to cancel): ");
}
}
}
}
void RemoveUser::rm()
{
Query db;
db.setType(Query::DEL, TABLE_USERS);
db.addCondition(COLUMN_USER_ID, uId);
db.exec();
flags &= ~MORE_INPUT;
async(ASYNC_USER_DELETED, uId);
}
void RemoveUser::ask()
{
flags |= MORE_INPUT;
promptTxt("Are you sure you want to permanently remove this user account? (y/n): ");
}
void RemoveUser::procIn(const QByteArray &binIn, quint8 dType)
{
if (dType == TEXT)
{
if (flags & MORE_INPUT)
{
auto ans = fromTEXT(binIn);
if (noCaseMatch("y", ans))
{
rm();
}
else if (noCaseMatch("n", ans))
{
retCode = ABORTED;
flags &= ~MORE_INPUT;
}
else
{
ask();
}
}
else
{
auto args = parseArgs(binIn, 2);
auto uName = getParam("-name", args);
retCode = INVALID_PARAMS;
if (uName.isEmpty())
{
errTxt("err: User name argument (-name) not found or is empty.\n");
}
else if (!validUserName(uName))
{
errTxt("err: Invalid username.\n");
}
else if (!userExists(uName, &uId))
{
errTxt("err: The requested user name does not exists.\n");
}
else if (rootUserId() == uId)
{
errTxt("err: Unable to delete root user: '" + uName + "'\n");
}
else if (isChOwner(uId))
{
errTxt("err: The requested user name is the owner of one or more channels. assign new owners for these channels before attempting to delete this account.\n");
}
else if (!canModifyUser(uId, rd32BitFromBlock(hostRank), false) && (rdFromBlock(userId, BLKSIZE_USER_ID) != uId))
{
errTxt("err: The target user account out ranks you, access denied.\n");
}
else
{
retCode = NO_ERRORS;
if (argExists("-force", args))
{
rm();
}
else
{
ask();
}
}
}
}
}
void ChangeUserRank::procIn(const QByteArray &binIn, quint8 dType)
{
if (dType == TEXT)
{
auto args = parseArgs(binIn, 4);
auto uName = getParam("-user", args);
auto rank = getParam("-rank", args);
QByteArray uId;
retCode = INVALID_PARAMS;
if (uName.isEmpty())
{
errTxt("err: User name argument (-user) not found or is empty.\n");
}
else if (rank.isEmpty())
{
errTxt("err: New rank argument (-rank) not found or is empty.\n");
}
else if (!validUserName(uName))
{
errTxt("err: Invalid username.\n");
}
else if (!isInt(rank))
{
errTxt("err: Invalid 32bit unsigned integer for the new rank.\n");
}
else if (rank.toUInt() == 0)
{
errTxt("err: Rank 0 is invalid. please set a rank of 1 or higher.\n");
}
else if (rank.toUInt() < rd32BitFromBlock(hostRank))
{
errTxt("err: You cannot assign a rank higher than your own.\n");
}
else if (!userExists(uName, &uId))
{
errTxt("err: The requested user account does not exists.\n");
}
else if (rootUserId() == uId)
{
errTxt("err: You are not allowed to change the rank of root user: '" + uName + "'\n");
}
else if (!canModifyUser(uId, rd32BitFromBlock(hostRank), false))
{
errTxt("err: The target user out ranks you or is equal to your own rank. access denied.\n");
}
else
{
retCode = NO_ERRORS;
Query db(this);
db.setType(Query::UPDATE, TABLE_USERS);
db.addColumn(COLUMN_HOST_RANK, rank.toUInt());
db.addCondition(COLUMN_USER_ID, uId);
db.exec();
async(ASYNC_USER_RANK_CHANGED, uId + wrInt(rank.toUInt(), 32));
}
}
}
void ChangePassword::procIn(const QByteArray &binIn, quint8 dType)
{
if (dType == TEXT)
{
if (flags & MORE_INPUT)
{
auto password = fromTEXT(binIn);
QString errMsg;
if (password.isEmpty())
{
retCode = ABORTED;
flags &= ~MORE_INPUT;
}
else if (!acceptablePw(password, rdFromBlock(userId, BLKSIZE_USER_ID), &errMsg))
{
errTxt(errMsg + "\n");
privTxt("Enter a new password (leave blank to cancel): ");
}
else
{
flags &= ~MORE_INPUT;
updatePassword(rdFromBlock(userId, BLKSIZE_USER_ID), password, TABLE_USERS);
}
}
else
{
flags |= MORE_INPUT;
privTxt("Enter a new password (leave blank to cancel): ");
}
}
}
void ChangeUsername::procIn(const QByteArray &binIn, quint8 dType)
{
if (dType == TEXT)
{
auto args = parseArgs(binIn, 2);
auto newName = getParam("-new_name", args);
retCode = INVALID_PARAMS;
if (newName.isEmpty())
{
errTxt("err: New user name argument (-new_name) not found or is empty.\n");
}
else if (!validUserName(newName))
{
errTxt("err: Invalid username. it must be 2-24 chars long and contain no spaces.\n");
}
else if (noCaseMatch(DEFAULT_ROOT_USER, newName))
{
errTxt("err: '" + QString(DEFAULT_ROOT_USER) + "' is a reserved keyword. invalid for use as a username.\n");
}
else if (validEmailAddr(newName))
{
errTxt("err: Invaild username. it looks like an email address.\n");
}
else if (userExists(newName))
{
errTxt("err: The requested user name already exists.\n");
}
else
{
retCode = NO_ERRORS;
auto uId = rdFromBlock(userId, BLKSIZE_USER_ID);
auto newNameBa = fixedToTEXT(newName, BLKSIZE_USER_NAME);
Query db(this);
db.setType(Query::UPDATE, TABLE_USERS);
db.addColumn(COLUMN_USERNAME, newName);
db.addCondition(COLUMN_USER_ID, rdFromBlock(userId, BLKSIZE_USER_ID));
db.exec();
async(ASYNC_USER_RENAMED, uId + newNameBa);
}
}
}
void ChangeDispName::procIn(const QByteArray &binIn, quint8 dType)
{
if (dType == TEXT)
{
auto args = parseArgs(binIn, 2);
auto name = getParam("-new_name", args).trimmed();
retCode = INVALID_PARAMS;
if (!argExists("-new_name", args))
{
errTxt("err: New display name argument (-new_name) not found.\n");
}
else if (!validDispName(name))
{
errTxt("err: The display name is too large or contains a newline char. limit: 32 chars.\n");
}
else
{
retCode = NO_ERRORS;
Query db(this);
auto uId = rdFromBlock(userId, BLKSIZE_USER_ID);
auto newNameBa = fixedToTEXT(name, BLKSIZE_DISP_NAME);
db.setType(Query::UPDATE, TABLE_USERS);
db.addColumn(COLUMN_DISPLAY_NAME, name);
db.addCondition(COLUMN_USER_ID, uId);
db.exec();
async(ASYNC_DISP_RENAMED, uId + newNameBa);
}
}
}
void OverWriteEmail::procArgs(const QString &uName, const QString &newEmail, bool sameRank)
{
QByteArray uId;
retCode = INVALID_PARAMS;
if (newEmail.isEmpty())
{
errTxt("err: New email address (-new_email) argument was not found or is empty.\n");
}
else if (uName.isEmpty())
{
errTxt("err: User name (-user) argument was not found or is empty.\n");
}
else if (!validUserName(uName))
{
errTxt("err: Invalid user name.\n");
}
else if (!validEmailAddr(newEmail))
{
errTxt("err: Invalid email address.\n");
}
else if (emailExists(newEmail))
{
errTxt("err: The requested email address is already in use.\n");
}
else if (!userExists(uName, &uId))
{
errTxt("err: The requested user account does not exists.\n");
}
else if (!canModifyUser(uId, rd32BitFromBlock(hostRank), sameRank))
{
errTxt("err: Access denied.\n");
}
else
{
retCode = NO_ERRORS;
Query db(this);
db.setType(Query::UPDATE, TABLE_USERS);
db.addColumn(COLUMN_EMAIL, newEmail);
db.addColumn(COLUMN_EMAIL_VERIFIED, false);
db.addCondition(COLUMN_USER_ID, uId);
db.exec();
async(ASYNC_RW_MY_INFO, uId);
}
}
void OverWriteEmail::procIn(const QByteArray &binIn, quint8 dType)
{
if (dType == TEXT)
{
auto args = parseArgs(binIn, 4);
auto uName = getParam("-user", args);
auto newEmail = getParam("-new_email", args);
procArgs(uName, newEmail, false);
}
}
void ChangeEmail::procIn(const QByteArray &binIn, quint8 dType)
{
if (dType == TEXT)
{
auto args = parseArgs(binIn, 2);
auto newEmail = getParam("-new_email", args);
procArgs(rdStringFromBlock(userName, BLKSIZE_USER_NAME), newEmail, true);
}
}
void PasswordChangeRequest::exec(const QByteArray &uId, bool req)
{
Query db(this);
db.setType(Query::UPDATE, TABLE_USERS);
db.addColumn(COLUMN_NEED_PASS, req);
db.addCondition(COLUMN_USER_ID, uId);
db.exec();
}
void PasswordChangeRequest::procIn(const QByteArray &binIn, quint8 dType)
{
if (dType == TEXT)
{
auto args = parseArgs(binIn, 4);
auto uName = getParam("-user", args);
auto req = getParam("-req", args);
QByteArray uId;
retCode = INVALID_PARAMS;
if (uName.isEmpty())
{
errTxt("err: User name (-user) argument is missing or empty.\n");
}
else if (req.isEmpty())
{
errTxt("err: Request bool (-req) argument is missing or empty.\n");
}
else if (!isBool(req))
{
errTxt("err: The request bool value (-req) must be a 0 or 1.\n");
}
else if (!validUserName(uName))
{
errTxt("err: Invalid user name.\n");
}
else if (!userExists(uName, &uId))
{
errTxt("err: The requested user account does not exists.\n");
}
else if (!canModifyUser(uId, rd32BitFromBlock(hostRank), false))
{
errTxt("err: The target user account out ranks or is equal to your own rank. access denied.\n");
}
else
{
retCode = NO_ERRORS;
exec(uId, static_cast(req.toUInt()));
}
}
}
void NameChangeRequest::exec(const QByteArray &uId, bool req)
{
Query db(this);
db.setType(Query::UPDATE, TABLE_USERS);
db.addColumn(COLUMN_NEED_NAME, req);
db.addCondition(COLUMN_USER_ID, uId);
db.exec();
}