Moved logging out of the host database and a few bug fixes
The host will no longer store logs in the database. Instead, logging is done by syslog if running linux. If running Windows, logs are now stored in a local file in the app config directory. Fixed a bug with -add_admin that would fail to create more than one admin accounts in sequence due to the blank email addresses being blank. It will now create fake email addresses unique to each admin account. Added -res_pw to reset user acccount passwords from the CLI if needed. Logging was also expanded to capture and log all failures reported by all modules stderr output. Updated build.py and install.py for QT6 support and moved the linux .service file from /etc to /lib to conform to systemd standards. Removed the ls_dbg command because in database logging is no longer done.
This commit is contained in:
parent
c8f53d1e5c
commit
508af40359
27
build.py
27
build.py
|
@ -112,11 +112,24 @@ def verbose_copy(src, dst):
|
|||
if os.path.exists(dst) and os.path.isdir(dst):
|
||||
shutil.rmtree(dst)
|
||||
|
||||
shutil.copytree(src, dst)
|
||||
try:
|
||||
# ignore errors thrown by shutil.copytree()
|
||||
# it's likely not actually failing to copy
|
||||
# the directory but still throws errors if
|
||||
# it fails to apply the same file stats as
|
||||
# the source. this type of errors can be
|
||||
# ignored.
|
||||
shutil.copytree(src, dst)
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
else:
|
||||
elif os.path.exists(src):
|
||||
shutil.copyfile(src, dst)
|
||||
|
||||
else:
|
||||
print("wrn: " + src + " does not exists. skipping.")
|
||||
|
||||
def linux_build_app_dir(app_ver, app_name, app_target, qt_bin):
|
||||
if not os.path.exists("app_dir/linux/sqldrivers"):
|
||||
os.makedirs("app_dir/linux/sqldrivers")
|
||||
|
@ -127,6 +140,7 @@ def linux_build_app_dir(app_ver, app_name, app_target, qt_bin):
|
|||
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(qt_bin + "/../plugins/sqldrivers/libqsqlmysql.so", "app_dir/linux/sqldrivers/libqsqlmysql.so")
|
||||
verbose_copy("build/linux/" + app_target, "app_dir/linux/" + app_target)
|
||||
|
||||
shutil.copyfile("build/linux/" + app_target, "/tmp/" + app_target)
|
||||
|
@ -226,9 +240,18 @@ def main():
|
|||
else:
|
||||
cd()
|
||||
|
||||
if platform.system() == "Linux":
|
||||
if os.path.exists("build/linux"):
|
||||
shutil.rmtree("build/linux")
|
||||
|
||||
elif platform.system() == "Windows":
|
||||
if os.path.exists("build/windows"):
|
||||
shutil.rmtree("build/windows")
|
||||
|
||||
result = subprocess.run([qt_bin + os.sep + "qmake", "-config", "release"])
|
||||
|
||||
if result.returncode == 0:
|
||||
|
||||
result = subprocess.run([maker])
|
||||
|
||||
if result.returncode == 0:
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
<file>docs/intern_commands/ls_auth_log.md</file>
|
||||
<file>docs/intern_commands/ls_ch_members.md</file>
|
||||
<file>docs/intern_commands/ls_chs.md</file>
|
||||
<file>docs/intern_commands/ls_dbg.md</file>
|
||||
<file>docs/intern_commands/ls_mods.md</file>
|
||||
<file>docs/intern_commands/ls_open_chs.md</file>
|
||||
<file>docs/intern_commands/ls_p2p.md</file>
|
||||
|
|
|
@ -65,7 +65,7 @@ Typical use for a MRCI host is to run commands on a remote host that clients ask
|
|||
|
||||
Because the host is modular, the things you can customize it to do is almost limitless by simply adding more commands.
|
||||
|
||||
** The email system of this application depends on external email clients that run on the command line. The default is [mutt](http://www.mutt.org/). If you want emails to work out of the box, consider installing and configuring mutt. It just needs to be configured with a smtp account to send emails with. You don't have to use mutt though, the host does have options to change the email client to any other application that has a command line interface.
|
||||
** The email system of this application depends on external email clients that run on the command line. The default is [mutt](http://www.mutt.org/). If you want emails to work out of the box, consider installing and configuring mutt. It just needs to be configured with a smtp account to send emails with. You don't have to use mutt though, the host does have the option to change the email client to any other application that has a command line interface.
|
||||
|
||||
### Documentation ###
|
||||
|
||||
|
|
|
@ -281,7 +281,7 @@ This internal only async command doesn't carry any data. The session object norm
|
|||
This internal only async command carries a [TEXT](type_ids.md) path that sets the working directory for the local session. All module processes started by the session will use this directory as the working directory and it is not shared among peer sessions. nothing happens if the path is invalid or does not exists.
|
||||
|
||||
```ASYNC_DEBUG_TEXT (44)```
|
||||
This internal only async command carries a [TEXT](type_ids.md) debug message to be logged into the host debug log from the module. Modules can use this to help with debugging issues if it doesn't have direct access to the host database.
|
||||
This internal only async command carries a [TEXT](type_ids.md) debug message to be logged into the host logging system. On Linux systems, syslog is used. On windows systems, a local log file in %PROGRAMDATA%\mrci\messages.log is used. This is useful if you want create custom log messages because it doesn't add any extras to the messages like the module path and message id. It is recommanded to have those extras added for easier debugging so module makers are encouraged simply use stderr output because the host will capture those messages for logging and will add the extras to them.
|
||||
|
||||
```ASYNC_HOOK_INPUT (45)```
|
||||
This async command doesn't carry any data. This just indicate to the local session that the module command is requesting to hook the tcp data input from the client. when the tcp input is hooked, all data sent from the client is redirected to the command object/process that initiated the hook until reqested to unhook. If the command that initiated the hook terminates in anyway with an active hook, the hook will automatically be removed.
|
||||
|
|
|
@ -106,7 +106,7 @@ db_driver : string
|
|||
|
||||
db_host_name : string
|
||||
|
||||
This value contains the network, internet or file location address
|
||||
This value contains the network, internet address or file location
|
||||
of the database.
|
||||
|
||||
db_password : string
|
||||
|
|
|
@ -66,8 +66,6 @@ The host is extendable via 3rd party modules but the host itself is an internal
|
|||
|
||||
* [ls_chs](intern_commands/ls_chs.md) - list all channels you are currently a member of and all pending invites.
|
||||
|
||||
* [ls_dbg](intern_commands/ls_dbg.md) - display all debug log messages.
|
||||
|
||||
* [ls_mods](intern_commands/ls_mods.md) - list all available modules currently configured in the host.
|
||||
|
||||
* [ls_open_chs](intern_commands/ls_open_chs.md) - list all of the sub-channels that are currently open.
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
### Summary ###
|
||||
|
||||
display all debug log messages.
|
||||
|
||||
### IO ###
|
||||
|
||||
```[{search_terms} {-delete}]/[text]```
|
||||
|
||||
### Description ###
|
||||
|
||||
by default, all entries in the table are displayed in 50 entries per page. you can pass the column names as -column_name (text) to refine your search to specific entries. this command can handle the following columns:
|
||||
|
||||
-time_stamp
|
||||
-log_entry
|
||||
|
||||
you can also pass -delete that will cause the command to delete the entries instead of displaying them. note: passing no search terms with this option will delete all entries in the table.
|
|
@ -53,4 +53,8 @@ notes:
|
|||
|
||||
* Modules called with -run_cmd does not need to terminate after running the requested command, instead it must send an [IDLE](type_ids.md) frame to indicate that the command is finished when it eventually does finish. This is desired because not only it tells the client that the command is finished but it also makes it so the session doesn't need to recreate the module process on every subsequent call to the command.
|
||||
|
||||
* The session will send a [KILL_CMD](type_ids.md) to the module after 2 mins of being idle (no IPC/Pipe activity). The module will have 3 seconds to do this before it is force killed. This will also happen when the user ends the session and the module process needs to terminate. Modules must still send an [IDLE](type_ids.md) frame to indicate the command is finished if a command was running.
|
||||
* The session will send a [KILL_CMD](type_ids.md) to the module after 2 mins of being idle (no IPC/Pipe activity). The module will have 3 seconds to do this before it is force killed. This will also happen when the user ends the session and the module process needs to terminate. Modules must still send an [IDLE](type_ids.md) frame to indicate the command is finished if a command was running.
|
||||
|
||||
### 2.4 Module Standard Output/Error ###
|
||||
|
||||
The host captures all text written to standard out/err. Although modules can send text data to clients via the [TEXT](type_ids.md) frame, another way to do the same thing is to write to stdout. The host however treats stderr differently, it sends all text written to stderr to the host logging system. On Linux systems, syslog is used. On windows systems, a local log file in %PROGRAMDATA%\mrci\messages.log is used. When logging messages, the host will send a generic error message to the client but the full error details will be sent to the logs along with the a generated message id and the module executable.
|
|
@ -160,7 +160,7 @@ def local_install(app_target, app_name):
|
|||
|
||||
template_to_deploy("app_dir/linux/" + app_target + ".sh", install_dir + "/" + app_target + ".sh", install_dir, app_name, app_target)
|
||||
template_to_deploy("app_dir/linux/uninstall.sh", install_dir + "/uninstall.sh", install_dir, app_name, app_target)
|
||||
template_to_deploy("app_dir/linux/" + app_target + ".service", "/etc/systemd/system/" + app_target + ".service", install_dir, app_name, app_target)
|
||||
template_to_deploy("app_dir/linux/" + app_target + ".service", "/lib/systemd/system/" + app_target + ".service", install_dir, app_name, app_target)
|
||||
|
||||
verbose_copy("app_dir/linux/" + app_target, install_dir + "/" + app_target)
|
||||
verbose_copy("app_dir/linux/lib", install_dir + "/lib")
|
||||
|
@ -170,7 +170,7 @@ def local_install(app_target, app_name):
|
|||
|
||||
subprocess.run(["useradd", "-r", app_target])
|
||||
subprocess.run(["chmod", "-R", "755", install_dir])
|
||||
subprocess.run(["chmod", "755", "/etc/systemd/system/" + app_target + ".service"])
|
||||
subprocess.run(["chmod", "644", "/lib/systemd/system/" + app_target + ".service"])
|
||||
subprocess.run(["chown", "-R", app_target + ":" + app_target, "/var/opt/" + app_target])
|
||||
subprocess.run(["chown", "-R", app_target + ":" + app_target, "/etc/" + app_target])
|
||||
subprocess.run(["systemctl", "start", app_target])
|
||||
|
|
|
@ -79,6 +79,7 @@ CmdObject::CmdObject(QObject *parent) : MemShare(parent)
|
|||
ipcWorker = new IPCWorker(pipe, nullptr);
|
||||
keepAliveTimer = new QTimer(this);
|
||||
progTimer = new QTimer(this);
|
||||
dProc = new QProcess(this);
|
||||
progCurrent = 0;
|
||||
progMax = 0;
|
||||
|
||||
|
@ -120,6 +121,11 @@ void CmdObject::term()
|
|||
flags = 0;
|
||||
retCode = ABORTED;
|
||||
|
||||
if (dProc->state() == QProcess::Running)
|
||||
{
|
||||
dProc->kill();
|
||||
}
|
||||
|
||||
onTerminate();
|
||||
postProc();
|
||||
}
|
||||
|
@ -132,6 +138,30 @@ void CmdObject::kill()
|
|||
QCoreApplication::instance()->exit();
|
||||
}
|
||||
|
||||
bool CmdObject::runDetachedProc(const QStringList &args)
|
||||
{
|
||||
auto ret = false;
|
||||
|
||||
if (args.isEmpty())
|
||||
{
|
||||
qCritical() << "The external command call has no arguments.";
|
||||
}
|
||||
else
|
||||
{
|
||||
dProc->setProgram(args[0]);
|
||||
dProc->setArguments(args.mid(1));
|
||||
|
||||
ret = dProc->startDetached();
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
qCritical() << "External command execution failure: " + dProc->errorString();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CmdObject::preProc(const QByteArray &data, quint8 typeId)
|
||||
{
|
||||
if (typeId == TERM_CMD)
|
||||
|
|
|
@ -67,6 +67,7 @@ protected:
|
|||
QTimer *keepAliveTimer;
|
||||
QTimer *progTimer;
|
||||
IPCWorker *ipcWorker;
|
||||
QProcess *dProc;
|
||||
quint32 flags;
|
||||
quint16 retCode;
|
||||
qint64 progCurrent;
|
||||
|
@ -81,6 +82,7 @@ protected:
|
|||
void startProgPulse();
|
||||
void stopProgPulse();
|
||||
void postProc();
|
||||
bool runDetachedProc(const QStringList &args);
|
||||
QString libName();
|
||||
|
||||
virtual void procIn(const QByteArray &, quint8) {}
|
||||
|
|
|
@ -47,9 +47,18 @@ ModProcess::ModProcess(const QString &app, const QString &memSes, const QString
|
|||
setProgram(app);
|
||||
}
|
||||
|
||||
void ModProcess::logErrMsgs(quint32 id)
|
||||
{
|
||||
auto msgId = genMsgNumber();
|
||||
|
||||
emit dataToClient(id, "The command module generated an error, msg_id: " + msgId.toUtf8() + "\n", ERR);
|
||||
|
||||
qCritical() << "Module: " + program() + " " + readAllStandardError() + " msg_id: " + msgId.toUtf8();
|
||||
}
|
||||
|
||||
void ModProcess::rdFromStdErr()
|
||||
{
|
||||
emit dataToClient(toCmdId32(ASYNC_SYS_MSG, 0), readAllStandardError(), ERR);
|
||||
logErrMsgs(toCmdId32(ASYNC_SYS_MSG, 0));
|
||||
}
|
||||
|
||||
void ModProcess::rdFromStdOut()
|
||||
|
@ -71,8 +80,9 @@ quint16 ModProcess::genCmdId()
|
|||
|
||||
QString ModProcess::makeCmdUnique(const QString &name)
|
||||
{
|
||||
QString strNum;
|
||||
QStringList names = cmdUniqueNames->values();
|
||||
QString strNum;
|
||||
|
||||
auto names = cmdUniqueNames->values();
|
||||
|
||||
for (int i = 1; names.contains(name + strNum); ++i)
|
||||
{
|
||||
|
@ -180,7 +190,7 @@ void ModProcess::onDataFromProc(quint8 typeId, const QByteArray &data)
|
|||
}
|
||||
else if (typeId == ERR)
|
||||
{
|
||||
qDebug() << QString::fromUtf8(data);
|
||||
qCritical() << "Module: " << program() << " - " << QString::fromUtf8(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,7 +277,6 @@ void ModProcess::setSessionParams(QHash<quint16, QString> *uniqueNames,
|
|||
|
||||
void ModProcess::onFailToStart()
|
||||
{
|
||||
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();
|
||||
|
@ -277,7 +286,8 @@ void ModProcess::err(QProcess::ProcessError error)
|
|||
{
|
||||
if (error == QProcess::FailedToStart)
|
||||
{
|
||||
qDebug() << "err: Module process: " << program() << " failed to start. reason: " << errorString();
|
||||
qCritical() << "Module process: " << program() << " failed to start. reason: " << errorString();
|
||||
qCritical() << "Args in use: " << arguments().join(' ');
|
||||
|
||||
onFailToStart();
|
||||
}
|
||||
|
@ -439,7 +449,6 @@ void CmdProcess::onReady()
|
|||
|
||||
void CmdProcess::onFailToStart()
|
||||
{
|
||||
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);
|
||||
|
||||
|
@ -453,7 +462,9 @@ void CmdProcess::onFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
|||
|
||||
if (!cmdIdle)
|
||||
{
|
||||
emit dataToClient(cmdId, "err: The command has stopped unexpectedly or it has failed to send an IDLE frame before exiting.\n", ERR);
|
||||
qCritical() << "Module: " + program() + "Command: '" + cmdName + "' has crashed or failed to return an IDLE frame when it terminated.";
|
||||
|
||||
emit dataToClient(cmdId, "err: The command '" + cmdName.toUtf8() + "' has stopped unexpectedly.\n", ERR);
|
||||
emit dataToClient(cmdId, wrInt(CRASH, 16), IDLE);
|
||||
}
|
||||
|
||||
|
@ -463,16 +474,16 @@ void CmdProcess::onFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
|||
deleteLater();
|
||||
}
|
||||
|
||||
void CmdProcess::rdFromStdErr()
|
||||
{
|
||||
emit dataToClient(cmdId, readAllStandardError(), ERR);
|
||||
}
|
||||
|
||||
void CmdProcess::rdFromStdOut()
|
||||
{
|
||||
emit dataToClient(cmdId, readAllStandardOutput(), TEXT);
|
||||
}
|
||||
|
||||
void CmdProcess::rdFromStdErr()
|
||||
{
|
||||
logErrMsgs(cmdId);
|
||||
}
|
||||
|
||||
void CmdProcess::dataFromSession(quint32 id, const QByteArray &data, quint8 dType)
|
||||
{
|
||||
if (id == cmdId)
|
||||
|
@ -673,7 +684,7 @@ void CmdProcess::onDataFromProc(quint8 typeId, const QByteArray &data)
|
|||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "async id: " << async << " from command id: " << toCmdId16(cmdId) << " blocked. reason: " << errMsg;
|
||||
qCritical() << "async id: " << async << " from command id/name: " << toCmdId16(cmdId) << "/" << cmdName << " blocked. reason: " << errMsg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ protected:
|
|||
virtual void onDataFromProc(quint8 typeId, const QByteArray &data);
|
||||
|
||||
void cleanupPipe();
|
||||
void logErrMsgs(quint32 id);
|
||||
void wrIpcFrame(quint8 typeId, const QByteArray &data);
|
||||
bool startProc(const QStringList &args);
|
||||
bool isCmdLoaded(const QString &name);
|
||||
|
@ -125,8 +126,8 @@ private:
|
|||
|
||||
private slots:
|
||||
|
||||
void rdFromStdErr();
|
||||
void rdFromStdOut();
|
||||
void rdFromStdErr();
|
||||
|
||||
public slots:
|
||||
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
// along with MRCI under the LICENSE.md file. If not, see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
RecoverAcct::RecoverAcct(QObject *parent) : CmdObject(parent) {}
|
||||
ResetPwRequest::ResetPwRequest(QObject *parent) : CmdObject(parent) {}
|
||||
VerifyEmail::VerifyEmail(QObject *parent) : CmdObject(parent) {}
|
||||
IsEmailVerified::IsEmailVerified(QObject *parent) : CmdObject(parent) {}
|
||||
RecoverAcct::RecoverAcct(QObject *parent) : CmdObject(parent) {}
|
||||
IsEmailVerified::IsEmailVerified(QObject *parent) : CmdObject(parent) {}
|
||||
ResetPwRequest::ResetPwRequest(QObject *parent) : CmdObject(parent) {}
|
||||
VerifyEmail::VerifyEmail(QObject *parent) : CmdObject(parent) {}
|
||||
|
||||
QString RecoverAcct::cmdName() {return "recover_acct";}
|
||||
QString ResetPwRequest::cmdName() {return "request_pw_reset";}
|
||||
QString VerifyEmail::cmdName() {return "verify_email";}
|
||||
QString IsEmailVerified::cmdName() {return "is_email_verified";}
|
||||
QString RecoverAcct::cmdName() {return "recover_acct";}
|
||||
QString ResetPwRequest::cmdName() {return "request_pw_reset";}
|
||||
QString VerifyEmail::cmdName() {return "verify_email";}
|
||||
QString IsEmailVerified::cmdName() {return "is_email_verified";}
|
||||
|
||||
void delRecoverPw(const QByteArray &uId)
|
||||
{
|
||||
|
@ -55,7 +55,7 @@ bool expired(const QByteArray &uId)
|
|||
|
||||
auto expiry = db.getData(COLUMN_TIME).toDateTime().addSecs(3600); // pw datetime + 1hour;
|
||||
|
||||
if (expiry > QDateTime::currentDateTime().toUTC())
|
||||
if (expiry > QDateTime::currentDateTimeUtc())
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ void RecoverAcct::addToThreshold()
|
|||
{
|
||||
Query db(this);
|
||||
|
||||
// log recovery attempt failure
|
||||
db.setType(Query::PUSH, TABLE_AUTH_LOG);
|
||||
db.addColumn(COLUMN_USER_ID, uId);
|
||||
db.addColumn(COLUMN_IPADDR, rdStringFromBlock(clientIp, BLKSIZE_CLIENT_IP));
|
||||
|
@ -83,6 +84,7 @@ void RecoverAcct::addToThreshold()
|
|||
auto confObj = confObject();
|
||||
auto maxAttempts = confObj[CONF_AUTO_LOCK_LIM].toInt();
|
||||
|
||||
// pull how many failed recovery attempts were made on this account
|
||||
db.setType(Query::PULL, TABLE_AUTH_LOG);
|
||||
db.addColumn(COLUMN_IPADDR);
|
||||
db.addCondition(COLUMN_USER_ID, uId);
|
||||
|
@ -95,11 +97,22 @@ void RecoverAcct::addToThreshold()
|
|||
{
|
||||
delRecoverPw(uId);
|
||||
|
||||
flags &= ~MORE_INPUT;
|
||||
// reset recovery attempts
|
||||
db.setType(Query::UPDATE, TABLE_AUTH_LOG);
|
||||
db.addColumn(COLUMN_COUNT, false);
|
||||
db.addCondition(COLUMN_USER_ID, uId);
|
||||
db.addCondition(COLUMN_RECOVER_ATTEMPT, true);
|
||||
db.addCondition(COLUMN_ACCEPTED, false);
|
||||
db.exec();
|
||||
|
||||
retCode = INVALID_PARAMS;
|
||||
flags &= ~MORE_INPUT;
|
||||
|
||||
errTxt("err: You have exceeded the maximum amount of recovery attempts, recovery password deleted.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
errTxt("err: Access denied.\n");
|
||||
errTxt("err: Access denied.\n\n");
|
||||
privTxt("Enter the temporary password (leave blank to cancel): ");
|
||||
}
|
||||
}
|
||||
|
@ -118,18 +131,42 @@ void RecoverAcct::procIn(const QByteArray &binIn, quint8 dType)
|
|||
{
|
||||
retCode = ABORTED;
|
||||
flags &= ~MORE_INPUT;
|
||||
|
||||
mainTxt("\n");
|
||||
}
|
||||
else if (!acceptablePw(pw, uId, &errMsg))
|
||||
{
|
||||
errTxt(errMsg + "\n");
|
||||
errTxt(errMsg + "\n\n");
|
||||
privTxt("Enter a new password (leave blank to cancel): ");
|
||||
}
|
||||
else
|
||||
{
|
||||
Query db(this);
|
||||
|
||||
// reset recovery attempts
|
||||
db.setType(Query::UPDATE, TABLE_AUTH_LOG);
|
||||
db.addColumn(COLUMN_COUNT, false);
|
||||
db.addCondition(COLUMN_USER_ID, uId);
|
||||
db.addCondition(COLUMN_RECOVER_ATTEMPT, true);
|
||||
db.addCondition(COLUMN_ACCEPTED, false);
|
||||
db.exec();
|
||||
|
||||
// log recovery accepted
|
||||
db.setType(Query::PUSH, TABLE_AUTH_LOG);
|
||||
db.addColumn(COLUMN_USER_ID, uId);
|
||||
db.addColumn(COLUMN_IPADDR, rdStringFromBlock(clientIp, BLKSIZE_CLIENT_IP));
|
||||
db.addColumn(COLUMN_AUTH_ATTEMPT, false);
|
||||
db.addColumn(COLUMN_RECOVER_ATTEMPT, true);
|
||||
db.addColumn(COLUMN_COUNT, false);
|
||||
db.addColumn(COLUMN_ACCEPTED, true);
|
||||
db.exec();
|
||||
|
||||
updatePassword(uId, pw, TABLE_USERS);
|
||||
delRecoverPw(uId);
|
||||
|
||||
flags &= ~MORE_INPUT;
|
||||
|
||||
mainTxt("\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -138,9 +175,12 @@ void RecoverAcct::procIn(const QByteArray &binIn, quint8 dType)
|
|||
{
|
||||
retCode = ABORTED;
|
||||
flags &= ~MORE_INPUT;
|
||||
|
||||
mainTxt("\n");
|
||||
}
|
||||
else if (!validPassword(pw))
|
||||
{
|
||||
errTxt("err: Invalid password.\n");
|
||||
addToThreshold();
|
||||
}
|
||||
else if (!auth(uId, pw, TABLE_PW_RECOVERY))
|
||||
|
@ -209,7 +249,6 @@ void ResetPwRequest::procIn(const QByteArray &binIn, uchar dType)
|
|||
|
||||
QByteArray uId;
|
||||
QString body;
|
||||
QString err;
|
||||
|
||||
if (!email.isEmpty() && validEmailAddr(email))
|
||||
{
|
||||
|
@ -218,11 +257,7 @@ void ResetPwRequest::procIn(const QByteArray &binIn, uchar dType)
|
|||
|
||||
retCode = INVALID_PARAMS;
|
||||
|
||||
if (!getEmailParams(cmdLine, msgFile, &body, &err))
|
||||
{
|
||||
errTxt(err);
|
||||
}
|
||||
else if (name.isEmpty() || !validUserName(name))
|
||||
if (name.isEmpty() || !validUserName(name))
|
||||
{
|
||||
errTxt("err: The -user or -email argument is empty, not found or invalid.\n");
|
||||
}
|
||||
|
@ -230,40 +265,39 @@ void ResetPwRequest::procIn(const QByteArray &binIn, uchar dType)
|
|||
{
|
||||
errTxt("err: No such user.\n");
|
||||
}
|
||||
else
|
||||
else if (getEmailParams(cmdLine, msgFile, &body))
|
||||
{
|
||||
retCode = NO_ERRORS;
|
||||
retCode = EXECUTION_FAIL;
|
||||
|
||||
auto pw = genPw();
|
||||
auto date = QDateTime::currentDateTimeUtc().toString("YYYY-MM-DD HH:MM:SS");
|
||||
auto mailArgs = parseArgs(cmdLine.toUtf8(), -1);
|
||||
auto pw = genPw();
|
||||
auto date = QDateTime::currentDateTimeUtc().toString("yyyy-MM-dd HH:mm:ss (t)");
|
||||
auto dbrdy = false;
|
||||
|
||||
if (recoverPWExists(uId))
|
||||
{
|
||||
updatePassword(uId, pw, TABLE_PW_RECOVERY);
|
||||
dbrdy = updatePassword(uId, pw, TABLE_PW_RECOVERY);
|
||||
}
|
||||
else
|
||||
{
|
||||
createTempPw(uId, pw);
|
||||
dbrdy = createTempPw(uId, pw);
|
||||
}
|
||||
|
||||
body.replace(DATE_SUB, date);
|
||||
body.replace(USERNAME_SUB, name);
|
||||
body.replace(OTP_SUB, pw);
|
||||
|
||||
cmdLine.replace(TARGET_EMAIL_SUB, email);
|
||||
cmdLine.replace(SUBJECT_SUB, "'" + escapeChars(subject, '\\', '\'') + "'");
|
||||
cmdLine.replace(MSG_SUB, "'" + escapeChars(body, '\\', '\'') + "'");
|
||||
|
||||
if (QProcess::startDetached(expandEnvVariables(mailArgs[0]), mailArgs.mid(1)))
|
||||
if (dbrdy)
|
||||
{
|
||||
mainTxt("A temporary password was sent to the email address associated with the account. this password will expire in 1 hour.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
retCode = EXECUTION_FAIL;
|
||||
body.replace(DATE_SUB, date);
|
||||
body.replace(USERNAME_SUB, name);
|
||||
body.replace(OTP_SUB, pw);
|
||||
|
||||
errTxt("err: The host email system has reported an internal error, please try again later.\n");
|
||||
cmdLine.replace(TARGET_EMAIL_SUB, email);
|
||||
cmdLine.replace(SUBJECT_SUB, "'" + escapeChars(subject, '\\', '\'') + "'");
|
||||
cmdLine.replace(MSG_SUB, "'" + escapeChars(body, '\\', '\'') + "'");
|
||||
|
||||
if (runDetachedProc(parseArgs(cmdLine.toUtf8(), -1)))
|
||||
{
|
||||
retCode = NO_ERRORS;
|
||||
|
||||
mainTxt("A temporary password was sent to the email address associated with the account. this password will expire in 1 hour.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -277,6 +311,8 @@ void VerifyEmail::procIn(const QByteArray &binIn, quint8 dType)
|
|||
|
||||
if (txt.isEmpty())
|
||||
{
|
||||
mainTxt("\n");
|
||||
|
||||
retCode = ABORTED;
|
||||
flags &= ~MORE_INPUT;
|
||||
}
|
||||
|
@ -291,11 +327,14 @@ void VerifyEmail::procIn(const QByteArray &binIn, quint8 dType)
|
|||
|
||||
async(ASYNC_RW_MY_INFO, uId);
|
||||
|
||||
flags &= ~MORE_INPUT;
|
||||
retCode = NO_ERRORS;
|
||||
flags &= ~MORE_INPUT;
|
||||
|
||||
mainTxt("\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
errTxt("err: The code you entered does not match.\n");
|
||||
errTxt("err: The code you entered does not match.\n\n");
|
||||
privTxt("Please try again: ");
|
||||
}
|
||||
}
|
||||
|
@ -312,26 +351,15 @@ void VerifyEmail::procIn(const QByteArray &binIn, quint8 dType)
|
|||
QString body;
|
||||
QString err;
|
||||
|
||||
if (email.isEmpty())
|
||||
{
|
||||
retCode = INVALID_PARAMS;
|
||||
retCode = INVALID_PARAMS;
|
||||
|
||||
errTxt("err: Your account currently has no email address, please contact an admin to update it.\n");
|
||||
}
|
||||
else if (!getEmailParams(cmdLine, msgFile, &body, &err))
|
||||
{
|
||||
retCode = INVALID_PARAMS;
|
||||
|
||||
errTxt(err);
|
||||
}
|
||||
else
|
||||
if (getEmailParams(cmdLine, msgFile, &body))
|
||||
{
|
||||
flags |= MORE_INPUT;
|
||||
code = QString::number(QRandomGenerator::global()->bounded(100000, 999999));
|
||||
|
||||
auto uName = rdStringFromBlock(userName, BLKSIZE_USER_NAME);
|
||||
auto date = QDateTime::currentDateTimeUtc().toString("yyyy-MM-dd HH:mm:ss");
|
||||
auto mailArgs = parseArgs(cmdLine.toUtf8(), -1);
|
||||
auto uName = rdStringFromBlock(userName, BLKSIZE_USER_NAME);
|
||||
auto date = QDateTime::currentDateTimeUtc().toString("yyyy-MM-dd HH:mm:ss (t)");
|
||||
|
||||
body.replace(DATE_SUB, date);
|
||||
body.replace(USERNAME_SUB, uName);
|
||||
|
@ -341,15 +369,13 @@ void VerifyEmail::procIn(const QByteArray &binIn, quint8 dType)
|
|||
cmdLine.replace(SUBJECT_SUB, "'" + escapeChars(subject, '\\', '\'') + "'");
|
||||
cmdLine.replace(MSG_SUB, "'" + escapeChars(body, '\\', '\'') + "'");
|
||||
|
||||
if (QProcess::startDetached(expandEnvVariables(mailArgs[0]), mailArgs.mid(1)))
|
||||
if (runDetachedProc(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
|
||||
{
|
||||
retCode = EXECUTION_FAIL;
|
||||
|
||||
errTxt("err: The host email system has reported an internal error, please try again later.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ void Auth::addToThreshold()
|
|||
{
|
||||
Query db(this);
|
||||
|
||||
// log the failed login attempt
|
||||
db.setType(Query::PUSH, TABLE_AUTH_LOG);
|
||||
db.addColumn(COLUMN_USER_ID, uId);
|
||||
db.addColumn(COLUMN_IPADDR, ip);
|
||||
|
@ -49,6 +50,7 @@ void Auth::addToThreshold()
|
|||
|
||||
auto maxAttempts = confObject()[CONF_AUTO_LOCK_LIM].toInt();
|
||||
|
||||
// pull all login attempts on the account
|
||||
db.setType(Query::PULL, TABLE_AUTH_LOG);
|
||||
db.addColumn(COLUMN_IPADDR);
|
||||
db.addCondition(COLUMN_USER_ID, uId);
|
||||
|
@ -59,12 +61,24 @@ void Auth::addToThreshold()
|
|||
|
||||
if (db.rows() > maxAttempts)
|
||||
{
|
||||
// reset login attempts
|
||||
db.setType(Query::UPDATE, TABLE_AUTH_LOG);
|
||||
db.addColumn(COLUMN_COUNT, false);
|
||||
db.addCondition(COLUMN_COUNT, true);
|
||||
db.addCondition(COLUMN_USER_ID, uId);
|
||||
db.addCondition(COLUMN_AUTH_ATTEMPT, true);
|
||||
db.exec();
|
||||
|
||||
// lock account
|
||||
db.setType(Query::UPDATE, TABLE_USERS);
|
||||
db.addColumn(COLUMN_LOCKED, true);
|
||||
db.addCondition(COLUMN_USER_ID, uId);
|
||||
db.exec();
|
||||
|
||||
flags &= ~MORE_INPUT;
|
||||
flags &= ~MORE_INPUT;
|
||||
retCode = INVALID_PARAMS;
|
||||
|
||||
errTxt("err: Maximum login attempts exceeded, the account is now locked.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -77,6 +91,7 @@ void Auth::confirmAuth()
|
|||
{
|
||||
Query db(this);
|
||||
|
||||
// reset login attempts
|
||||
db.setType(Query::UPDATE, TABLE_AUTH_LOG);
|
||||
db.addColumn(COLUMN_COUNT, false);
|
||||
db.addCondition(COLUMN_COUNT, true);
|
||||
|
@ -84,6 +99,7 @@ void Auth::confirmAuth()
|
|||
db.addCondition(COLUMN_AUTH_ATTEMPT, true);
|
||||
db.exec();
|
||||
|
||||
// log the login attempt as accepted
|
||||
db.setType(Query::PUSH, TABLE_AUTH_LOG);
|
||||
db.addColumn(COLUMN_USER_ID, uId);
|
||||
db.addColumn(COLUMN_IPADDR, ip);
|
||||
|
@ -205,6 +221,7 @@ void Auth::procIn(const QByteArray &binIn, quint8 dType)
|
|||
}
|
||||
else if (!validPassword(text))
|
||||
{
|
||||
errTxt("err: Invalid password.\n");
|
||||
addToThreshold();
|
||||
}
|
||||
else if (!auth(uId, text, TABLE_USERS))
|
||||
|
|
|
@ -26,13 +26,6 @@ IPHist::IPHist(QObject *parent) : TableViewer(parent)
|
|||
addTableColumn(TABLE_IPHIST, COLUMN_LOGENTRY);
|
||||
}
|
||||
|
||||
ListDBG::ListDBG(QObject *parent) : TableViewer(parent)
|
||||
{
|
||||
setParams(TABLE_DMESG, true);
|
||||
addTableColumn(TABLE_DMESG, COLUMN_TIME);
|
||||
addTableColumn(TABLE_DMESG, COLUMN_LOGENTRY);
|
||||
}
|
||||
|
||||
ListCommands::ListCommands(const QStringList &cmdList, QObject *parent) : CmdObject(parent)
|
||||
{
|
||||
list = cmdList;
|
||||
|
@ -43,7 +36,6 @@ MyInfo::MyInfo(QObject *parent) : CmdObject(parent) {}
|
|||
|
||||
QString HostInfo::cmdName() {return "host_info";}
|
||||
QString IPHist::cmdName() {return "ls_act_log";}
|
||||
QString ListDBG::cmdName() {return "ls_dbg";}
|
||||
QString MyInfo::cmdName() {return "my_info";}
|
||||
|
||||
QString ListCommands::shortText(const QString &cmdName)
|
||||
|
|
|
@ -88,17 +88,4 @@ public:
|
|||
explicit MyInfo(QObject *parent = nullptr);
|
||||
};
|
||||
|
||||
//-----------------------------------
|
||||
|
||||
class ListDBG : public TableViewer
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
static QString cmdName();
|
||||
|
||||
explicit ListDBG(QObject *parent = nullptr);
|
||||
};
|
||||
|
||||
#endif // INFO_H
|
||||
|
|
|
@ -218,7 +218,6 @@ void TableViewer::procIn(const QByteArray &binIn, quint8 dType)
|
|||
{
|
||||
delQuery.exec();
|
||||
|
||||
mainTxt(delQuery.errDetail());
|
||||
idle();
|
||||
onDel();
|
||||
}
|
||||
|
|
|
@ -150,20 +150,17 @@ void updateConf(const char *key, const QJsonValue &value)
|
|||
updateConf(obj);
|
||||
}
|
||||
|
||||
bool getEmailParams(const QString &mailCmd, const QString &bodyFile, QString *bodyText, QString *errMsg)
|
||||
bool getEmailParams(const QString &mailCmd, const QString &bodyFile, QString *bodyText)
|
||||
{
|
||||
auto ret = false;
|
||||
|
||||
errMsg->clear();
|
||||
bodyText->clear();
|
||||
|
||||
QFile file(bodyFile);
|
||||
|
||||
if (!file.open(QFile::ReadOnly))
|
||||
{
|
||||
errMsg->append("err: The host could not open the email message template, please notify a system administrator.\n");
|
||||
|
||||
qDebug() << "err: Could not open the email message template file '" << bodyFile << "' reason: " << file.errorString();
|
||||
qCritical() << "Could not open the email message template file '" << bodyFile << "' reason: " << file.errorString();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -173,17 +170,14 @@ bool getEmailParams(const QString &mailCmd, const QString &bodyFile, QString *bo
|
|||
(!body.contains(USERNAME_SUB, Qt::CaseInsensitive)) ||
|
||||
(!body.contains(OTP_SUB, Qt::CaseInsensitive)))
|
||||
{
|
||||
errMsg->append("err: The host email message template is invalid, please notify a system administrator.\n");
|
||||
|
||||
qDebug() << "err: Email message template '" << bodyFile << "' is missing one of the following key words: " << DATE_SUB << ", " << USERNAME_SUB << ", " << OTP_SUB;
|
||||
qCritical() << "Email message template '" << bodyFile << "' is missing one or more of the following key words: " << DATE_SUB << ", " << USERNAME_SUB << ", " << OTP_SUB;
|
||||
}
|
||||
else if ((mailCmd.contains(SUBJECT_SUB, Qt::CaseInsensitive)) ||
|
||||
(mailCmd.contains(MSG_SUB, Qt::CaseInsensitive)) ||
|
||||
(mailCmd.contains(TARGET_EMAIL_SUB, Qt::CaseInsensitive)))
|
||||
{
|
||||
errMsg->append("err: The host email client parameters are invalid, please notify a system administrator.\n");
|
||||
|
||||
qDebug() << "err: Email client command line '" << mailCmd << "' is missing one of the following key words: " << SUBJECT_SUB << ", " << MSG_SUB << ", " << TARGET_EMAIL_SUB;
|
||||
qCritical() << "Email client command line '" << mailCmd << "' is missing one or more of the following key words: " << SUBJECT_SUB << ", " << MSG_SUB << ", " << TARGET_EMAIL_SUB;
|
||||
qCritical() << "Mail command: " << mailCmd;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -239,9 +233,16 @@ QString boolStr(bool state)
|
|||
|
||||
QString genSerialNumber()
|
||||
{
|
||||
Serial::serialIndex++;
|
||||
Serial::threadIndex++;
|
||||
|
||||
return QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch()) + "-" + QString::number(Serial::serialIndex);
|
||||
return QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch()) + "-" + QString::number(Serial::threadIndex);
|
||||
}
|
||||
|
||||
QString genMsgNumber()
|
||||
{
|
||||
Serial::msgIndex++;
|
||||
|
||||
return QDateTime::currentDateTime().toString("yyyy.MM.dd.hh.mm.ss.") + QString::number(Serial::msgIndex).rightJustified(5, ' ', true);
|
||||
}
|
||||
|
||||
void serializeThread(QThread *thr)
|
||||
|
@ -1053,7 +1054,7 @@ QString getParam(const QString &key, const QStringList &args)
|
|||
|
||||
QString ret;
|
||||
|
||||
int pos = args.indexOf(QRegExp(key, Qt::CaseInsensitive));
|
||||
int pos = args.indexOf(QRegularExpression(key, QRegularExpression::CaseInsensitiveOption));
|
||||
|
||||
if (pos != -1)
|
||||
{
|
||||
|
@ -1121,8 +1122,6 @@ ShellIPC::ShellIPC(const QStringList &args, bool supressErr, QObject *parent) :
|
|||
|
||||
bool ShellIPC::connectToHost()
|
||||
{
|
||||
auto pipeInfo = QFileInfo(HOST_CONTROL_PIPE);
|
||||
|
||||
connectToServer(HOST_CONTROL_PIPE);
|
||||
|
||||
if (!waitForConnected() && !holdErrs)
|
||||
|
@ -1154,4 +1153,6 @@ void ShellIPC::dataIn()
|
|||
emit closeInstance();
|
||||
}
|
||||
|
||||
quint64 Serial::serialIndex = 0;
|
||||
quint64 Serial::threadIndex = 0;
|
||||
quint16 Serial::msgIndex = 0;
|
||||
bool Serial::msgDetails = true;
|
||||
|
|
16
src/common.h
16
src/common.h
|
@ -26,12 +26,11 @@
|
|||
#include <QRandomGenerator>
|
||||
#include <QProcess>
|
||||
#include <QHash>
|
||||
#include <QRegExp>
|
||||
#include <QRegularExpression>
|
||||
#include <QStringList>
|
||||
#include <QDateTime>
|
||||
#include <QHostAddress>
|
||||
#include <QCoreApplication>
|
||||
#include <QTextCodec>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QSysInfo>
|
||||
|
@ -71,7 +70,6 @@
|
|||
#include <QSqlError>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QTextCodec>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDateTime>
|
||||
|
||||
|
@ -82,7 +80,7 @@
|
|||
#include "mem_share.h"
|
||||
|
||||
#define APP_NAME "MRCI"
|
||||
#define APP_VER "5.0.2.1"
|
||||
#define APP_VER "5.1.2.1"
|
||||
#define APP_TARGET "mrci"
|
||||
#define SERVER_HEADER_TAG "MRCI"
|
||||
#define HOST_CONTROL_PIPE "MRCI_HOST_CONTROL"
|
||||
|
@ -91,6 +89,7 @@
|
|||
#define LOCAL_BUFFSIZE 16777215
|
||||
#define CLIENT_HEADER_LEN 292
|
||||
#define MAX_LS_ENTRIES 50
|
||||
#define MAX_LOG_SIZE 100000000
|
||||
|
||||
#define SUBJECT_SUB "%subject%"
|
||||
#define MSG_SUB "%message_body%"
|
||||
|
@ -111,6 +110,7 @@
|
|||
|
||||
#define DEFAULT_DB_DRIVER "QSQLITE"
|
||||
#define DEFAULT_DB_FILENAME "data.db"
|
||||
#define DEFAULT_LOG_FILENAME "messages.log"
|
||||
#define DEFAULT_CERT_FILENAME "tls_chain.pem"
|
||||
#define DEFAULT_PRIV_FILENAME "tls_priv.pem"
|
||||
#define DEFAULT_RES_PW_FILENAME "res_pw_template.txt"
|
||||
|
@ -152,7 +152,6 @@
|
|||
#define TABLE_CMD_RANKS "command_ranks"
|
||||
#define TABLE_AUTH_LOG "auth_log"
|
||||
#define TABLE_PW_RECOVERY "pw_recovery"
|
||||
#define TABLE_DMESG "host_debug_messages"
|
||||
#define TABLE_CHANNELS "channels"
|
||||
#define TABLE_CH_MEMBERS "channel_members"
|
||||
#define TABLE_SUB_CHANNELS "sub_channels"
|
||||
|
@ -357,7 +356,7 @@ void containsActiveCh(const char *subChs, char *actBlock);
|
|||
void updateConf(const char *key, const QJsonValue &value);
|
||||
void updateConf(const QJsonObject &obj);
|
||||
void wrDefaultMailTemplates(const QJsonObject &obj);
|
||||
bool getEmailParams(const QString &mailCmd, const QString &bodyFile, QString *bodyText, QString *errMsg);
|
||||
bool getEmailParams(const QString &mailCmd, const QString &bodyFile, QString *bodyText);
|
||||
bool acceptablePw(const QString &pw, const QByteArray &uId, QString *errMsg);
|
||||
bool acceptablePw(const QString &pw, const QString &uName, const QString &dispName, const QString &email, QString *errMsg);
|
||||
bool containsNewLine(const QString &str);
|
||||
|
@ -399,6 +398,7 @@ QString boolStr(bool state);
|
|||
QString getParam(const QString &key, const QStringList &args);
|
||||
QString escapeChars(const QString &str, const QChar &escapeChr, const QChar &chr);
|
||||
QString genSerialNumber();
|
||||
QString genMsgNumber();
|
||||
QString getLocalFilePath(const QString &fileName, bool var = false);
|
||||
QStringList parseArgs(const QByteArray &data, int maxArgs, int *pos = nullptr);
|
||||
QJsonObject confObject();
|
||||
|
@ -467,7 +467,9 @@ class Serial
|
|||
|
||||
public:
|
||||
|
||||
static quint64 serialIndex;
|
||||
static quint64 threadIndex;
|
||||
static quint16 msgIndex;
|
||||
static bool msgDetails;
|
||||
};
|
||||
|
||||
#endif // COMMON_H
|
||||
|
|
46
src/db.cpp
46
src/db.cpp
|
@ -283,7 +283,7 @@ bool auth(const QByteArray &uId, const QString &password, const QString &table)
|
|||
|
||||
QCryptographicHash hasher(QCryptographicHash::Keccak_512);
|
||||
|
||||
hasher.addData(QTextCodec::codecForName("UTF-16LE")->fromUnicode(password) + salt);
|
||||
hasher.addData(password.toUtf8() + salt);
|
||||
|
||||
db.setType(Query::PULL, table);
|
||||
db.addColumn(COLUMN_HASH);
|
||||
|
@ -348,35 +348,19 @@ Query::Query(QObject *parent) : QObject(parent)
|
|||
if (!testDbWritable())
|
||||
{
|
||||
queryOk = false;
|
||||
lastErr = "Write access to the database is denied.";
|
||||
|
||||
qCritical("%s", "Write access to the database is denied.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
queryOk = false;
|
||||
lastErr = db.lastError().databaseText().trimmed();
|
||||
|
||||
qCritical("%s", db.lastError().databaseText().trimmed().toUtf8().constData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString Query::errDetail()
|
||||
{
|
||||
QString ret;
|
||||
QString errTxt = "none";
|
||||
|
||||
if (!lastErr.isEmpty())
|
||||
{
|
||||
errTxt = lastErr;
|
||||
}
|
||||
|
||||
QTextStream txtOut(&ret);
|
||||
|
||||
txtOut << " driver error: " << errTxt << Qt::endl;
|
||||
txtOut << " query: " << qStr << jStr << wStr << limit << Qt::endl;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Query::inErrorstate()
|
||||
{
|
||||
return !queryOk;
|
||||
|
@ -440,7 +424,6 @@ void Query::setType(QueryType qType, const QString &tbl)
|
|||
limit.clear();
|
||||
columnList.clear();
|
||||
bindValues.clear();
|
||||
lastErr.clear();
|
||||
directBind.clear();
|
||||
whereBinds.clear();
|
||||
columnsAsPassed.clear();
|
||||
|
@ -837,7 +820,6 @@ bool Query::exec()
|
|||
}
|
||||
|
||||
queryOk = query.exec();
|
||||
lastErr = query.lastError().driverText().trimmed();
|
||||
rowsAffected = query.numRowsAffected();
|
||||
|
||||
if (queryOk && query.isSelect())
|
||||
|
@ -858,6 +840,24 @@ bool Query::exec()
|
|||
{
|
||||
createRan = true;
|
||||
}
|
||||
else if (!queryOk)
|
||||
{
|
||||
auto errobj = query.lastError();
|
||||
|
||||
qCritical() << "Database failure";
|
||||
qCritical() << "Query prep string: " + qStr + jStr + wStr + limit + ";";
|
||||
qCritical() << "Driver text: " + errobj.driverText();
|
||||
qCritical() << "Database text: " + errobj.databaseText();
|
||||
|
||||
switch (errobj.type())
|
||||
{
|
||||
case QSqlError::NoError: qCritical() << "Error type: NoError"; break;
|
||||
case QSqlError::ConnectionError: qCritical() << "Error type: ConnectionError"; break;
|
||||
case QSqlError::StatementError: qCritical() << "Error type: StatementError"; break;
|
||||
case QSqlError::TransactionError: qCritical() << "Error type: TransactionError"; break;
|
||||
case QSqlError::UnknownError: qCritical() << "Error type: UnknownError"; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return queryOk;
|
||||
|
|
3
src/db.h
3
src/db.h
|
@ -105,8 +105,6 @@ public:
|
|||
QStringList tables();
|
||||
QStringList columnsInTable(const QString &tbl);
|
||||
QVariant getData(const QString &column, int row = 0);
|
||||
QString errDetail();
|
||||
QString errString();
|
||||
QList<QList<QVariant> > &allData();
|
||||
|
||||
private:
|
||||
|
@ -116,7 +114,6 @@ private:
|
|||
bool queryOk;
|
||||
int rowsAffected;
|
||||
QString table;
|
||||
QString lastErr;
|
||||
QString limit;
|
||||
QString qStr;
|
||||
QString wStr;
|
||||
|
|
|
@ -16,20 +16,15 @@
|
|||
// along with MRCI under the LICENSE.md file. If not, see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
bool setupDb(QString *errMsg)
|
||||
bool setupDb()
|
||||
{
|
||||
auto ret = true;
|
||||
|
||||
errMsg->clear();
|
||||
|
||||
Query query(QThread::currentThread());
|
||||
|
||||
if (query.inErrorstate())
|
||||
{
|
||||
ret = false;
|
||||
|
||||
errMsg->append("database open failure: \n");
|
||||
errMsg->append(query.errDetail());
|
||||
}
|
||||
|
||||
if (ret)
|
||||
|
@ -74,15 +69,6 @@ bool setupDb(QString *errMsg)
|
|||
ret = query.exec();
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
query.setType(Query::CREATE_TABLE, TABLE_DMESG);
|
||||
query.addColumn(COLUMN_TIME);
|
||||
query.addColumn(COLUMN_LOGENTRY);
|
||||
|
||||
ret = query.exec();
|
||||
}
|
||||
|
||||
if (ret)
|
||||
{
|
||||
query.setType(Query::CREATE_TABLE, TABLE_USERS);
|
||||
|
@ -169,11 +155,5 @@ bool setupDb(QString *errMsg)
|
|||
ret = query.exec();
|
||||
}
|
||||
|
||||
if (query.inErrorstate())
|
||||
{
|
||||
errMsg->append("main setup: \n");
|
||||
errMsg->append(query.errDetail());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,6 @@
|
|||
|
||||
#include "db.h"
|
||||
|
||||
bool setupDb(QString *errMsg = nullptr);
|
||||
bool setupDb();
|
||||
|
||||
#endif // DB_SETUP_H
|
||||
|
|
262
src/main.cpp
262
src/main.cpp
|
@ -37,20 +37,80 @@ extern "C"
|
|||
// a "no OPENSSL_Applink" error.
|
||||
#include <src/applink.c>
|
||||
}
|
||||
#else
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
|
||||
void windowsLog(const QByteArray &id, const QByteArray &msg)
|
||||
{
|
||||
auto file = QFile(getLocalFilePath(DEFAULT_LOG_FILENAME));
|
||||
auto date = QDateTime::currentDateTime();
|
||||
|
||||
if (file.exists())
|
||||
{
|
||||
if (file.size() >= MAX_LOG_SIZE)
|
||||
{
|
||||
file.remove();
|
||||
|
||||
windowsLog(id, msg);
|
||||
}
|
||||
else if (file.open(QIODevice::Append))
|
||||
{
|
||||
file.write(date.toString(Qt::ISODateWithMs).toUtf8() + ": msg_id: " + id + " " + msg + "\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
file.write(date.toString(Qt::ISODateWithMs).toUtf8() + ": msg_id: " + id + " " + msg + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void msgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
{
|
||||
Q_UNUSED(type)
|
||||
Q_UNUSED(context)
|
||||
|
||||
if (!msg.contains("QSslSocket: cannot resolve"))
|
||||
{
|
||||
Query db;
|
||||
auto logMsg = msg.toUtf8();
|
||||
|
||||
db.setType(Query::PUSH, TABLE_DMESG);
|
||||
db.addColumn(COLUMN_LOGENTRY, msg);
|
||||
db.exec();
|
||||
switch (type)
|
||||
{
|
||||
case QtDebugMsg: case QtInfoMsg: case QtWarningMsg:
|
||||
{
|
||||
fprintf(stdout, "inf: %s\n", logMsg.constData());
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
format.chop(2);
|
||||
|
||||
windowsLog(format, "inf: " + utf8);
|
||||
#else
|
||||
syslog(LOG_INFO, "inf: %s", logMsg.constData());
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case QtCriticalMsg: case QtFatalMsg:
|
||||
{
|
||||
fprintf(stderr, "err: %s\n", logMsg.constData());
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
format.chop(2);
|
||||
|
||||
windowsLog(format, "err: " + utf8);
|
||||
#else
|
||||
syslog(LOG_ERR, "err: %s", logMsg.constData());
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,12 +134,16 @@ void showHelp()
|
|||
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;
|
||||
txtOut << " -elevate : elevate any user account to rank 1." << Qt::endl;
|
||||
txtOut << " -res_pw : reset a user account password with a randomized one time password." << Qt::endl;
|
||||
txtOut << " -add_admin : create a rank 1 account with a randomized one time password." << 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;
|
||||
txtOut << " -mem_ses : the shared memory key for the session." << Qt::endl;
|
||||
txtOut << " -mem_host : the shared memory key for the host main process." << Qt::endl << Qt::endl;
|
||||
txtOut << "Details:" << Qt::endl << Qt::endl;
|
||||
txtOut << "res_pw - this argument takes a single string representing a user name to reset the password. the host" << Qt::endl;
|
||||
txtOut << " will set a randomized password and display it on the CLI." << Qt::endl << Qt::endl;
|
||||
txtOut << " example: -res_pw somebody" << Qt::endl << Qt::endl;
|
||||
txtOut << "add_admin - this argument takes a single string representing a user name to create a rank 1 account with." << Qt::endl;
|
||||
txtOut << " the host will set a randomized password for it and display it on the CLI. this user will be" << Qt::endl;
|
||||
txtOut << " required to change the password upon logging in." << Qt::endl;
|
||||
|
@ -91,14 +155,6 @@ void showHelp()
|
|||
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)
|
||||
{
|
||||
*retCode = 1;
|
||||
|
||||
QTextStream(stderr) << "" << Qt::endl << "err: Stop error." << Qt::endl;
|
||||
QTextStream(stderr) << " what happened: " << Qt::endl << *errMsg << Qt::endl << Qt::endl;
|
||||
}
|
||||
|
||||
int shellToHost(const QStringList &args, bool holdErrs, QCoreApplication &app)
|
||||
{
|
||||
auto ret = 0;
|
||||
|
@ -130,10 +186,6 @@ int main(int argc, char *argv[])
|
|||
QCoreApplication::setApplicationName(APP_NAME);
|
||||
QCoreApplication::setApplicationVersion(APP_VER);
|
||||
|
||||
QString err;
|
||||
|
||||
qInstallMessageHandler(msgHandler);
|
||||
|
||||
// args.append("-ls_sql_drvs"); // debug
|
||||
|
||||
if (args.contains("-run_cmd", Qt::CaseInsensitive) ||
|
||||
|
@ -141,7 +193,7 @@ int main(int argc, char *argv[])
|
|||
args.contains("-exempt_cmds", Qt::CaseInsensitive) ||
|
||||
args.contains("-user_cmds", Qt::CaseInsensitive))
|
||||
{
|
||||
if (setupDb(&err))
|
||||
if (setupDb())
|
||||
{
|
||||
auto *mod = new Module(&app);
|
||||
|
||||
|
@ -152,7 +204,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
else
|
||||
{
|
||||
soeDueToDbErr(&ret, &err);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
else if (args.contains("-help", Qt::CaseInsensitive) || args.size() == 1)
|
||||
|
@ -163,7 +215,7 @@ int main(int argc, char *argv[])
|
|||
{
|
||||
QTextStream(stdout) << "" << Qt::endl;
|
||||
|
||||
for (auto driver : QSqlDatabase::drivers())
|
||||
for (const auto &driver : QSqlDatabase::drivers())
|
||||
{
|
||||
QTextStream(stdout) << driver << Qt::endl;
|
||||
}
|
||||
|
@ -181,91 +233,131 @@ int main(int argc, char *argv[])
|
|||
args.contains("-status", Qt::CaseInsensitive) ||
|
||||
args.contains("-load_ssl", Qt::CaseInsensitive))
|
||||
{
|
||||
qInstallMessageHandler(msgHandler);
|
||||
|
||||
ret = shellToHost(args, false, app);
|
||||
}
|
||||
else if (setupDb(&err))
|
||||
else
|
||||
{
|
||||
if (args.contains("-host", Qt::CaseInsensitive))
|
||||
{
|
||||
auto *serv = new TCPServer(&app);
|
||||
qInstallMessageHandler(msgHandler);
|
||||
|
||||
if (serv->start())
|
||||
{
|
||||
ret = QCoreApplication::exec();
|
||||
}
|
||||
}
|
||||
else if (args.contains("-host_trig", Qt::CaseInsensitive))
|
||||
if (setupDb())
|
||||
{
|
||||
QProcess::startDetached(QCoreApplication::applicationFilePath(), QStringList() << "-host");
|
||||
}
|
||||
else if (args.contains("-elevate", Qt::CaseInsensitive))
|
||||
{
|
||||
ret = 1;
|
||||
if (args.contains("-host", Qt::CaseInsensitive))
|
||||
{
|
||||
auto *serv = new TCPServer(&app);
|
||||
|
||||
QByteArray uId;
|
||||
if (serv->start())
|
||||
{
|
||||
ret = QCoreApplication::exec();
|
||||
}
|
||||
}
|
||||
else if (args.contains("-host_trig", Qt::CaseInsensitive))
|
||||
{
|
||||
QProcess::startDetached(QCoreApplication::applicationFilePath(), QStringList() << "-host");
|
||||
}
|
||||
else if (args.contains("-elevate", Qt::CaseInsensitive))
|
||||
{
|
||||
ret = 1;
|
||||
|
||||
if (args.size() <= 2)
|
||||
{
|
||||
QTextStream(stderr) << "err: A user name was not given." << Qt::endl;
|
||||
QByteArray uId;
|
||||
|
||||
if (args.size() <= 2)
|
||||
{
|
||||
QTextStream(stderr) << "err: A user name was not given." << Qt::endl;
|
||||
}
|
||||
else if (!validUserName(args[2]))
|
||||
{
|
||||
QTextStream(stderr) << "err: Invalid user name." << Qt::endl;
|
||||
}
|
||||
else if (!userExists(args[2], &uId))
|
||||
{
|
||||
QTextStream(stderr) << "err: The user name does not exists." << Qt::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
Query db;
|
||||
|
||||
db.setType(Query::UPDATE, TABLE_USERS);
|
||||
db.addColumn(COLUMN_HOST_RANK, 1);
|
||||
db.addCondition(COLUMN_USER_ID, uId);
|
||||
|
||||
if (db.exec())
|
||||
{
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!validUserName(args[2]))
|
||||
else if (args.contains("-add_admin", Qt::CaseInsensitive))
|
||||
{
|
||||
QTextStream(stderr) << "err: Invalid user name." << Qt::endl;
|
||||
ret = 1;
|
||||
|
||||
if (args.size() <= 2)
|
||||
{
|
||||
QTextStream(stderr) << "err: A user name was not given." << Qt::endl;
|
||||
}
|
||||
else if (!validUserName(args[2]))
|
||||
{
|
||||
QTextStream(stderr) << "err: Invalid user name." << Qt::endl;
|
||||
}
|
||||
else if (userExists(args[2]))
|
||||
{
|
||||
QTextStream(stderr) << "err: The user name already exists." << Qt::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto randPw = genPw();
|
||||
|
||||
if (createUser(args[2], args[2] + "@change_me.null", "", randPw, 1, true))
|
||||
{
|
||||
QTextStream(stdout) << "password: " << randPw << Qt::endl;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!userExists(args[2], &uId))
|
||||
else if (args.contains("-res_pw", Qt::CaseInsensitive))
|
||||
{
|
||||
QTextStream(stderr) << "err: The user name does not exists." << Qt::endl;
|
||||
ret = 1;
|
||||
|
||||
QByteArray uId;
|
||||
|
||||
if (args.size() <= 2)
|
||||
{
|
||||
QTextStream(stderr) << "err: A user name was not given." << Qt::endl;
|
||||
}
|
||||
else if (!validUserName(args[2]))
|
||||
{
|
||||
QTextStream(stderr) << "err: Invalid user name." << Qt::endl;
|
||||
}
|
||||
else if (!userExists(args[2], &uId))
|
||||
{
|
||||
QTextStream(stderr) << "err: The user name does not exists." << Qt::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto randPw = genPw();
|
||||
|
||||
if (updatePassword(uId, randPw, TABLE_USERS, true))
|
||||
{
|
||||
QTextStream(stdout) << "password: " << randPw << Qt::endl;
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Query db;
|
||||
|
||||
db.setType(Query::UPDATE, TABLE_USERS);
|
||||
db.addColumn(COLUMN_HOST_RANK, 1);
|
||||
db.addCondition(COLUMN_USER_ID, uId);
|
||||
db.exec();
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
else if (args.contains("-add_admin", Qt::CaseInsensitive))
|
||||
{
|
||||
ret = 1;
|
||||
|
||||
if (args.size() <= 2)
|
||||
{
|
||||
QTextStream(stderr) << "err: A user name was not given." << Qt::endl;
|
||||
}
|
||||
else if (!validUserName(args[2]))
|
||||
{
|
||||
QTextStream(stderr) << "err: Invalid user name." << Qt::endl;
|
||||
}
|
||||
else if (userExists(args[2]))
|
||||
{
|
||||
QTextStream(stderr) << "err: The user name already exists." << Qt::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto randPw = genPw();
|
||||
|
||||
createUser(args[2], "", "", randPw, 1, true);
|
||||
|
||||
QTextStream(stdout) << "password: " << randPw << Qt::endl;
|
||||
|
||||
ret = 0;
|
||||
showHelp();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
showHelp();
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
soeDueToDbErr(&ret, &err);
|
||||
}
|
||||
|
||||
cleanupDbConnection();
|
||||
cleanupDbConnection();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -361,11 +361,11 @@ bool MemShare::createSharedMem(const QByteArray &sesId, const QString &hostKey)
|
|||
|
||||
if (!sharedMem->create(len))
|
||||
{
|
||||
qDebug() << "err: Failed to create a shared memory master block. reason: " + sharedMem->errorString();
|
||||
qCritical() << "Failed to create a shared memory master block. reason: " + sharedMem->errorString();
|
||||
}
|
||||
else if (!hostSharedMem->attach())
|
||||
{
|
||||
qDebug() << "err: Failed to attach to the host shared memory master block. reason: " + hostSharedMem->errorString();
|
||||
qCritical() << "Failed to attach to the host shared memory master block. reason: " + hostSharedMem->errorString();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -387,11 +387,11 @@ bool MemShare::attachSharedMem(const QString &sKey, const QString &hKey)
|
|||
|
||||
if (!sharedMem->attach())
|
||||
{
|
||||
qDebug() << "err: Failed to attach to the session shared memory master block. reason: " + sharedMem->errorString();
|
||||
qCritical() << "Failed to attach to the session shared memory master block. reason: " + sharedMem->errorString();
|
||||
}
|
||||
else if (!hostSharedMem->attach())
|
||||
{
|
||||
qDebug() << "err: Failed to attach to the host shared memory master block. reason: " + hostSharedMem->errorString();
|
||||
qCritical() << "Failed to attach to the host shared memory master block. reason: " + hostSharedMem->errorString();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -54,7 +54,6 @@ QStringList Module::userCmdList()
|
|||
ret << FileInfo::cmdName();
|
||||
ret << MakePath::cmdName();
|
||||
ret << ChangeDir::cmdName();
|
||||
ret << ListDBG::cmdName();
|
||||
ret << ToPeer::cmdName();
|
||||
ret << LsP2P::cmdName();
|
||||
ret << P2POpen::cmdName();
|
||||
|
@ -134,7 +133,7 @@ QStringList Module::rankExemptList()
|
|||
|
||||
bool Module::runCmd(const QString &name)
|
||||
{
|
||||
bool ret = true;
|
||||
auto ret = true;
|
||||
|
||||
if (userCmdList().contains(name, Qt::CaseInsensitive))
|
||||
{
|
||||
|
@ -178,7 +177,6 @@ bool Module::runCmd(const QString &name)
|
|||
else if (noCaseMatch(name, FileInfo::cmdName())) new FileInfo(this);
|
||||
else if (noCaseMatch(name, MakePath::cmdName())) new MakePath(this);
|
||||
else if (noCaseMatch(name, ChangeDir::cmdName())) new ChangeDir(this);
|
||||
else if (noCaseMatch(name, ListDBG::cmdName())) new ListDBG(this);
|
||||
else if (noCaseMatch(name, ToPeer::cmdName())) new ToPeer(this);
|
||||
else if (noCaseMatch(name, LsP2P::cmdName())) new LsP2P(this);
|
||||
else if (noCaseMatch(name, P2POpen::cmdName())) new P2POpen(this);
|
||||
|
@ -209,14 +207,14 @@ bool Module::runCmd(const QString &name)
|
|||
else if (noCaseMatch(name, Tree::cmdName())) new Tree(this);
|
||||
else
|
||||
{
|
||||
qDebug() << "Module err: the loader claims command name '" << name << "' exists but no command object was actually matched/built.";
|
||||
qCritical() << "Internal module - the module claim command name '" << name << "' exists but no command object was actually matched/built.";
|
||||
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Module err: command name '" << name << "' not found.";
|
||||
qCritical() << "Internal module - command name '" << name << "' not found.";
|
||||
|
||||
ret = false;
|
||||
}
|
||||
|
@ -231,7 +229,7 @@ void Module::listCmds(const QStringList &list)
|
|||
|
||||
bool Module::start(const QStringList &args)
|
||||
{
|
||||
bool ret = true;
|
||||
auto ret = true;
|
||||
|
||||
if (args.contains("-run_cmd"))
|
||||
{
|
||||
|
|
|
@ -83,7 +83,7 @@ void Session::connectToPeer(const QSharedPointer<SessionCarrier> &peer)
|
|||
{
|
||||
if (peer->sessionObj == nullptr)
|
||||
{
|
||||
qDebug() << "Session::connectToPeer() the peer session object is null.";
|
||||
qCritical() << "Session::connectToPeer() the peer session object is null.";
|
||||
}
|
||||
else if ((peer->sessionObj != this) && (flags & SESSION_RDY))
|
||||
{
|
||||
|
|
|
@ -101,7 +101,7 @@ QString parseMd(const QString &cmdName, int offset)
|
|||
|
||||
if (!file.open(QFile::ReadOnly))
|
||||
{
|
||||
qDebug() << "err: internal command: " << cmdName << " does not have a document file.";
|
||||
qDebug() << "Internal command: " << cmdName << " does not have a document file.";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
systemctl -q stop $app_target
|
||||
systemctl -q disable $app_target
|
||||
rm -v /etc/systemd/system/$app_target.service
|
||||
rm -v /lib/systemd/system/$app_target.service
|
||||
rm -v /usr/bin/$app_target
|
||||
rm -rv $install_dir
|
||||
deluser $app_target
|
||||
|
|
Loading…
Reference in New Issue
Block a user