Changed the versioning system to a 4 number system that have the first 2 numbers as major.minor for the host application itself and the next 2 numbers (tcp_rev.mod_rev) used by clients and modules to determine compatibility. A full description of this new system has been edited into protocol.md. This new system offically starts at v3.0.0.0. Added the PROMPT data type id that will work exactly like PRIV_TEXT except it tells the client that the command is asking for non-private information from the user. Added PROG and PROG_LAST type ids that can be used by commands to notify the client of the progress of the command if it is long running. The long running fs_* commands were updated to use these instead of TEXT for progress updates. PUB_IPC, PRIV_IPC and PUB_IPC_WITH_FEEDBACK have all been combined into one: ASYNC_PAYLOAD. This type id is now the only means at which module commands can now run async commands. The command process object will now determine where to direct the async payload (public, private or public with feedback) based on the async command id being requested. A description for TERM_CMD was missing in data_types.md so it was added. Refactored HALT_CMD to YIELD_CMD. The new name just seems more appropriate or the effect it has on the command. Module commands can now do input hooking using the new ASYNC_HOOK_INPUT and ASYNC_UNHOOK async commands. input hooking basically makes it so all client data gets redirected to the module command that initiated the hook. This can be used to implement something like a EULA agreement that blocks all actions that can place during the session until the user accepts or anything else to that effect. The command process object will now check the open sub-channels list being sent by ASYNC_CAST or ASYNC_LIMITED_CAST in any order and will not be required match exactly to open sub-channels list in the session object. It however cannot contain sub-channels not already listed in session's list or else the async payload will be blocked. Fixed the CmdProcess::validAsync() function that was comparing the input aysnc command id with the process's command id in some places which is invalid logic for this function. Fixed the 'cast' core command that was outputting a malformed async payload that didn't include the open writable sub-channels list. Fixed a bug that caused all casted payloads to be forwared to the clients even when the sub-channel(s) are closed. Fixed the 'set_disp_name' core command so it can now see the -new_name argument properly.
439 lines
14 KiB
C++
439 lines
14 KiB
C++
#include "cast.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
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
Cast::Cast(QObject *parent) : CmdObject(parent) {}
|
|
OpenSubChannel::OpenSubChannel(QObject *parent) : CmdObject(parent) {}
|
|
CloseSubChannel::CloseSubChannel(QObject *parent) : CmdObject(parent) {}
|
|
LsOpenChannels::LsOpenChannels(QObject *parent) : CmdObject(parent) {}
|
|
PingPeers::PingPeers(QObject *parent) : CmdObject(parent) {}
|
|
AddRDOnlyFlag::AddRDOnlyFlag(QObject *parent) : CmdObject(parent) {}
|
|
RemoveRDOnlyFlag::RemoveRDOnlyFlag(QObject *parent) : CmdObject(parent) {}
|
|
|
|
ListRDonlyFlags::ListRDonlyFlags(QObject *parent) : TableViewer(parent)
|
|
{
|
|
setParams(TABLE_RDONLY_CAST, false);
|
|
addJointColumn(TABLE_CHANNELS, COLUMN_CHANNEL_ID);
|
|
addTableColumn(TABLE_CHANNELS, COLUMN_CHANNEL_NAME);
|
|
addTableColumn(TABLE_RDONLY_CAST, COLUMN_CHANNEL_ID);
|
|
addTableColumn(TABLE_RDONLY_CAST, COLUMN_SUB_CH_ID);
|
|
addTableColumn(TABLE_RDONLY_CAST, COLUMN_ACCESS_LEVEL);
|
|
}
|
|
|
|
QString Cast::cmdName() {return "cast";}
|
|
QString OpenSubChannel::cmdName() {return "open_sub_ch";}
|
|
QString CloseSubChannel::cmdName() {return "close_sub_ch";}
|
|
QString LsOpenChannels::cmdName() {return "ls_open_chs";}
|
|
QString PingPeers::cmdName() {return "ping_peers";}
|
|
QString AddRDOnlyFlag::cmdName() {return "add_rdonly_flag";}
|
|
QString RemoveRDOnlyFlag::cmdName() {return "rm_rdonly_flag";}
|
|
QString ListRDonlyFlags::cmdName() {return "ls_rdonly_flags";}
|
|
|
|
bool canOpenSubChannel(const QByteArray &uId, const char *override, quint64 chId, quint8 subId)
|
|
{
|
|
auto uLevel = channelAccessLevel(uId, override, chId);
|
|
auto sLevel = lowestAcessLevel(chId, subId);
|
|
|
|
return uLevel <= sLevel;
|
|
}
|
|
|
|
int lowestAcessLevel(quint64 chId, quint8 subId)
|
|
{
|
|
auto ret = 5000;
|
|
|
|
Query db;
|
|
|
|
db.setType(Query::PULL, TABLE_SUB_CHANNELS);
|
|
db.addColumn(COLUMN_LOWEST_LEVEL);
|
|
db.addCondition(COLUMN_CHANNEL_ID, chId);
|
|
db.addCondition(COLUMN_SUB_CH_ID, subId);
|
|
db.exec();
|
|
|
|
if (db.rows())
|
|
{
|
|
ret = db.getData(COLUMN_LOWEST_LEVEL).toInt();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void Cast::procIn(const QByteArray &binIn, quint8 dType)
|
|
{
|
|
async(ASYNC_CAST, rdFromBlock(openWritableSubChs, BLKSIZE_SUB_CHANNEL * MAX_OPEN_SUB_CHANNELS) + wrInt(dType, 8) + binIn);
|
|
}
|
|
|
|
void OpenSubChannel::procIn(const QByteArray &binIn, quint8 dType)
|
|
{
|
|
if (dType == TEXT)
|
|
{
|
|
auto args = parseArgs(binIn, 4);
|
|
auto ch = getParam("-ch_name", args);
|
|
auto sub = getParam("-sub_name", args);
|
|
|
|
quint64 chId;
|
|
quint8 subId;
|
|
|
|
retCode = INVALID_PARAMS;
|
|
|
|
if (ch.isEmpty())
|
|
{
|
|
errTxt("err: Channel name (-ch_name) argument not found or is empty.\n");
|
|
}
|
|
else if (sub.isEmpty())
|
|
{
|
|
errTxt("err: Sub-Channel name (-sub_name) argument not found or is empty.\n");
|
|
}
|
|
else if (!channelExists(ch, &chId))
|
|
{
|
|
errTxt("err: The requested channel does not exists.\n");
|
|
}
|
|
else if (!channelSubExists(chId, sub, &subId))
|
|
{
|
|
errTxt("err: The requested sub-channel does not exists.\n");
|
|
}
|
|
else if (!canOpenSubChannel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId, subId))
|
|
{
|
|
errTxt("err: Access denied.\n");
|
|
}
|
|
else
|
|
{
|
|
retCode = NO_ERRORS;
|
|
|
|
async(ASYNC_OPEN_SUBCH, wrInt(chId, 64) + wrInt(subId, 8));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CloseSubChannel::procIn(const QByteArray &binIn, quint8 dType)
|
|
{
|
|
if (dType == TEXT)
|
|
{
|
|
auto args = parseArgs(binIn, 4);
|
|
auto ch = getParam("-ch_name", args);
|
|
auto sub = getParam("-sub_name", args);
|
|
|
|
quint64 chId;
|
|
quint8 subId;
|
|
|
|
retCode = INVALID_PARAMS;
|
|
|
|
if (ch.isEmpty())
|
|
{
|
|
errTxt("err: Channel name (-ch_name) argument not found or is empty.\n");
|
|
}
|
|
else if (sub.isEmpty())
|
|
{
|
|
errTxt("err: Sub-Channel name (-sub_name) argument not found or is empty.\n");
|
|
}
|
|
else if (!channelExists(ch, &chId))
|
|
{
|
|
errTxt("err: The requested channel does not exists.\n");
|
|
}
|
|
else if (!channelSubExists(chId, sub, &subId))
|
|
{
|
|
errTxt("err: The requested sub-channel does not exists.\n");
|
|
}
|
|
else
|
|
{
|
|
retCode = NO_ERRORS;
|
|
|
|
async(ASYNC_CLOSE_SUBCH, wrInt(chId, 64) + wrInt(subId, 8));
|
|
}
|
|
}
|
|
}
|
|
|
|
void LsOpenChannels::procIn(const QByteArray &binIn, quint8 dType)
|
|
{
|
|
Q_UNUSED(binIn)
|
|
|
|
if (dType == TEXT)
|
|
{
|
|
Query db;
|
|
QList<QStringList> tableData;
|
|
QStringList separators;
|
|
QList<int> justLens;
|
|
|
|
for (int i = 0; i < 5; ++i)
|
|
{
|
|
justLens.append(14);
|
|
separators.append("-------");
|
|
}
|
|
|
|
tableData.append(QStringList() << COLUMN_CHANNEL_NAME << COLUMN_SUB_CH_NAME << COLUMN_CHANNEL_ID << COLUMN_SUB_CH_ID << "read_only");
|
|
tableData.append(separators);
|
|
|
|
for (int i = 0; i < MAX_OPEN_SUB_CHANNELS; i += BLKSIZE_SUB_CHANNEL)
|
|
{
|
|
auto chId = rd64BitFromBlock(openSubChs + i);
|
|
auto subId = rd8BitFromBlock(openSubChs + (i + 8));
|
|
|
|
if (chId)
|
|
{
|
|
QStringList columnData;
|
|
|
|
db.setType(Query::INNER_JOIN_PULL, TABLE_SUB_CHANNELS);
|
|
db.addTableColumn(TABLE_SUB_CHANNELS, COLUMN_SUB_CH_NAME);
|
|
db.addTableColumn(TABLE_CHANNELS, COLUMN_CHANNEL_NAME);
|
|
db.addJoinCondition(COLUMN_CHANNEL_ID, TABLE_CHANNELS);
|
|
db.addCondition(COLUMN_CHANNEL_ID, chId);
|
|
db.addCondition(COLUMN_SUB_CH_ID, subId);
|
|
db.exec();
|
|
|
|
auto chName = db.getData(COLUMN_CHANNEL_NAME).toString();
|
|
auto subName = db.getData(COLUMN_SUB_CH_NAME).toString();
|
|
|
|
QString rdOnly;
|
|
|
|
if (posOfBlock(openSubChs + i, openWritableSubChs, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL) == -1)
|
|
{
|
|
rdOnly = "1";
|
|
}
|
|
else
|
|
{
|
|
rdOnly = "0";
|
|
}
|
|
|
|
columnData.append(chName);
|
|
columnData.append(subName);
|
|
columnData.append(QString::number(chId));
|
|
columnData.append(QString::number(subId));
|
|
columnData.append(rdOnly);
|
|
|
|
for (int k = 0; k < justLens.size(); ++k)
|
|
{
|
|
if (justLens[k] < columnData[k].size()) justLens[k] = columnData[k].size();
|
|
}
|
|
|
|
tableData.append(columnData);
|
|
}
|
|
}
|
|
|
|
mainTxt("\n");
|
|
|
|
for (auto&& row : tableData)
|
|
{
|
|
for (int i = 0; i < row.size(); ++i)
|
|
{
|
|
mainTxt(row[i].leftJustified(justLens[i] + 4, ' '));
|
|
}
|
|
|
|
mainTxt("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void PingPeers::procIn(const QByteArray &binIn, quint8 dType)
|
|
{
|
|
Q_UNUSED(binIn)
|
|
|
|
if (dType == TEXT)
|
|
{
|
|
if (rd8BitFromBlock(activeUpdate) == 0)
|
|
{
|
|
retCode = INVALID_PARAMS;
|
|
|
|
errTxt("err: You don't currently have any active update sub-channels open. sending a ping request is pointless because peers won't be able to respond.\n");
|
|
}
|
|
else
|
|
{
|
|
async(ASYNC_PING_PEERS);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AddRDOnlyFlag::procIn(const QByteArray &binIn, quint8 dType)
|
|
{
|
|
if (dType == TEXT)
|
|
{
|
|
auto args = parseArgs(binIn, 6);
|
|
auto chName = getParam("-ch_name", args);
|
|
auto subId = getParam("-sub_id", args);
|
|
auto level = getParam("-level", args);
|
|
|
|
quint64 chId;
|
|
|
|
retCode = INVALID_PARAMS;
|
|
|
|
if (chName.isEmpty())
|
|
{
|
|
errTxt("err: The channel name (-ch_name) argument was not found or is empty.\n");
|
|
}
|
|
else if (subId.isEmpty())
|
|
{
|
|
errTxt("err: The sub-channel id (-sub_id) was not found or is empty.\n");
|
|
}
|
|
else if (level.isEmpty())
|
|
{
|
|
errTxt("err: The privilage level (-level) argument was not found or is empty.\n");
|
|
}
|
|
else if (!validChName(chName))
|
|
{
|
|
errTxt("err: Invalid channel name.\n");
|
|
}
|
|
else if (!validSubId(subId))
|
|
{
|
|
errTxt("err: Invalid sub-channel id. valid range (0-255).\n");
|
|
}
|
|
else if (!validLevel(level, true))
|
|
{
|
|
errTxt("err: Invalid privilage level. valid range (1-5).\n");
|
|
}
|
|
else if (!channelExists(chName, &chId))
|
|
{
|
|
errTxt("err: Channel name '" + chName + "' does not exists.\n");
|
|
}
|
|
else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > ADMIN)
|
|
{
|
|
errTxt("err: Access denied.\n");
|
|
}
|
|
else if (rdOnlyFlagExists(chName, static_cast<quint8>(subId.toInt()), level.toInt()))
|
|
{
|
|
errTxt("err: A read only flag for sub_id: " + QString::number(subId.toInt()) + " level: " + QString::number(level.toInt()) + " already exists.\n");
|
|
}
|
|
else
|
|
{
|
|
retCode = NO_ERRORS;
|
|
|
|
auto frame = wrInt(chId, 64) + wrInt(subId.toUInt(), 8) + wrInt(level.toUInt(), 8);
|
|
|
|
Query db(this);
|
|
|
|
db.setType(Query::PUSH, TABLE_RDONLY_CAST);
|
|
db.addColumn(COLUMN_CHANNEL_ID, chId);
|
|
db.addColumn(COLUMN_SUB_CH_ID, subId.toInt());
|
|
db.addColumn(COLUMN_ACCESS_LEVEL, level.toInt());
|
|
db.exec();
|
|
|
|
async(ASYNC_ADD_RDONLY, frame);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RemoveRDOnlyFlag::procIn(const QByteArray &binIn, quint8 dType)
|
|
{
|
|
if (dType == TEXT)
|
|
{
|
|
auto args = parseArgs(binIn, 6);
|
|
auto chName = getParam("-ch_name", args);
|
|
auto subId = getParam("-sub_id", args);
|
|
auto level = getParam("-level", args);
|
|
|
|
quint64 chId;
|
|
|
|
retCode = INVALID_PARAMS;
|
|
|
|
if (chName.isEmpty())
|
|
{
|
|
errTxt("err: The channel name (-ch_name) argument was not found or is empty.\n");
|
|
}
|
|
else if (subId.isEmpty())
|
|
{
|
|
errTxt("err: The sub-channel id (-sub_id) was not found or is empty.\n");
|
|
}
|
|
else if (level.isEmpty())
|
|
{
|
|
errTxt("err: The privilage level (-level) argument was not found or is empty.\n");
|
|
}
|
|
else if (!validChName(chName))
|
|
{
|
|
errTxt("err: Invalid channel name.\n");
|
|
}
|
|
else if (!validSubId(subId))
|
|
{
|
|
errTxt("err: Invalid sub-channel id. valid range (0-255).\n");
|
|
}
|
|
else if (!validLevel(level, true))
|
|
{
|
|
errTxt("err: Invalid privilage level. valid range (1-5).\n");
|
|
}
|
|
else if (!channelExists(chName, &chId))
|
|
{
|
|
errTxt("err: Channel name '" + chName + "' does not exists.\n");
|
|
}
|
|
else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > ADMIN)
|
|
{
|
|
errTxt("err: Access denied.\n");
|
|
}
|
|
else if (!rdOnlyFlagExists(chName, static_cast<quint8>(subId.toInt()), level.toInt()))
|
|
{
|
|
errTxt("err: A read only flag for sub_id: " + QString::number(subId.toInt()) + " level: " + QString::number(level.toInt()) + " does not exists.\n");
|
|
}
|
|
else
|
|
{
|
|
retCode = NO_ERRORS;
|
|
|
|
auto frame = wrInt(chId, 64) + wrInt(subId.toUInt(), 8) + wrInt(level.toUInt(), 8);
|
|
|
|
Query db(this);
|
|
|
|
db.setType(Query::DEL, TABLE_RDONLY_CAST);
|
|
db.addCondition(COLUMN_CHANNEL_ID, chId);
|
|
db.addCondition(COLUMN_SUB_CH_ID, subId.toInt());
|
|
db.addCondition(COLUMN_ACCESS_LEVEL, level.toInt());
|
|
db.exec();
|
|
|
|
async(ASYNC_RM_RDONLY, frame);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ListRDonlyFlags::procIn(const QByteArray &binIn, quint8 dType)
|
|
{
|
|
if (dType == TEXT)
|
|
{
|
|
if (flags & MORE_INPUT)
|
|
{
|
|
TableViewer::procIn(binIn, dType);
|
|
}
|
|
else
|
|
{
|
|
auto args = parseArgs(binIn, 2);
|
|
auto chName = getParam("-ch_name", args);
|
|
|
|
quint64 chId;
|
|
|
|
retCode = INVALID_PARAMS;
|
|
|
|
if (chName.isEmpty())
|
|
{
|
|
errTxt("err: The channel name (-ch_name) argument was not found or is empty.\n");
|
|
}
|
|
else if (!validChName(chName))
|
|
{
|
|
errTxt("err: Invalid channel name.\n");
|
|
}
|
|
else if (!channelExists(chName, &chId))
|
|
{
|
|
errTxt("err: Channel name '" + chName + "' does not exists.\n");
|
|
}
|
|
else
|
|
{
|
|
retCode = NO_ERRORS;
|
|
|
|
if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > REGULAR)
|
|
{
|
|
TableViewer::procIn(toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName + " -" + QString(COLUMN_LOWEST_LEVEL) + " " + QString::number(PUBLIC)), dType);
|
|
}
|
|
else
|
|
{
|
|
TableViewer::procIn(toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName), dType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|