Unicode text, database and a few protocol changes

changed the TEXT type id format from UTF16LE to UTF8 (no BOM). this
included all displayable text used throughout this project. doing
this reduced overhead because UTF16 strings required 2 bytes per
char while UTF8 runs on 1 byte for char. this string format also
expands support for QTs built in Postgresql driver or any driver
that supports UTF8 unicode only.

added "mod_instructions" to the client header format so the client
applications themselves can send direct command line args to the
modules loaded by the host. note: main.cpp needed to be modified
to check for core module parameters before checking the additional
parameters sent by the client; doing this protects against possible
unintentional core parameters being sent by the client.

added a Q_OS_WINDOWS check to applink.c so this file is completely
ignored when compiling on a Linux platform.

all commands that send emails will now return an appropriate error
message if the internal email client fails for any reason.

added a db_settings.json file that the host will now check for to
get database parameters such as hostname, username, driver and
password. doing this opens up the host to other database drivers
other than SQLITE. if not present, the host will create a default
db_settings file that uses SQLITE.

added the -ls_sql_drvs command line arg that will list all of the
SQL drivers that the host currently has installed and can be used
in the db_settings file.
This commit is contained in:
Maurice ONeal 2020-08-09 12:21:33 -04:00
parent f62eb125cf
commit bdde52da76
29 changed files with 452 additions and 336 deletions

View File

@ -125,6 +125,8 @@ def linux_build_app_dir(app_ver, app_name, app_target, qt_bin):
os.makedirs("app_dir/linux/lib")
verbose_copy(qt_bin + "/../plugins/sqldrivers/libqsqlite.so", "app_dir/linux/sqldrivers/libqsqlite.so")
verbose_copy(qt_bin + "/../plugins/sqldrivers/libqsqlodbc.so", "app_dir/linux/sqldrivers/libqsqlodbc.so")
verbose_copy(qt_bin + "/../plugins/sqldrivers/libqsqlpsql.so", "app_dir/linux/sqldrivers/libqsqlpsql.so")
verbose_copy("build/linux/" + app_target, "app_dir/linux/" + app_target)
shutil.copyfile("build/linux/" + app_target, "/tmp/" + app_target)

View File

@ -58,18 +58,21 @@ Any increments to the Major resets the Minor to 0. Any 3rd party client applicat
### 1.4 Client Header ###
```
[tag][appName][padding]
[tag][appName][mod_instructions][padding]
tag - 4bytes - 0x4D, 0x52, 0x43, 0x49 (MRCI)
appName - 134bytes - UTF16LE string (padded with 0x00)
padding - 272bytes - padding of 0x00 bytes reserved for future expansion
appName - 32bytes - UTF8 string (padded with 0x00)
modInst - 128bytes - UTF8 string (padded with 0x00)
padding - 128bytes - string of (0x00)
```
notes:
* The **tag** is just a fixed ascii string "MRCI" that indicates to the host that the client is indeed attempting to use the MRCI protocol.
* **tag** is just a fixed ascii string "MRCI" that indicates to the host that the client is indeed attempting to use the MRCI protocol.
* The **appName** is the name of the client application that is connected to the host. It can also contain the client's app version if needed because it doesn't follow any particular standard. This string is accessable to all modules so the commands themselves can be made aware of what app the user is currently using.
* **appName** is the name of the client application that is connected to the host. It can also contain the client's app version if needed because it doesn't follow any particular standard. This string is accessable to all modules so the commands themselves can be made aware of what app the user is currently using.
* **modInst** is an additional set of command lines that can be passed onto to all module processes when they are intialized. This can be used by certain clients that want to intruct certain modules that might be installed in the host to do certain actions during intialization. This remains constant for as long as the session is active and cannot be changed at any point.
### 1.5 Host Header ###

View File

@ -42,7 +42,7 @@ enum TypeID : quint8
```TEXT```
This is text that can be displayed directly to the user, passed as command line arguments to be processed or used to carry text data within other data types.
format: ```[UTF-16LE_string] (no BOM)```
format: ```[UTF-8_string] (no BOM)```
```GEN_FILE```
This is a file transfer type id that can be used to transfer any file type (music, photos, documents, etc...). It operates in its own protocol of sorts. The 1st GEN_FILE frame received by the host or client is TEXT parameters similar to what you see in terminal command lines with at least one of the arguments listed below. The next set of GEN_FILE frames received by the host or client is then the binary data that needs to be written to an open file or streamed until the limit defined in -len is meet.
@ -77,7 +77,7 @@ This id can be treated exactly like TEXT except this tells the client that the c
This is similar to PRIV_TEXT expect it is not asking for private information. It is simply prompting for non-sensitive information from the user.
```BIG_TEXT```
Also formatted exactly like TEXT but this indicates to the client that this is a large body of text that is recommended to be word wrapped when displaying to the user. It can contain line breaks so clients are also recommended to honor those line breaks.
Also formatted exactly like TEXT but this indicates to the client that this is a large body of text that is recommended to be word wrapped when displaying to the user. It can contain line breaks so clients are recommended to honor those line breaks.
```IDLE```
All commands started during the session returns this type id when it has finished it's task. It carries a 16bit unsigned integer indicating the result of the task that the command was running.
@ -97,13 +97,13 @@ enum RetCode : quint16
notes:
1. the custom return code can be additional data added to the end of the 16bit
integer that can carry additional data about the result of the task. it can
be any format that the module itself decides it can should be. nothing is
stopping modules from defining return codes beyond the value of 7 but it is
advised not to because this enum might be expanded in the future.
be any format that the module itself decides it to be. nothing is stopping
modules from defining return codes beyond the value of 7 but it is advised
not to because this enum might be expanded in the future.
```
```TERM_CMD```
This type id doesn't carry any actual data. It is used to tell the host to stop/terminate the command id and branch id that was used to send it. It does not actually terminate the module's process within the host, it only simply tells it to stop what it is currently doing. This will also terminate any commands in a prompt/more input state.
This type id doesn't carry any actual data. It is used to tell the host to stop/terminate the command id and branch id that was used to send it. It does not actually terminate the module's process within the host, it will only tell it to stop what it is currently doing. This will also terminate any commands in a prompt state.
```KILL_CMD```
This works similarly to TERM_CMD except it will also terminate the module process. The module process will have 3 seconds to shutdown gracefully before it is force killed by the host session.
@ -143,12 +143,12 @@ This is a data structure that carries information about a file system object (fi
2. bytes[1-8] - creation time in msec since Epoch UTC (64bit little endian uint)
3. bytes[9-16] - modification time in msec since Epoch UTC (64bit little endian uint)
4. bytes[17-24] - file size (64bit little endian uint)
5. bytes[25-n] - file name (UTF16-LE string, 16bit terminated)
6. bytes[n-n] - symmlink target if it is a symmlink (UTF16-LE string, 16bit terminated)
5. bytes[25-n] - file name (UTF8 string, NULL terminated)
6. bytes[n-n] - symmlink target if it is a symmlink (UTF8 string, NULL terminated)
notes:
1. 16bit terminated UTF-16LE strings are basically
terminated by 2 bytes of 0x00.
1. NULL terminated UTF-8 strings are basically tailed
by a single 0x00 byte to indicate the end-of-string.
2. the symmlink target is empty if not a symmlink but
the terminator should still be present.
@ -171,9 +171,9 @@ This carry some user account and session information about a peer client connect
format:
1. bytes[0-27] 28bytes - session id (224bit hash)
2. bytes[28-59] 32bytes - user id (256bit hash)
3. bytes[60-107] 48bytes - user name (TEXT - padded with 0x00)
4. bytes[108-241] 134bytes - app name (TEXT - padded with 0x00)
5. bytes[242-305] 64bytes - disp name (TEXT - padded with 0x00)
3. bytes[60-83] 24bytes - user name (TEXT - padded with 0x00)
4. bytes[84-115] 32bytes - app name (TEXT - padded with 0x00)
5. bytes[116-139] 24bytes - disp name (TEXT - padded with 0x00)
notes:
1. the session id is unique to the peer's session connection only. it
@ -183,23 +183,23 @@ This carry some user account and session information about a peer client connect
even when the user name changes and across all clients logged into
the same account.
3. the display name is the preffered display name of the peer. clients
3. the display name is the preferred display name of the peer. clients
are encouraged to use this rather than the user name when displaying
peer info to the user. if empty, it's ok to just fall back to the user
name.
peer info to the end user. if empty, then it is ok to fall back to
displaying the user name.
```
```PING_PEERS```
This is formatted extactly as PEER_INFO except it is used by the ASYNC_LIMITED_CAST [async](async.md) command to tell all peer sessions that receive it to send a PEER_INFO frame about the local session to their own clients and return PEER_INFO frames about themselves to the local session.
This is formatted extactly as PEER_INFO except it is used by the ASYNC_LIMITED_CAST [async](async.md) command to tell all peer sessions that receive it to forward the same frame to their own clients and return PEER_INFO frames about themselves to the session that sent the PING_PEERS.
```MY_INFO```
This contains all of the information found in ```PEER_INFO``` for the local session but also includes the following:
```
format:
1. bytes[306-433] 128bytes - email (TEXT - padded with 0x00)
2. bytes[434-437] 4bytes - host rank (32bit unsigned int)
3. bytes[438] 1byte - is email confirmed? (0x00 false, 0x01 true)
1. bytes[140-203] 64bytes - email (TEXT - padded with 0x00)
2. bytes[204-207] 4bytes - host rank (32bit unsigned int)
3. bytes[208] 1byte - is email confirmed? (0x00 false, 0x01 true)
```
```NEW_CMD```
@ -209,11 +209,11 @@ This contains information about a new command that was added to the current sess
format:
1. bytes[0-1] 2bytes - 16bit LE unsigned int (command id)
2. bytes[2] 1byte - 8bit LE unsigned int (genfile type)
3. bytes[3-130] 128bytes - command name (TEXT - padded with 0x00)
4. bytes[131-258] 128bytes - library name (TEXT - padded with 0x00)
5. bytes[259-n] variable - short text (16bit null terminated)
6. bytes[n-n] variable - io text (16bit null terminated)
7. bytes[n-n] variable - long text (16bit null terminated)
3. bytes[3-66] 64bytes - command name (TEXT - padded with 0x00)
4. bytes[67-130] 64bytes - library name (TEXT - padded with 0x00)
5. bytes[131-n] variable - short text (null terminated)
6. bytes[n-n] variable - io text (null terminated)
7. bytes[n-n] variable - long text (null terminated)
notes:
1. the genfile type is numerical value of 2, 3 or 0. a value of 2
@ -288,13 +288,13 @@ This contains public information about a channel member.
2. bytes[8-39] 32bytes - user id (256bit hash)
3. bytes[40] 1byte - is invite? (0x00=false, 0x01=true)
4. bytes[41] 1byte - member's channel privilege level (8bit unsigned int)
5. bytes[42-n] variable - user name (TEXT - 16bit null terminated)
6. bytes[n-n] variable - display name (TEXT - 16bit null terminated)
7. bytes[n-n] variable - channel name (TEXT - 16bit null terminated)
5. bytes[42-n] variable - user name (TEXT - null terminated)
6. bytes[n-n] variable - display name (TEXT - null terminated)
7. bytes[n-n] variable - channel name (TEXT - null terminated)
notes:
1. a 16bit null terminated TEXT formatted string ended with 2 bytes of
(0x00) to indicate the end of the string data.
1. a null terminated TEXT formatted string ends with a 0x00 to
indicate the end of the string.
2. the member's privilege level can be any of the values discribed in
section [4.3](host_features.md).

View File

@ -7,6 +7,10 @@
* https://www.openssl.org/source/license.html
*/
#include <QtCore>
#ifdef Q_OS_WINDOWS
#define APPLINK_STDIN 1
#define APPLINK_STDOUT 2
#define APPLINK_STDERR 3
@ -136,3 +140,4 @@ OPENSSL_Applink(void)
}
#endif
#endif
#endif

View File

@ -25,8 +25,8 @@ void Session::acctDeleted(const QByteArray &data)
if (memcmp(userId, data.data(), BLKSIZE_USER_ID) == 0)
{
logout("", true);
asyncToClient(ASYNC_SYS_MSG, toTEXT("\nsystem: your session was forced to logout because your account was deleted.\n"), TEXT);
asyncToClient(ASYNC_USER_DELETED, data, TEXT);
asyncToClient(ASYNC_SYS_MSG, "\nsystem: your session was forced to logout because your account was deleted.\n", TEXT);
asyncToClient(ASYNC_USER_DELETED, data, BYTES);
}
}
}
@ -48,7 +48,7 @@ void Session::acctRenamed(const QByteArray &data)
{
if (flags & LOGGED_IN)
{
// format: [32bytes(user_id)][48bytes(new_user_name)]
// format: [32bytes(user_id)][24bytes(new_user_name)]
if (memcmp(userId, data.data(), BLKSIZE_USER_ID) == 0)
{
@ -63,7 +63,7 @@ void Session::acctDispChanged(const QByteArray &data)
{
if (flags & LOGGED_IN)
{
// format: [32bytes(user_id)][64bytes(new_disp_name)]
// format: [32bytes(user_id)][24bytes(new_disp_name)]
if (memcmp(userId, data.data(), BLKSIZE_USER_ID) == 0)
{
@ -325,7 +325,7 @@ void Session::subChannelUpdated(quint16 cmdId, const QByteArray &data)
void Session::addModule(const QByteArray &data)
{
auto modApp = fromTEXT(data);
auto modApp = QString::fromUtf8(data);
if (!modCmdNames.contains(modApp))
{
@ -335,7 +335,7 @@ void Session::addModule(const QByteArray &data)
void Session::rmModule(const QByteArray &data)
{
auto modApp = fromTEXT(data);
auto modApp = QString::fromUtf8(data);
if (modCmdNames.contains(modApp) && (modApp != QCoreApplication::applicationFilePath()))
{

View File

@ -197,27 +197,27 @@ void CmdObject::postProc()
void CmdObject::mainTxt(const QString &txt)
{
emit procOut(toTEXT(txt), TEXT);
emit procOut(txt.toUtf8(), TEXT);
}
void CmdObject::errTxt(const QString &txt)
{
emit procOut(toTEXT(txt), ERR);
emit procOut(txt.toUtf8(), ERR);
}
void CmdObject::privTxt(const QString &txt)
{
emit procOut(toTEXT(txt), PRIV_TEXT);
emit procOut(txt.toUtf8(), PRIV_TEXT);
}
void CmdObject::bigTxt(const QString &txt)
{
emit procOut(toTEXT(txt), BIG_TEXT);
emit procOut(txt.toUtf8(), BIG_TEXT);
}
void CmdObject::promptTxt(const QString &txt)
{
emit procOut(toTEXT(txt), PROMPT_TEXT);
emit procOut(txt.toUtf8(), PROMPT_TEXT);
}
void CmdObject::async(quint16 asyncId, const QByteArray &data)

View File

@ -49,12 +49,12 @@ ModProcess::ModProcess(const QString &app, const QString &memSes, const QString
void ModProcess::rdFromStdErr()
{
emit dataToClient(toCmdId32(ASYNC_SYS_MSG, 0), toTEXT(readAllStandardError()), ERR);
emit dataToClient(toCmdId32(ASYNC_SYS_MSG, 0), readAllStandardError(), ERR);
}
void ModProcess::rdFromStdOut()
{
emit dataToClient(toCmdId32(ASYNC_SYS_MSG, 0), toTEXT(readAllStandardOutput()), TEXT);
emit dataToClient(toCmdId32(ASYNC_SYS_MSG, 0), readAllStandardOutput(), TEXT);
}
quint16 ModProcess::genCmdId()
@ -124,11 +124,11 @@ void ModProcess::onDataFromProc(quint8 typeId, const QByteArray &data)
{
if ((typeId == NEW_CMD) && (flags & SESSION_PARAMS_SET))
{
if (data.size() >= 259)
if (data.size() >= 131)
{
// a valid NEW_CMD must have a minimum of 259 bytes.
// a valid NEW_CMD must have a minimum of 131 bytes.
auto cmdName = fromTEXT(data.mid(3, 128)).trimmed().toLower();
auto cmdName = QString::fromUtf8(data.mid(3, 64)).trimmed().toLower();
if (isCmdLoaded(cmdName))
{
@ -172,7 +172,7 @@ void ModProcess::onDataFromProc(quint8 typeId, const QByteArray &data)
modCmdNames->insert(program(), list);
}
auto frame = cmdIdBa + data.mid(2, 1) + fixedToTEXT(unique, 128) + data.mid(131);
auto frame = cmdIdBa + data.mid(2, 1) + toFixedTEXT(unique, 64) + data.mid(67);
emit dataToClient(toCmdId32(ASYNC_ADD_CMD, 0), frame, NEW_CMD);
}
@ -180,7 +180,7 @@ void ModProcess::onDataFromProc(quint8 typeId, const QByteArray &data)
}
else if (typeId == ERR)
{
qDebug() << fromTEXT(data);
qDebug() << QString::fromUtf8(data);
}
}
@ -267,7 +267,7 @@ void ModProcess::setSessionParams(QHash<quint16, QString> *uniqueNames,
void ModProcess::onFailToStart()
{
emit dataToClient(toCmdId32(ASYNC_SYS_MSG, 0), toTEXT("\nerr: A module failed to start so some commands may not have loaded. detailed error information was logged for admin review.\n"), ERR);
emit dataToClient(toCmdId32(ASYNC_SYS_MSG, 0), "\nerr: A module failed to start so some commands may not have loaded. detailed error information was logged for admin review.\n", ERR);
emit modProcFinished();
deleteLater();
@ -307,7 +307,7 @@ bool ModProcess::startProc(const QStringList &args)
{
fullPipe = ipcServ->fullServerName();
setArguments(QStringList() << "-pipe_name" << fullPipe << "-mem_ses" << sesMemKey << "-mem_host" << hostMemKey << args);
setArguments(QStringList() << "-pipe_name" << fullPipe << "-mem_ses" << sesMemKey << "-mem_host" << hostMemKey << args << additionalArgs);
start();
}
else
@ -320,6 +320,11 @@ bool ModProcess::startProc(const QStringList &args)
return ret;
}
void ModProcess::addArgs(const QString &cmdLine)
{
additionalArgs = parseArgs(cmdLine.toUtf8(), -1);
}
bool ModProcess::loadPublicCmds()
{
flags |= LOADING_PUB_CMDS;
@ -434,7 +439,7 @@ void CmdProcess::onReady()
void CmdProcess::onFailToStart()
{
emit dataToClient(cmdId, toTEXT("err: The command failed to start. error details were logged for admin review.\n"), ERR);
emit dataToClient(cmdId, "err: The command failed to start. error details were logged for admin review.\n", ERR);
emit dataToClient(cmdId, wrInt(FAILED_TO_START, 16), IDLE);
emit cmdProcFinished(cmdId);
@ -448,7 +453,7 @@ void CmdProcess::onFinished(int exitCode, QProcess::ExitStatus exitStatus)
if (!cmdIdle)
{
emit dataToClient(cmdId, toTEXT("err: The command has stopped unexpectedly or it has failed to send an IDLE frame before exiting.\n"), ERR);
emit dataToClient(cmdId, "err: The command has stopped unexpectedly or it has failed to send an IDLE frame before exiting.\n", ERR);
emit dataToClient(cmdId, wrInt(CRASH, 16), IDLE);
}
@ -460,12 +465,12 @@ void CmdProcess::onFinished(int exitCode, QProcess::ExitStatus exitStatus)
void CmdProcess::rdFromStdErr()
{
emit dataToClient(cmdId, toTEXT(readAllStandardError()), ERR);
emit dataToClient(cmdId, readAllStandardError(), ERR);
}
void CmdProcess::rdFromStdOut()
{
emit dataToClient(cmdId, toTEXT(readAllStandardOutput()), TEXT);
emit dataToClient(cmdId, readAllStandardOutput(), TEXT);
}
void CmdProcess::dataFromSession(quint32 id, const QByteArray &data, quint8 dType)

View File

@ -46,6 +46,7 @@ protected:
quint32 ipcDataSize;
quint32 hostRank;
quint32 flags;
QStringList additionalArgs;
IdleTimer *idleTimer;
QLocalServer *ipcServ;
QLocalSocket *ipcSocket;
@ -75,6 +76,7 @@ public:
explicit ModProcess(const QString &app, const QString &memSes, const QString &memHos, const QString &pipe, QObject *parent = nullptr);
void addArgs(const QString &cmdLine);
void setSessionParams(QHash<quint16, QString> *uniqueNames,
QHash<quint16, QString> *realNames,
QHash<quint16, QString> *appById,

View File

@ -115,7 +115,7 @@ void RecoverAcct::procIn(const QByteArray &binIn, quint8 dType)
{
if ((flags & MORE_INPUT) && (dType == TEXT))
{
auto pw = fromTEXT(binIn);
auto pw = QString::fromUtf8(binIn);
if (inputOk)
{
@ -265,10 +265,15 @@ void ResetPwRequest::procIn(const QByteArray &binIn, uchar dType)
cmdLine.replace(SUBJECT_SUB, "'" + escapeChars(subject, '\\', '\'') + "'");
cmdLine.replace(MSG_SUB, "'" + escapeChars(body, '\\', '\'') + "'");
QProcess::startDetached(expandEnvVariables(app), parseArgs(toTEXT(cmdLine), -1));
if (QProcess::startDetached(expandEnvVariables(app), parseArgs(cmdLine.toUtf8(), -1)))
{
mainTxt("A temporary password was sent to the email address associated with the account. this password will expire in 1hour.\n");
}
else
{
errTxt("err: The host email system has reported an internal error, try again later.\n");
}
}
}
}
@ -276,7 +281,7 @@ void VerifyEmail::procIn(const QByteArray &binIn, quint8 dType)
{
if ((flags & MORE_INPUT) && (dType == TEXT))
{
auto txt = fromTEXT(binIn);
auto txt = QString::fromUtf8(binIn);
if (txt.isEmpty())
{
@ -343,10 +348,15 @@ void VerifyEmail::procIn(const QByteArray &binIn, quint8 dType)
cmdLine.replace(SUBJECT_SUB, "'" + escapeChars(subject, '\\', '\'') + "'");
cmdLine.replace(MSG_SUB, "'" + escapeChars(body, '\\', '\'') + "'");
QProcess::startDetached(expandEnvVariables(app), parseArgs(toTEXT(cmdLine), -1));
if (QProcess::startDetached(expandEnvVariables(app), parseArgs(cmdLine.toUtf8(), -1)))
{
privTxt("A confirmation code was sent to your email address: " + email + "\n\n" + "Please enter that code now or leave blank to cancel: ");
}
else
{
errTxt("err: The host email system has reported an internal error, try again later.\n");
}
}
}
}
@ -373,7 +383,7 @@ void SetEmailTemplate::procIn(const QByteArray &binIn, quint8 dType)
{
if ((flags & MORE_INPUT) && (dType == GEN_FILE))
{
bodyText.append(fromTEXT(binIn));
bodyText.append(QString::fromUtf8(binIn));
dataSent += binIn.size();

View File

@ -30,7 +30,7 @@ void CloseHost::procIn(const QByteArray &binIn, quint8 dType)
{
if (flags & MORE_INPUT)
{
auto input = fromTEXT(binIn);
auto input = QString::fromUtf8(binIn);
if (input == "CLOSE")
{
@ -64,7 +64,7 @@ void RestartHost::procIn(const QByteArray &binIn, quint8 dType)
{
if (flags & MORE_INPUT)
{
auto input = fromTEXT(binIn);
auto input = QString::fromUtf8(binIn);
if (input == "RESTART")
{
@ -127,10 +127,10 @@ void ServSettings::printSettings()
txtOut << "Initial Host Rank: " << db.getData(COLUMN_INITRANK).toUInt() << Qt::endl;
txtOut << "Root User: " << getUserName(rootUserId()) << Qt::endl;
txtOut << "Working Path: " << QDir::currentPath() << Qt::endl;
txtOut << "Database: " << sqlDataPath() << Qt::endl;
txtOut << "Mailer Executable: " << db.getData(COLUMN_MAILERBIN).toString() << Qt::endl;
txtOut << "Mailer Command: " << db.getData(COLUMN_MAIL_SEND).toString() << Qt::endl << Qt::endl;
printDatabaseInfo(txtOut);
mainTxt(txt);
}
@ -178,7 +178,7 @@ void ServSettings::procIn(const QByteArray &binIn, quint8 dType)
auto ok = false;
select = fromTEXT(binIn).toInt(&ok);
select = QString::fromUtf8(binIn).toInt(&ok);
if ((select == 1) && ok)
{
@ -327,7 +327,7 @@ void ServSettings::procIn(const QByteArray &binIn, quint8 dType)
}
else if (level == 2)
{
auto value = fromTEXT(binIn);
auto value = QString::fromUtf8(binIn);
if (value.isEmpty())
{

View File

@ -115,7 +115,7 @@ void Auth::procIn(const QByteArray &binIn, quint8 dType)
{
if (flags & MORE_INPUT)
{
QString text = fromTEXT(binIn);
auto text = QString::fromUtf8(binIn);
if (loginOk)
{
@ -174,7 +174,7 @@ void Auth::procIn(const QByteArray &binIn, quint8 dType)
}
else if (validEmailAddr(text))
{
errTxt("err: Invaild use rname. it looks like an email address.\n");
errTxt("err: Invaild user name. it looks like an email address.\n");
promptTxt("Enter a new user name (leave blank to cancel): ");
}
else if (!validUserName(text))
@ -202,7 +202,7 @@ void Auth::procIn(const QByteArray &binIn, quint8 dType)
db.addCondition(COLUMN_USER_ID, uId);
db.exec();
async(ASYNC_USER_RENAMED, uId + fixedToTEXT(text, BLKSIZE_USER_NAME));
async(ASYNC_USER_RENAMED, uId + toFixedTEXT(text, BLKSIZE_USER_NAME));
uName = text;
newUserName = false;

View File

@ -426,11 +426,11 @@ void ListRDonlyFlags::procIn(const QByteArray &binIn, quint8 dType)
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);
TableViewer::procIn(QString("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName + " -" + QString(COLUMN_LOWEST_LEVEL) + " " + QString::number(PUBLIC)).toUtf8(), dType);
}
else
{
TableViewer::procIn(toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName), dType);
TableViewer::procIn(QString("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName).toUtf8(), dType);
}
}
}

View File

@ -123,7 +123,7 @@ void ListChannels::procIn(const QByteArray &binIn, quint8 dType)
}
else
{
TableViewer::procIn(toTEXT("-" + QString(COLUMN_USERNAME) + " " + rdStringFromBlock(userName, BLKSIZE_USER_NAME)), dType);
TableViewer::procIn(QString("-" + QString(COLUMN_USERNAME) + " " + rdStringFromBlock(userName, BLKSIZE_USER_NAME)).toUtf8(), dType);
}
}
@ -156,11 +156,11 @@ void ListSubCh::procIn(const QByteArray &binIn, quint8 dType)
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);
TableViewer::procIn(QString("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName + " -" + QString(COLUMN_LOWEST_LEVEL) + " " + QString::number(PUBLIC)).toUtf8(), dType);
}
else
{
TableViewer::procIn(toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName), dType);
TableViewer::procIn(QString("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName).toUtf8(), dType);
}
}
}
@ -194,13 +194,13 @@ void SearchChannels::procIn(const QByteArray &binIn, quint8 dType)
{
retCode = NO_ERRORS;
TableViewer::procIn(toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + name), dType);
TableViewer::procIn(QString("-" + QString(COLUMN_CHANNEL_NAME) + " " + name).toUtf8(), dType);
}
else if (!chId.isEmpty())
{
retCode = NO_ERRORS;
TableViewer::procIn(toTEXT("-" + QString(COLUMN_CHANNEL_ID) + " " + chId), dType);
TableViewer::procIn(QString("-" + QString(COLUMN_CHANNEL_ID) + " " + chId).toUtf8(), dType);
}
else
{
@ -251,16 +251,16 @@ void ListMembers::procIn(const QByteArray &binIn, quint8 dType)
{
retCode = NO_ERRORS;
auto argsBa = toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName);
auto argsBa = QString("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName).toUtf8();
if (!userFind.isEmpty())
{
argsBa.append(toTEXT(" -" + QString(COLUMN_USERNAME) + " " + userFind));
argsBa.append(QString(" -" + QString(COLUMN_USERNAME) + " " + userFind).toUtf8());
}
if (!dispFind.isEmpty())
{
argsBa.append(toTEXT(" -" + QString(COLUMN_DISPLAY_NAME) + " " + dispFind));
argsBa.append(QString(" -" + QString(COLUMN_DISPLAY_NAME) + " " + dispFind).toUtf8());
}
TableViewer::procIn(argsBa, dType);

View File

@ -24,7 +24,7 @@ QByteArray toFILE_INFO(const QFileInfo &info)
// format: [1byte(flags)][8bytes(createTime)][8bytes(modTime)][8bytes(fileSize)]
// [TEXT(fileName)][TEXT(symLinkTarget)]
// note: the TEXT strings are 16bit NULL terminated meaning 2 bytes of 0x00
// note: the TEXT strings are NULL terminated which means 1 byte of 0x00
// indicate the end of the string.
// note: the integer data found in flags, modTime, createTime and fileSize
@ -41,14 +41,13 @@ QByteArray toFILE_INFO(const QFileInfo &info)
if (info.exists()) flags |= EXISTS;
QByteArray ret;
QByteArray strTerm(2, 0);
ret.append(flags);
ret.append(wrInt(info.birthTime().toMSecsSinceEpoch(), 64));
ret.append(wrInt(info.lastModified().toMSecsSinceEpoch(), 64));
ret.append(wrInt(info.size(), 64));
ret.append(toTEXT(info.fileName()) + strTerm);
ret.append(toTEXT(info.symLinkTarget()) + strTerm);
ret.append(nullTermTEXT(info.fileName()));
ret.append(nullTermTEXT(info.symLinkTarget()));
return ret;
}
@ -184,7 +183,7 @@ void DownloadFile::procIn(const QByteArray &binIn, quint8 dType)
emit mainTxt("dl_file: " + path + "\n");
emit mainTxt("bytes: " + QString::number(progMax) + "\n");
emit procOut(toTEXT("-len " + QString::number(progMax)), GEN_FILE);
emit procOut(QString("-len " + QString::number(progMax)).toUtf8(), GEN_FILE);
startProgPulse();
}
@ -257,7 +256,7 @@ void UploadFile::procIn(const QByteArray &binIn, quint8 dType)
{
if (((dType == GEN_FILE) || (dType == TEXT)) && confirm)
{
auto ans = fromTEXT(binIn);
auto ans = QString::fromUtf8(binIn);
if (noCaseMatch("y", ans))
{
@ -386,7 +385,7 @@ void Delete::procIn(const QByteArray &binIn, uchar dType)
{
if ((flags & MORE_INPUT) && (dType == TEXT))
{
auto ans = fromTEXT(binIn);
auto ans = QString::fromUtf8(binIn);
if (noCaseMatch("y", ans))
{
@ -609,7 +608,7 @@ void Copy::procIn(const QByteArray &binIn, uchar dType)
}
else if ((dType == TEXT) && (flags & MORE_INPUT))
{
auto ans = fromTEXT(binIn);
auto ans = QString::fromUtf8(binIn);
if (noCaseMatch("y", ans))
{
@ -942,7 +941,7 @@ void ChangeDir::procIn(const QByteArray &binIn, quint8 dType)
QDir::setCurrent(path);
mainTxt(QDir::currentPath() + "\n");
async(ASYNC_SET_DIR, toTEXT(path));
async(ASYNC_SET_DIR, path.toUtf8());
}
}
}

View File

@ -84,8 +84,8 @@ void ListCommands::onIPCConnected()
frame.append(QByteArray(2, 0x00));
frame.append(genType);
frame.append(fixedToTEXT(cmdName, BLKSIZE_CMD_NAME));
frame.append(fixedToTEXT(libName(), BLKSIZE_LIB_NAME));
frame.append(toFixedTEXT(cmdName, 64));
frame.append(toFixedTEXT(libName(), 64));
frame.append(nullTermTEXT(shortText(cmdName)));
frame.append(nullTermTEXT(ioText(cmdName)));
frame.append(nullTermTEXT(longText(cmdName)));
@ -139,9 +139,9 @@ void MyInfo::procIn(const QByteArray &binIn, quint8 dType)
QString txt;
QTextStream txtOut(&txt);
QString sesId = rdFromBlock(sessionId, BLKSIZE_SESSION_ID).toHex();
QString ip = rdStringFromBlock(clientIp, BLKSIZE_CLIENT_IP);
QString app = rdStringFromBlock(appName, BLKSIZE_APP_NAME);
auto sesId = rdFromBlock(sessionId, BLKSIZE_SESSION_ID).toHex();
auto ip = rdStringFromBlock(clientIp, BLKSIZE_CLIENT_IP);
auto app = rdStringFromBlock(appName, BLKSIZE_APP_NAME);
txtOut << "Session id: " << sesId << Qt::endl;
txtOut << "IP Address: " << ip << Qt::endl;
@ -149,7 +149,7 @@ void MyInfo::procIn(const QByteArray &binIn, quint8 dType)
if (!isEmptyBlock(userId, BLKSIZE_USER_ID))
{
QByteArray uId = rdFromBlock(userId, BLKSIZE_USER_ID);
auto uId = rdFromBlock(userId, BLKSIZE_USER_ID);
Query db(this);

View File

@ -71,7 +71,7 @@ void AddMod::procIn(const QByteArray &binIn, quint8 dType)
db.addColumn(COLUMN_MOD_MAIN, path);
db.exec();
async(ASYNC_ENABLE_MOD, toTEXT(path));
async(ASYNC_ENABLE_MOD, path.toUtf8());
}
}
}
@ -107,7 +107,7 @@ void DelMod::procIn(const QByteArray &binIn, quint8 dType)
db.addCondition(COLUMN_MOD_MAIN, path);
db.exec();
async(ASYNC_DISABLE_MOD, toTEXT(path));
async(ASYNC_DISABLE_MOD, path.toUtf8());
}
}
}

View File

@ -210,7 +210,7 @@ void TableViewer::procIn(const QByteArray &binIn, quint8 dType)
{
if (flags & MORE_INPUT)
{
auto text = fromTEXT(binIn).toLower();
auto text = QString::fromUtf8(binIn).toLower();
if (text == "y")
{

View File

@ -133,7 +133,7 @@ void CreateUser::procIn(const QByteArray &binIn, quint8 dType)
{
if (flags & MORE_INPUT)
{
auto password = fromTEXT(binIn);
auto password = QString::fromUtf8(binIn);
QString errMsg;
@ -242,7 +242,7 @@ void RemoveUser::procIn(const QByteArray &binIn, quint8 dType)
{
if (flags & MORE_INPUT)
{
auto ans = fromTEXT(binIn);
auto ans = QString::fromUtf8(binIn);
if (noCaseMatch("y", ans))
{
@ -376,7 +376,7 @@ void ChangePassword::procIn(const QByteArray &binIn, quint8 dType)
{
if (flags & MORE_INPUT)
{
auto password = fromTEXT(binIn);
auto password = QString::fromUtf8(binIn);
QString errMsg;
@ -440,7 +440,7 @@ void ChangeUsername::procIn(const QByteArray &binIn, quint8 dType)
retCode = NO_ERRORS;
auto uId = rdFromBlock(userId, BLKSIZE_USER_ID);
auto newNameBa = fixedToTEXT(newName, BLKSIZE_USER_NAME);
auto newNameBa = toFixedTEXT(newName, BLKSIZE_USER_NAME);
Query db(this);
@ -478,7 +478,7 @@ void ChangeDispName::procIn(const QByteArray &binIn, quint8 dType)
Query db(this);
auto uId = rdFromBlock(userId, BLKSIZE_USER_ID);
auto newNameBa = fixedToTEXT(name, BLKSIZE_DISP_NAME);
auto newNameBa = toFixedTEXT(name, BLKSIZE_DISP_NAME);
db.setType(Query::UPDATE, TABLE_USERS);
db.addColumn(COLUMN_DISPLAY_NAME, name);

View File

@ -99,30 +99,14 @@ quint16 toCmdId16(quint32 id)
return ret;
}
QByteArray toTEXT(const QString &txt)
QByteArray toFixedTEXT(const QString &txt, int len)
{
QByteArray ret = QTextCodec::codecForName(TXT_CODEC)->fromUnicode(txt);
return ret.mid(2); // removes BOM.
}
QByteArray fixedToTEXT(const QString &txt, int len)
{
return toTEXT(txt).leftJustified(len, 0, true);
return txt.toUtf8().leftJustified(len, 0, true);
}
QByteArray nullTermTEXT(const QString &txt)
{
return toTEXT(txt) + QByteArray(2, 0x00);
}
QString fromTEXT(const QByteArray &txt)
{
QByteArray ba = txt;
ba.replace(QByteArray(2, 0x00), QByteArray());
return QTextCodec::codecForName(TXT_CODEC)->toUnicode(ba);
return txt.toUtf8() + QByteArray(1, 0x00);
}
bool noCaseMatch(const QString &strA, const QString &strB)
@ -132,7 +116,7 @@ bool noCaseMatch(const QString &strA, const QString &strB)
bool containsNewLine(const QString &str)
{
bool ret = false;
auto ret = false;
for (auto&& chr : str)
{
@ -147,7 +131,7 @@ bool containsNewLine(const QString &str)
bool validSubId(const QString &num)
{
bool ret = false;
auto ret = false;
if (isInt(num))
{
@ -159,9 +143,9 @@ bool validSubId(const QString &num)
bool validUserName(const QString &uName)
{
bool ret = false;
auto ret = false;
if ((uName.size() >= 2) && ((uName.size() * 2) <= BLKSIZE_USER_NAME))
if ((uName.size() >= 2) && (uName.size() <= BLKSIZE_USER_NAME))
{
ret = !uName.contains(' ') && !containsNewLine(uName);
}
@ -169,25 +153,12 @@ bool validUserName(const QString &uName)
return ret;
}
bool validCommonName(const QString &name)
{
bool ret = false;
if ((name.size() >= 1) && (name.size() <= 136))
{
ret = !name.contains(' ') && !containsNewLine(name);
}
return ret;
}
bool validEmailAddr(const QString &email)
{
bool ret = false;
auto ret = false;
auto spEmail = email.split('@');
QStringList spEmail = email.split('@');
if ((spEmail.size() == 2) && (email.size() >= 4) && (email.size() <= 64))
if ((spEmail.size() == 2) && (email.size() >= 4) && (email.size() <= BLKSIZE_EMAIL_ADDR))
{
if (!email.contains(' ') && !containsNewLine(email))
{
@ -212,7 +183,7 @@ bool validCommandName(const QString &name)
bool validDispName(const QString &name)
{
return ((name.size() * 2) <= BLKSIZE_DISP_NAME) && !containsNewLine(name);
return (name.size() <= BLKSIZE_DISP_NAME) && !containsNewLine(name);
}
bool validChName(const QString &name)
@ -248,11 +219,12 @@ bool validLevel(const QString &num, bool includePub)
bool validModPath(const QString &modPath)
{
bool ret = true;
QString forbidden = "|*:\"?<>";
auto ret = !modPath.isEmpty();
if ((modPath.size() > 512) || modPath.isEmpty())
if (ret)
{
static const QString forbidden = "|*:\"?<>";
for (auto&& chr : forbidden)
{
if (modPath.contains(chr))
@ -584,6 +556,27 @@ void containsActiveCh(const char *subChs, char *actBlock)
}
}
void printDatabaseInfo(QTextStream &txt)
{
auto json = getDbSettings();
auto driver = json["driver"].toString();
txt << "Database Parameters --" << Qt::endl << Qt::endl;
txt << "Driver: " << driver << Qt::endl;
if (driver == "QSQLITE")
{
txt << "File: " << sqlDataPath() << Qt::endl;
}
else
{
txt << "Host: " << json["host_name"].toString() << Qt::endl;
txt << "User: " << json["user_name"].toString() << Qt::endl;
}
txt << Qt::endl;
}
QString defaultPw()
{
Query db;
@ -854,16 +847,17 @@ QStringList parseArgs(const QByteArray &data, int maxArgs, int *pos)
{
QStringList ret;
QString arg;
QString line = fromTEXT(data);
bool inDQuotes = false;
bool inSQuotes = false;
bool escaped = false;
auto line = QString::fromUtf8(data);
auto inDQuotes = false;
auto inSQuotes = false;
auto escaped = false;
if (pos != nullptr) *pos = 0;
for (int i = 0; i < line.size(); ++i)
{
if (pos != nullptr) *pos += (TXT_CODEC_BITS / 8);
if (pos != nullptr) *pos += 1;
if ((line[i] == '\'') && !inDQuotes && !escaped)
{
@ -990,9 +984,10 @@ void IdleTimer::attach(QIODevice *dev, int msec)
connect(dev, SIGNAL(bytesWritten(qint64)), this, SLOT(detectWrite(qint64)));
}
ShellIPC::ShellIPC(const QStringList &args, QObject *parent) : QLocalSocket(parent)
ShellIPC::ShellIPC(const QStringList &args, bool supressErr, QObject *parent) : QLocalSocket(parent)
{
arguments = args;
holdErrs = supressErr;
connect(this, SIGNAL(connected()), this, SLOT(hostConnected()));
connect(this, SIGNAL(disconnected()), this, SIGNAL(closeInstance()));
@ -1001,17 +996,23 @@ ShellIPC::ShellIPC(const QStringList &args, QObject *parent) : QLocalSocket(pare
bool ShellIPC::connectToHost()
{
connectToServer(HOST_CONTROL_PIPE);
auto pipeInfo = QFileInfo(QDir::tempPath() + "/" + HOST_CONTROL_PIPE);
if (!waitForConnected())
if (!pipeInfo.exists())
{
if (QFileInfo(QDir::tempPath() + "/" + HOST_CONTROL_PIPE).exists())
if (!holdErrs)
{
QTextStream(stdout) << "" << Qt::endl << "Permission denied." << Qt::endl << Qt::endl;
QTextStream(stdout) << "" << Qt::endl << "A host instance is not running." << Qt::endl << Qt::endl;
}
}
else
{
QTextStream(stdout) << "" << Qt::endl << "Host instance not running." << Qt::endl << Qt::endl;
connectToServer(HOST_CONTROL_PIPE);
if (!waitForConnected() && !holdErrs)
{
QTextStream(stdout) << "" << Qt::endl << "err: Failed to connect to the host instance control pipe." << Qt::endl;
QTextStream(stdout) << "err: Reason - " << errorString() << Qt::endl;
}
}
@ -1020,12 +1021,12 @@ bool ShellIPC::connectToHost()
void ShellIPC::hostConnected()
{
write(toTEXT(arguments.join(' ')));
write(arguments.join(' ').toUtf8());
}
void ShellIPC::dataIn()
{
QTextStream(stdout) << fromTEXT(readAll());
QTextStream(stdout) << QString::fromUtf8(readAll());
emit closeInstance();
}

View File

@ -74,19 +74,10 @@
#define FRAME_HEADER_SIZE 8
#define MAX_FRAME_BITS 24
#define IMPORT_REV 3
#define LOCAL_BUFFSIZE 16777215
#define CLIENT_INIT_TIME 5000
#define IPC_PREP_TIME 1000
#define IPC_CONNECT_DELAY 500
#define CLIENT_HEADER_LEN 410
#define SERVER_HEADER_LEN 35
#define EXE_CRASH_LIMIT 5
#define EXE_DEBUG_INFO_SIZE 512
#define CLIENT_HEADER_LEN 292
#define SERVER_HEADER_TAG "MRCI"
#define HOST_CONTROL_PIPE "MRCI_HOST_CONTROL"
#define TXT_CODEC "UTF-16LE"
#define TXT_CODEC_BITS 16
enum AsyncCommands : quint16
{
@ -231,12 +222,12 @@ enum ChannelMemberLevel : quint8
class Session;
QByteArray toTEXT(const QString &txt);
QByteArray fixedToTEXT(const QString &txt, int len);
QByteArray toFixedTEXT(const QString &txt, int len);
QByteArray nullTermTEXT(const QString &txt);
QByteArray rdFileContents(const QString &path, QTextStream &msg);
quint32 toCmdId32(quint16 cmdId, quint16 branchId);
quint16 toCmdId16(quint32 id);
void printDatabaseInfo(QTextStream &txt);
void serializeThread(QThread *thr);
void mkPath(const QString &path);
void listDir(QList<QPair<QString,QString> > &list, const QString &srcPath, const QString &dstPath);
@ -249,7 +240,6 @@ bool validUserName(const QString &uName);
bool validEmailAddr(const QString &email);
bool validPassword(const QString &pw);
bool validCommandName(const QString &name);
bool validCommonName(const QString &name);
bool validDispName(const QString &name);
bool validChName(const QString &name);
bool validLevel(const QString &num, bool includePub);
@ -277,7 +267,6 @@ bool isChOwner(const QByteArray &uId);
int channelAccessLevel(const QByteArray &uId, quint64 chId);
int channelAccessLevel(const QByteArray &uId, const char *override, quint64 chId);
int maxSubChannels();
QString fromTEXT(const QByteArray &txt);
QString getUserNameForEmail(const QString &email);
QString getEmailForUser(const QByteArray &uId);
QString getDispName(const QByteArray &uId);
@ -330,6 +319,7 @@ class ShellIPC : public QLocalSocket
private:
QStringList arguments;
bool holdErrs;
private slots:
@ -340,7 +330,7 @@ public:
bool connectToHost();
explicit ShellIPC(const QStringList &args, QObject *parent = nullptr);
explicit ShellIPC(const QStringList &args, bool supressErr = false, QObject *parent = nullptr);
signals:

View File

@ -116,7 +116,7 @@ QChar genSpecialChar()
int inRange(int pos, int min, int max)
{
int ret = pos;
auto ret = pos;
if (pos < min) ret = min;
if (pos > max) ret = max;
@ -128,7 +128,7 @@ void moveCharLeft(int pos, QString &str)
{
pos = inRange(pos, 0, str.size() - 1);
QChar chr = str[pos];
auto chr = str[pos];
str.remove(pos, 1);
@ -140,7 +140,7 @@ void moveCharRight(int pos, QString &str)
{
pos = inRange(pos, 0, str.size() - 1);
QChar chr = str[pos];
auto chr = str[pos];
str.remove(pos, 1);
@ -152,7 +152,7 @@ QString genPw()
{
QString ret;
QList<int> seq = genSequence(2, 5, 4);
auto seq = genSequence(2, 5, 4);
for (int i = 0; i < seq[0]; ++i)
{
@ -176,7 +176,7 @@ QString genPw()
seq = genSequence(0, ret.size() - 1, 10);
bool toggle = false;
auto toggle = false;
for (int i : seq)
{
@ -220,7 +220,7 @@ QByteArray rootUserId()
db.addColumn(COLUMN_ROOT_USER);
db.exec();
QByteArray id = db.getData(COLUMN_ROOT_USER).toByteArray();
auto id = db.getData(COLUMN_ROOT_USER).toByteArray();
if (id.isEmpty())
{
@ -235,12 +235,59 @@ QByteArray rootUserId()
return id;
}
QJsonObject getDbSettings(bool defaults)
{
QJsonObject ret;
QFile file(DEFAULT_DB_JSON_FILE);
if (file.exists() && !defaults)
{
if (file.open(QFile::ReadOnly))
{
ret = QJsonDocument::fromJson(file.readAll()).object();
}
else
{
ret = getDbSettings(true);
}
}
else
{
ret.insert("driver", "QSQLITE");
ret.insert("host_name", "localhost");
ret.insert("user_name", QSysInfo::machineHostName());
ret.insert("password", QString(QSysInfo::machineUniqueId().toHex()));
if (file.open(QFile::WriteOnly | QFile::Truncate))
{
file.write(QJsonDocument(ret).toJson());
}
}
file.close();
return ret;
}
void saveDbSettings(const QJsonObject &obj)
{
QFile file(DEFAULT_DB_JSON_FILE);
if (file.open(QFile::WriteOnly | QFile::Truncate))
{
file.write(QJsonDocument(obj).toJson());
}
file.close();
}
bool createUser(const QString &userName, const QString &email, const QString &dispName, const QString &password)
{
bool ret = false;
auto ret = false;
auto newUId = genUniqueHash();
Query db;
QByteArray newUId = genUniqueHash();
db.setType(Query::PUSH, TABLE_USERS);
db.addColumn(COLUMN_USERNAME, userName);
@ -264,7 +311,7 @@ bool createUser(const QString &userName, const QString &email, const QString &di
bool createTempPw(const QByteArray &uId, const QString &password)
{
bool ret = false;
auto ret = false;
Query db;
@ -282,8 +329,8 @@ bool createTempPw(const QByteArray &uId, const QString &password)
bool updatePassword(const QByteArray &uId, const QString &password, const QString &table, bool requireNewPass)
{
bool ret = false;
QByteArray salt = getSalt(uId, table);
auto ret = false;
auto salt = getSalt(uId, table);
if (!salt.isEmpty())
{
@ -291,7 +338,7 @@ bool updatePassword(const QByteArray &uId, const QString &password, const QStrin
QCryptographicHash hasher(QCryptographicHash::Keccak_512);
hasher.addData(QTextCodec::codecForName("UTF-16LE")->fromUnicode(password) + salt);
hasher.addData(password.toUtf8() + salt);
db.setType(Query::UPDATE, table);
db.addColumn(COLUMN_HASH, hasher.result());
@ -306,8 +353,8 @@ bool updatePassword(const QByteArray &uId, const QString &password, const QStrin
bool auth(const QByteArray &uId, const QString &password, const QString &table)
{
bool ret = false;
QByteArray salt = getSalt(uId, table);
auto ret = false;
auto salt = getSalt(uId, table);
if (!salt.isEmpty())
{
@ -354,15 +401,28 @@ Query::Query(QObject *parent) : QObject(parent)
if (!QSqlDatabase::contains(getConnectionName()))
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", getConnectionName());
auto settings = getDbSettings();
auto driver = settings["driver"].toString();
auto db = QSqlDatabase::addDatabase(driver, getConnectionName());
db.setConnectOptions("ISC_DPB_LC_CTYPE=UTF16LE");
db.setConnectOptions("ISC_DPB_LC_CTYPE=UTF8");
if (driver == "QSQLITE")
{
db.setDatabaseName(sqlDataPath());
}
else
{
db.setDatabaseName(APP_NAME);
db.setUserName(settings["user_name"].toString());
db.setHostName(settings["host_name"].toString());
db.setPassword(settings["password"].toString());
}
if (db.open())
{
enableForeignKeys(true);
setTextEncoding("UTF16LE");
setTextEncoding("UTF8");
}
else
{
@ -386,19 +446,6 @@ QString Query::errDetail()
txtOut << " driver error: " << errTxt << Qt::endl;
txtOut << " query: " << qStr << jStr << wStr << limit << Qt::endl;
txtOut << " database: " << sqlDataPath() << Qt::endl;
auto info = QFileInfo(QFileInfo(sqlDataPath()).path());
if (!info.isReadable())
{
txtOut << " readable: database path doesn't have read permissions." << Qt::endl;
}
if (!info.isWritable())
{
txtOut << " writable: database path doesn't have write permissions." << Qt::endl;
}
return ret;
}
@ -752,7 +799,7 @@ void Query::addForeign(const QString &column, const QString &refTable, const QSt
{
if ((columnsAsPassed.contains(column)) && ((type == CREATE_TABLE) || (type == ALTER_TABLE)))
{
QString str = "FOREIGN KEY (" + column + ") REFERENCES " + refTable + " (" + refColum + ")";
auto str = "FOREIGN KEY (" + column + ") REFERENCES " + refTable + " (" + refColum + ")";
switch (onDel)
{
@ -778,7 +825,7 @@ void Query::addForeign(const QString &column, const QString &refTable, const QSt
void Query::preExec()
{
QString columnsStr = QStringList(columnList).join(", ");
auto columnsStr = QStringList(columnList).join(", ");
qStr.replace("%columns%", columnsStr);
data.clear();
@ -806,14 +853,14 @@ void Query::preExec()
bool Query::createRedirect()
{
bool ret = false;
auto ret = false;
if ((type == CREATE_TABLE) && (tables().contains(table)))
{
ret = true;
QStringList existingColumns = columnsInTable(table);
QStringList newColumns = columnsAsPassed;
auto existingColumns = columnsInTable(table);
auto newColumns = columnsAsPassed;
for (int i = 0; (i < newColumns.size()) && queryOk; ++i)
{
@ -895,7 +942,7 @@ QVariant Query::getData(const QString &column, int row)
if ((row < data.size()) && (row >= 0))
{
int index = columnsAsPassed.indexOf(column);
auto index = columnsAsPassed.indexOf(column);
if (index != -1)
{
@ -908,7 +955,7 @@ QVariant Query::getData(const QString &column, int row)
int Query::rows()
{
int ret = 0;
auto ret = 0;
if ((type == PULL) || (type == INNER_JOIN_PULL))
{

View File

@ -33,11 +33,13 @@
#include <QProcess>
#include <QDateTime>
#include <QRandomGenerator>
#include <QJsonObject>
#include <QJsonDocument>
#include "shell.h"
#define APP_NAME "MRCI"
#define APP_VER "3.5.1.0"
#define APP_VER "4.0.2.0"
#define APP_TARGET "mrci"
#ifdef Q_OS_WIN
@ -68,6 +70,7 @@
#define DEFAULT_PUB_KEY_NAME "cert.pem"
#define DEFAULT_PRIV_KEY_NAME "priv.pem"
#define DEFAULT_DB_FILE "data.db"
#define DEFAULT_DB_JSON_FILE "db_settings.json"
#define DEFAULT_ROOT_USER "root"
#define DEFAULT_CONFIRM_SUBJECT "Email Verification"
#define DEFAULT_TEMP_PW_SUBJECT "Password Reset"
@ -171,11 +174,13 @@ quint32 initHostRank();
QByteArray getSalt(const QByteArray &uId, const QString &table);
QByteArray genUniqueHash();
QByteArray rootUserId();
QJsonObject getDbSettings(bool defaults = false);
bool createUser(const QString &userName, const QString &email, const QString &dispName, const QString &password);
bool createTempPw(const QByteArray &uId, const QString &password);
bool updatePassword(const QByteArray &uId, const QString &password, const QString &table, bool requireNewPass = false);
bool auth(const QByteArray &uId, const QString &password, const QString &table);
void cleanupDbConnection();
void saveDbSettings(const QJsonObject &obj);
void moveCharLeft(int pos, QString &str);
void moveCharRight(int pos, QString &str);

View File

@ -18,7 +18,7 @@
bool setupDb(QString *errMsg)
{
bool ret = true;
auto ret = true;
errMsg->clear();
@ -110,7 +110,7 @@ bool setupDb(QString *errMsg)
if (query.createExecuted())
{
QByteArray uId = genUniqueHash();
auto uId = genUniqueHash();
query.setType(Query::PUSH, TABLE_USERS);
query.addColumn(COLUMN_USERNAME, DEFAULT_ROOT_USER);
@ -234,7 +234,7 @@ bool setupDb(QString *errMsg)
randPw = genPw();
}
QByteArray rootUId = rootUserId();
auto rootUId = rootUserId();
if (query.createExecuted())
{

View File

@ -73,6 +73,7 @@ void showHelp()
txtOut << " -exempt_cmds : run the internal module to list it's rank exempt commands. for internal use only." << Qt::endl;
txtOut << " -user_cmds : run the internal module to list it's user commands. for internal use only." << Qt::endl;
txtOut << " -run_cmd : run an internal module command. for internal use only." << Qt::endl;
txtOut << " -ls_sql_drvs : list all available SQL drivers that the host currently supports." << Qt::endl;
txtOut << " -load_ssl : re-load the host SSL certificate without stopping the host instance." << Qt::endl << Qt::endl;
txtOut << "Internal module | -public_cmds, -user_cmds, -exempt_cmds, -run_cmd |:" << Qt::endl << Qt::endl;
txtOut << " -pipe : the named pipe used to establish a data connection with the session." << Qt::endl;
@ -81,9 +82,9 @@ void showHelp()
txtOut << "Details:" << Qt::endl << Qt::endl;
txtOut << "addr - this argument takes a {ip_address:port} string. it will return an error if not formatted correctly" << Qt::endl;
txtOut << " examples: 10.102.9.2:35516 or 0.0.0.0:35516." << Qt::endl << Qt::endl;
txtOut << "run_cmd - this argument is used by the host itself, along side the internal module arguments below to run" << Qt::endl;
txtOut << " the internal command names passed by it. this is not ment to be run directly by human input." << Qt::endl;
txtOut << " the executable will auto close if it fails to connect to the pipe and/or shared memory segments" << Qt::endl << Qt::endl;
txtOut << "run_cmd - this argument is used by the host itself along with the internal module arguments to run the" << Qt::endl;
txtOut << " internal command names passed by it. this is not ment to be run directly by human input. the" << Qt::endl;
txtOut << " executable will auto close if it fails to connect to the pipe and/or shared memory segments" << Qt::endl << Qt::endl;
}
void soeDueToDbErr(int *retCode, const QString *errMsg)
@ -94,10 +95,10 @@ void soeDueToDbErr(int *retCode, const QString *errMsg)
QTextStream(stderr) << " what happened: " << Qt::endl << *errMsg << Qt::endl << Qt::endl;
}
int shellToHost(const QStringList &args, QCoreApplication &app)
int shellToHost(const QStringList &args, bool holdErrs, QCoreApplication &app)
{
auto ret = 0;
auto *ipc = new ShellIPC(args, &app);
auto *ipc = new ShellIPC(args, holdErrs, &app);
QObject::connect(ipc, SIGNAL(closeInstance()), &app, SLOT(quit()));
@ -135,12 +136,49 @@ int main(int argc, char *argv[])
qInstallMessageHandler(msgHandler);
//args.append("-host"); // debug
// args.append("-ls_sql_drvs"); // debug
if (args.contains("-help", Qt::CaseInsensitive) || args.size() == 1)
if (args.contains("-run_cmd", Qt::CaseInsensitive) ||
args.contains("-public_cmds", Qt::CaseInsensitive) ||
args.contains("-exempt_cmds", Qt::CaseInsensitive) ||
args.contains("-user_cmds", Qt::CaseInsensitive))
{
// security note: it is critical that the above internal arguments are checked
// first. external clients have the ability to pass additional
// args and those args come through here. it can be a security
// threat if an external arg is a powerful arg like "reset_root"
// and it ends up getting processed unintentionally by this
// function.
if (setupDb(&err))
{
auto *mod = new Module(&app);
if (mod->start(args))
{
ret = QCoreApplication::exec();
}
}
else
{
soeDueToDbErr(&ret, &err);
}
}
else if (args.contains("-help", Qt::CaseInsensitive) || args.size() == 1)
{
showHelp();
}
else if (args.contains("-ls_sql_drvs", Qt::CaseInsensitive))
{
QTextStream(stdout) << "" << Qt::endl;
for (auto driver : QSqlDatabase::drivers())
{
QTextStream(stdout) << driver << Qt::endl;
}
QTextStream(stdout) << "" << Qt::endl;
}
else if (args.contains("-about", Qt::CaseInsensitive))
{
QTextStream(stdout) << "" << Qt::endl << APP_NAME << " v" << QCoreApplication::applicationVersion() << Qt::endl << Qt::endl;
@ -152,11 +190,24 @@ int main(int argc, char *argv[])
args.contains("-status", Qt::CaseInsensitive) ||
args.contains("-load_ssl", Qt::CaseInsensitive))
{
ret = shellToHost(args, app);
ret = shellToHost(args, false, app);
}
else if (setupDb(&err))
{
if (args.contains("-addr", Qt::CaseInsensitive))
if (args.contains("-host", Qt::CaseInsensitive))
{
auto *serv = new TCPServer(&app);
if (serv->start())
{
ret = QCoreApplication::exec();
}
}
else if (args.contains("-host_trig", Qt::CaseInsensitive))
{
QProcess::startDetached(QCoreApplication::applicationFilePath(), QStringList() << "-host");
}
else if (args.contains("-addr", Qt::CaseInsensitive))
{
auto params = getParam("-addr", args);
auto addr = params.split(':');
@ -186,41 +237,16 @@ int main(int argc, char *argv[])
}
else
{
ret = 0;
Query db(&app);
db.setType(Query::UPDATE, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_IPADDR, addr[0]);
db.addColumn(COLUMN_PORT, port);
db.exec();
}
}
}
else if (args.contains("-run_cmd", Qt::CaseInsensitive) ||
args.contains("-public_cmds", Qt::CaseInsensitive) ||
args.contains("-exempt_cmds", Qt::CaseInsensitive) ||
args.contains("-user_cmds", Qt::CaseInsensitive))
{
auto *mod = new Module(&app);
if (mod->start(args))
{
ret = QCoreApplication::exec();
ret = shellToHost(args, true, app);
}
}
else if (args.contains("-host", Qt::CaseInsensitive))
{
auto *serv = new TCPServer(&app);
if (serv->start())
{
ret = QCoreApplication::exec();
}
}
else if (args.contains("-host_trig"))
{
QProcess::startDetached(QCoreApplication::applicationFilePath(), QStringList() << "-host");
}
else if (args.contains("-reset_root", Qt::CaseInsensitive))
{

View File

@ -18,7 +18,8 @@
QString createHostSharedMem(QSharedMemory *mem)
{
int len = 0;
auto len = 0;
QString ret;
len += BLKSIZE_HOST_LOAD; // hostLoad
@ -39,8 +40,8 @@ QString createHostSharedMem(QSharedMemory *mem)
int posOfLikeBlock(const QByteArray &block, const char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock)
{
int ret = -1;
quint32 cmpLen = static_cast<quint32>(block.size());
auto ret = -1;
auto cmpLen = static_cast<quint32>(block.size());
if (cmpLen > bytesPerBlock)
{
@ -110,7 +111,7 @@ int posOfEmptyBlock(const char *blocks, quint32 numOfBlocks, quint32 bytesPerBlo
bool addBlockToBlockset(const char *block, char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock)
{
bool ret = false;
auto ret = false;
if (posOfBlock(block, blocks, numOfBlocks, bytesPerBlock) == -1)
{
@ -180,7 +181,8 @@ bool isEmptyBlock(const char *block, quint32 blockSize)
void wrStringToBlock(const QString &str, char *block, quint32 blockSize)
{
quint32 strByteSize = static_cast<quint32>(str.size()) * 2;
auto strBytes = str.toUtf8();
auto strByteSize = static_cast<quint32>(strBytes.size());
if (strByteSize > blockSize)
{
@ -191,7 +193,7 @@ void wrStringToBlock(const QString &str, char *block, quint32 blockSize)
memset(block, 0, blockSize);
}
memcpy(block, reinterpret_cast<const char*>(str.utf16()), strByteSize);
memcpy(block, strBytes.data(), strByteSize);
}
void wr8BitToBlock(quint8 num, char *block)
@ -290,24 +292,21 @@ quint64 rd64BitFromBlock(const char *block)
QString rdStringFromBlock(const char *block, quint32 blockSize)
{
QString ret;
quint16 chr;
quint32 len = 0;
for (quint32 i = 0; i < blockSize; i += 2)
for (quint32 i = 0; i < blockSize; i++)
{
memcpy(&chr, block + i, 2);
if (chr == 0)
if (block[i] == 0)
{
break;
}
else
{
ret.append(QChar(chr));
len++;
}
}
return ret;
return QString(QByteArray(block, len));
}
QByteArray rdFromBlock(const char *block, quint32 blockSize)
@ -339,8 +338,8 @@ MemShare::MemShare(QObject *parent) : QObject(parent)
bool MemShare::createSharedMem(const QByteArray &sesId, const QString &hostKey)
{
int len = 0;
bool ret = false;
auto len = 0;
auto ret = false;
sharedMem->setKey(sesId.toHex());
hostSharedMem->setNativeKey(hostKey);
@ -433,11 +432,11 @@ void MemShare::setupDataBlocks()
QByteArray MemShare::createPeerInfoFrame()
{
QByteArray sesId = rdFromBlock(sessionId, BLKSIZE_SESSION_ID);
QByteArray usrId = rdFromBlock(userId, BLKSIZE_USER_ID);
QByteArray uName = rdFromBlock(userName, BLKSIZE_USER_NAME);
QByteArray aName = rdFromBlock(appName, BLKSIZE_APP_NAME);
QByteArray dName = rdFromBlock(displayName, BLKSIZE_DISP_NAME);
auto sesId = rdFromBlock(sessionId, BLKSIZE_SESSION_ID);
auto usrId = rdFromBlock(userId, BLKSIZE_USER_ID);
auto uName = rdFromBlock(userName, BLKSIZE_USER_NAME);
auto aName = rdFromBlock(appName, BLKSIZE_APP_NAME);
auto dName = rdFromBlock(displayName, BLKSIZE_DISP_NAME);
return sesId + usrId + uName + aName + dName;
}

View File

@ -29,18 +29,16 @@
#define BLKSIZE_CLIENT_IP 78
#define BLKSIZE_SESSION_ID 28
#define BLKSIZE_SUB_CHANNEL 9
#define BLKSIZE_APP_NAME 134
#define BLKSIZE_APP_NAME 32
#define BLKSIZE_USER_ID 32
#define BLKSIZE_USER_NAME 48
#define BLKSIZE_DISP_NAME 64
#define BLKSIZE_USER_NAME 24
#define BLKSIZE_DISP_NAME 24
#define BLKSIZE_CHANNEL_ID 8
#define BLKSIZE_HOST_RANK 4
#define BLKSIZE_ACT_UPDATE 1
#define BLKSIZE_CH_OVERRIDE 1
#define BLKSIZE_HOST_LOAD 4
#define BLKSIZE_CMD_NAME 128
#define BLKSIZE_LIB_NAME 128
#define BLKSIZE_EMAIL_ADDR 128
#define BLKSIZE_EMAIL_ADDR 64
#define HOST_NON_NATIVE_KEY "MRCI_Host_Shared_Mem_Key"

View File

@ -103,7 +103,7 @@ void Session::sesRdy()
emit connectPeers(QSharedPointer<SessionCarrier>(payload));
loadCmds();
asyncToClient(ASYNC_RDY, toTEXT("\nReady!\n\n"), TEXT);
asyncToClient(ASYNC_RDY, QString("\nReady!\n\n").toUtf8(), TEXT);
}
void Session::addIpAction(const QString &action)
@ -221,6 +221,7 @@ ModProcess *Session::initModProc(const QString &modApp)
auto *proc = new ModProcess(modApp, sesMemKey, hostMemKey, pipe, this);
proc->setWorkingDirectory(currentDir);
proc->addArgs(modInst);
proc->setSessionParams(&cmdUniqueNames, &cmdRealNames, &cmdAppById, &modCmdNames, &cmdIds, rnk);
connect(proc, &ModProcess::dataToClient, this, &Session::dataToClient);
@ -292,7 +293,7 @@ void Session::dataToCmd(quint32 cmdId, const QByteArray &data, quint8 typeId)
}
else
{
dataToClient(cmdId, toTEXT("err: No such command id: " + QString::number(cmdId16) + "."), ERR);
dataToClient(cmdId, QString("err: No such command id: " + QString::number(cmdId16) + ".").toUtf8(), ERR);
}
}
@ -336,16 +337,19 @@ void Session::dataFromClient()
{
auto clientHeader = tcpSocket->read(CLIENT_HEADER_LEN);
// client header format: [4bytes(tag)][134bytes(appName)][272bytes(padding)]
// client header format: [4bytes(tag)][32bytes(appName)][128bytes(mod_instructions)][128byes(padding)]
// tag = 0x4D, 0x52, 0x43, 0x49 (MRCI)
// appName = UTF16LE string (padded with 0x00)
// padding = just a string of 0x00 (reserved for future expansion)
// appName = UTF8 string (padded with 0x00)
// modInst = UTF8 string (padded with 0x00)
// padding = 128 bytes of (0x00)
if (clientHeader.startsWith(SERVER_HEADER_TAG))
{
wrToBlock(clientHeader.mid(4, BLKSIZE_APP_NAME), appName, BLKSIZE_APP_NAME);
modInst = rdStringFromBlock(clientHeader.data() + 36, 64);
auto ver = QCoreApplication::applicationVersion().split('.');
QByteArray servHeader;
@ -474,7 +478,7 @@ void Session::login(const QByteArray &uId)
for (int i = 0; i < db.rows(); ++i)
{
QByteArray chId = wrInt(db.getData(COLUMN_CHANNEL_ID, i).toULongLong(), 64);
auto chId = wrInt(db.getData(COLUMN_CHANNEL_ID, i).toULongLong(), 64);
addBlockToBlockset(chId.data(), chList, MAX_CHANNELS_PER_USER, BLKSIZE_CHANNEL_ID);
}
@ -498,7 +502,7 @@ void Session::sendLocalInfo()
db.addCondition(COLUMN_USER_ID, rdFromBlock(userId, BLKSIZE_USER_ID));
db.exec();
frame.append(fixedToTEXT(db.getData(COLUMN_EMAIL).toString(), BLKSIZE_EMAIL_ADDR));
frame.append(toFixedTEXT(db.getData(COLUMN_EMAIL).toString(), BLKSIZE_EMAIL_ADDR));
frame.append(rdFromBlock(hostRank, BLKSIZE_HOST_RANK));
if (db.getData(COLUMN_EMAIL_VERIFIED).toBool())
@ -616,11 +620,11 @@ void Session::privAsyncDataIn(quint16 cmdId, const QByteArray &data)
}
else if (cmdId == ASYNC_SET_DIR)
{
currentDir = fromTEXT(data);
currentDir = QString::fromUtf8(data);
}
else if (cmdId == ASYNC_DEBUG_TEXT)
{
qDebug() << fromTEXT(data);
qDebug() << QString::fromUtf8(data);
}
sharedMem->unlock();

View File

@ -33,6 +33,7 @@ private:
QSslSocket *tcpSocket;
QList<QSslCertificate> *sslChain;
QSslKey *sslKey;
QString modInst;
QString currentDir;
QHash<QString, QStringList> modCmdNames;
QHash<quint32, QList<QByteArray> > frameQueue;

View File

@ -217,11 +217,31 @@ void TCPServer::procPipeIn()
{
closeServer();
controlSocket->write(toTEXT("\n"));
controlSocket->write(QString("\n").toUtf8());
}
else if (args.contains("-load_ssl", Qt::CaseInsensitive))
{
controlSocket->write(toTEXT(loadSSLData(true)));
controlSocket->write(loadSSLData(true).toUtf8());
}
else if (args.contains("-addr", Qt::CaseInsensitive))
{
auto params = getParam("-addr", args);
auto addr = params.split(':');
close();
QString text;
QTextStream txtOut(&text);
if (!listen(QHostAddress(addr[0]), addr[1].toUInt()))
{
txtOut << "" << Qt::endl << "err: TCP listen failure on address: " << addr[0] << " port: " << addr[1] << Qt::endl;
txtOut << "err: Reason - " << errorString() << Qt::endl;
}
txtOut << "" << Qt::endl;
controlSocket->write(text.toUtf8());
}
else if (args.contains("-status", Qt::CaseInsensitive))
{
@ -239,17 +259,16 @@ void TCPServer::procPipeIn()
txtOut << "" << Qt::endl;
txtOut << "Host Load: " << rd32BitFromBlock(hostLoad) << "/" << maxSessions << Qt::endl;
txtOut << "Active Address: " << serverAddress().toString() << Qt::endl;
txtOut << "Active Port: " << serverPort() << Qt::endl;
txtOut << "Set Address: " << db.getData(COLUMN_IPADDR).toString() << Qt::endl;
txtOut << "Set Port: " << db.getData(COLUMN_PORT).toUInt() << Qt::endl;
txtOut << "Address: " << serverAddress().toString() << Qt::endl;
txtOut << "Port: " << serverPort() << Qt::endl;
txtOut << "Working Path: " << QDir::currentPath() << Qt::endl;
txtOut << "Database: " << sqlDataPath() << Qt::endl;
txtOut << "SSL Chain: " << sslCertChain() << Qt::endl;
txtOut << "SSL Private: " << sslPrivKey() << Qt::endl << Qt::endl;
printDatabaseInfo(txtOut);
hostSharedMem->unlock();
controlSocket->write(toTEXT(text));
controlSocket->write(text.toUtf8());
}
}