Added multi-threaded support

along with the main thread, the project now supports 3
additional threads.

-the Session object operates in it's own thread that
 will take data directly from the host, process and
 direct it to various parts of the client.

-the main GUI and client command objects will continue
 to operate on the main thread.

-the GenFile object which is dedicated to processing
 GEN_FILE data to or from the host will operate in
 it's own thread.

-last thread is for a new object called TextWorker.
 this object will take all of the text buffered from
 all of other objects and then send the text to be
 displayed by the main GUI in a slower, controlled
 manner.

by building out all of these threads, the main GUI
will not lag even when the host or the client itself
is outputting an excessive amount of data.

fixed a bug that prevented the client from being able
to totally unhook a genfile command of it crashes.

also reduced the default max lines from 5000 to 1000.
This commit is contained in:
Maurice ONeal 2019-12-04 22:01:52 -05:00
parent 51c2e63a24
commit ac364c1e8f
20 changed files with 468 additions and 224 deletions

View File

@ -43,6 +43,16 @@ CmdLine::CmdLine(QWidget *parent) : QComboBox(parent)
connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(checkForHistReset(QString)));
}
void CmdLine::cacheTxt(quint8 typeId, QString txt)
{
Shared::cacheTxt(Shared::TXT_IN, typeId, txt);
if (!(*Shared::activeDisp))
{
emit txtInCache();
}
}
void CmdLine::setFlags(int flgs)
{
flags |= flgs;
@ -50,7 +60,7 @@ void CmdLine::setFlags(int flgs)
void CmdLine::unsetFlags(int flgs)
{
flags ^= flgs;
flags &= ~flgs;
}
void CmdLine::selectionCheck()
@ -105,7 +115,7 @@ void CmdLine::echo(const QString &line)
{
if (!line.trimmed().isEmpty())
{
emit mainTxtOut(line + "\n\n");
cacheTxt(TEXT, line + "\n\n");
}
}
@ -162,7 +172,7 @@ void CmdLine::toHost(const QString &cmdName, const QString &args)
if (!Shared::hostCmds->values().contains(cmdName))
{
emit errTxtOut("err: No such command: " + cmdName + "\n\n");
cacheTxt(ERR, "err: No such command: " + cmdName + "\n\n");
}
else if (Shared::genfileCmds->contains(cmdId))
{

View File

@ -35,6 +35,7 @@ private:
void echo(const QString &line);
void syncHistToFile();
void syncHistFromFile();
void cacheTxt(quint8 typeId, QString txt);
void duplicateScan(const QString &txt);
void toHost(const QString &cmdName, const QString &args);
void toLocalCmd(const QString &cmdName, const QString &args);
@ -64,8 +65,7 @@ signals:
void dataToHookedHost(const QByteArray &data, uchar dType);
void dataToGenFile(quint16 cmdId, const QByteArray &data);
void dataToHookedGenFile(const QByteArray &data);
void mainTxtOut(const QString &txt);
void errTxtOut(const QString &txt);
void txtInCache();
};
#endif // CMDLINE_H

View File

@ -91,7 +91,7 @@ void SaveBookmark::run(const QString &name, QStringList &args)
}
else
{
emit errTxtOut("err: Could not open the bookmark file for writing, reason: " + file.errorString() + "\n");
cacheTxt(ERR, "err: Could not open the bookmark file for writing, reason: " + file.errorString() + "\n");
}
file.close();
@ -99,7 +99,8 @@ void SaveBookmark::run(const QString &name, QStringList &args)
void SaveBookmark::askOverwrite()
{
emit mainTxtOut("'" + baseName + "' already exists. do you want to overwrite? (y/n): ");
cacheTxt(TEXT, "'" + baseName + "' already exists. do you want to overwrite? (y/n): ");
emit setUserIO(LOCAL_HOOK);
}
@ -129,7 +130,7 @@ void SaveBookmark::dataIn(const QString &argsLine)
if (name.isEmpty())
{
emit errTxtOut("err: Bookmark name (-name) not given.\n");
cacheTxt(ERR, "err: Bookmark name (-name) not given.\n");
}
else
{
@ -151,21 +152,21 @@ void SaveBookmark::dataIn(const QString &argsLine)
void ListBookmarks::dataIn(const QString &argsLine)
{
Q_UNUSED(argsLine);
Q_UNUSED(argsLine)
QStringList list = QDir(appDataDir() + BOOKMARK_FOLDER).entryList(QStringList() << "*.json", QDir::Files, QDir::Name);
if (list.isEmpty())
{
emit mainTxtOut("Empty...\n");
cacheTxt(TEXT, "Empty...\n");
}
else
{
emit mainTxtOut("Bookmarks:\n\n");
cacheTxt(TEXT, "Bookmarks:\n\n");
for (int i = 0; i < list.size(); ++i)
{
emit mainTxtOut(" " + QFileInfo(list[i]).baseName() + "\n");
cacheTxt(TEXT, " " + QFileInfo(list[i]).baseName() + "\n");
}
}
}
@ -177,7 +178,7 @@ void DeleteBookmark::dataIn(const QString &argsLine)
if (name.isEmpty())
{
emit errTxtOut("err: Bookmark name (-name) not given.\n");
cacheTxt(ERR, "err: Bookmark name (-name) not given.\n");
}
else
{
@ -185,7 +186,7 @@ void DeleteBookmark::dataIn(const QString &argsLine)
if (!QFile::remove(path))
{
emit errTxtOut("err: Could not delete the bookmark, the file might not exist or you don't have write permissions.\n");
cacheTxt(ERR, "err: Could not delete the bookmark, the file might not exist or you don't have write permissions.\n");
}
}
}
@ -197,7 +198,7 @@ void SeeBookmark::dataIn(const QString &argsLine)
if (name.isEmpty())
{
emit errTxtOut("err: Bookmark name (-name) not given.\n");
cacheTxt(ERR, "err: Bookmark name (-name) not given.\n");
}
else
{
@ -209,12 +210,12 @@ void SeeBookmark::dataIn(const QString &argsLine)
{
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
emit mainTxtOut("address: " + doc.object().value("address").toString() + "\n");
emit mainTxtOut("port: " + QString::number(doc.object().value("port").toInt()) + "\n");
cacheTxt(TEXT, "address: " + doc.object().value("address").toString() + "\n");
cacheTxt(TEXT, "port: " + QString::number(doc.object().value("port").toInt()) + "\n");
}
else
{
emit errTxtOut("err: Could not open the requested bookmark file for reading, reason: " + file.errorString() + '\n');
cacheTxt(ERR, "err: Could not open the requested bookmark file for reading, reason: " + file.errorString() + '\n');
}
file.close();

View File

@ -21,10 +21,7 @@ Command::Command(QObject *parent) : QObject(parent)
// the QObject::objectName property determines the command
// name for any object inheriting this object. avoid using
// spaces and keep in mind that all command names are case
// insensitive. avoid starting the command with the char
// defined in CMD_ESCAPE, this char is used to send
// commands directly to the host in case of naming
// conflicts with the host commands.
// insensitive.
setObjectName("do_nothing");
@ -32,6 +29,16 @@ Command::Command(QObject *parent) : QObject(parent)
connect(this, &Command::unsetUserIO, this, &Command::unsetHook);
}
void Command::cacheTxt(quint8 typeId, QString txt)
{
Shared::cacheTxt(Shared::TXT_IN, typeId, txt);
if (!(*Shared::activeDisp))
{
emit txtInCache();
}
}
void Command::setHook(int flgs)
{
if (flgs & LOCAL_HOOK)
@ -112,5 +119,5 @@ void Command::hookedCmdCall(const QString &argsLine)
void Command::postExec()
{
emit mainTxtOut("\nFinished: " + objectName() + "\n\n");
cacheTxt(TEXT, "\nFinished: " + objectName() + "\n\n");
}

View File

@ -31,6 +31,7 @@ private slots:
protected:
bool activeHook();
void cacheTxt(quint8 typeId, QString txt);
public:
@ -54,9 +55,6 @@ public slots:
signals:
void mainTxtOut(const QString &txt);
void errTxtOut(const QString &txt);
void bigTxtOut(const QString &txt);
void setUserIO(int flgs);
void unsetUserIO(int flgs);
void setMaxLines(int value);
@ -68,6 +66,7 @@ signals:
void disconnectHost();
void colorsChanged();
void fontChanged();
void txtInCache();
};
#endif // COMMAND_H

View File

@ -115,7 +115,7 @@ void Connect::dataIn(const QString &argsLine)
}
else
{
emit errTxtOut("err: Could not open the requested bookmark for reading, reason: " + file.errorString() + "\n");
cacheTxt(ERR, "err: Could not open the requested bookmark for reading, reason: " + file.errorString() + "\n");
}
file.close();
@ -123,15 +123,15 @@ void Connect::dataIn(const QString &argsLine)
if (Shared::hostAddress->isEmpty())
{
emit errTxtOut("err: Host address is empty.\n");
cacheTxt(ERR, "err: Host address is empty.\n");
}
else if (QHostAddress(*Shared::hostAddress).isNull())
{
emit errTxtOut("err: '" + *Shared::hostAddress + "' is not a valid address.\n");
cacheTxt(ERR, "err: '" + *Shared::hostAddress + "' is not a valid address.\n");
}
else if (*Shared::hostPort == 0)
{
emit errTxtOut("err: The host port cannot be 0.\n");
cacheTxt(ERR, "err: The host port cannot be 0.\n");
}
else
{
@ -160,6 +160,8 @@ void Term::dataIn(const QString &argsLine)
{
Q_UNUSED(argsLine)
Shared::cacheTxt(Shared::TXT_CLEAR);
emit termHostCmd();
}

View File

@ -39,7 +39,7 @@ void About::dispInfo(Command *cmdObj)
txtOut << "" << endl;
wordWrap("usage: ", txtOut, cmdObj->longText(), Shared::mainWidget);
emit mainTxtOut(txt);
cacheTxt(TEXT, txt);
}
bool About::dispClientCmd(const QString &cmdName)
@ -108,7 +108,7 @@ void About::dataIn(const QString &argsLine)
if (!dispInfo(cmdName))
{
emit errTxtOut("err: No such command: '" + cmdName + "'\n");
cacheTxt(ERR, "err: No such command: '" + cmdName + "'\n");
}
}
else
@ -136,7 +136,7 @@ void About::dataIn(const QString &argsLine)
txtOut << endl << endl << "for more detailed information about a command type: about <command>" << endl << endl;
emit mainTxtOut(txt);
cacheTxt(TEXT, txt);
}
}

View File

@ -66,5 +66,5 @@ void Status::dataIn(const QString &argsLine)
}
}
emit mainTxtOut(txt);
cacheTxt(TEXT, txt);
}

View File

@ -117,9 +117,9 @@ void SetColors::dataIn(const QString &argsLine)
QJsonObject obj = localData->value("text_settings").toObject();
emit mainTxtOut("text: " + obj.value("text_color").toString() + "\n");
emit mainTxtOut("error: " + obj.value("err_color").toString() + "\n");
emit mainTxtOut("bg: " + obj.value("bg_color").toString() + "\n");
cacheTxt(TEXT, "text: " + obj.value("text_color").toString() + "\n");
cacheTxt(TEXT, "error: " + obj.value("err_color").toString() + "\n");
cacheTxt(TEXT, "bg: " + obj.value("bg_color").toString() + "\n");
if (changed) emit colorsChanged();
}
@ -134,8 +134,8 @@ void SetFont::dataIn(const QString &argsLine)
QJsonObject obj = localData->value("text_settings").toObject();
emit mainTxtOut("family: " + obj.value("font_family").toString() + "\n");
emit mainTxtOut("size: " + obj.value("font_size").toString() + "\n");
cacheTxt(TEXT, "family: " + obj.value("font_family").toString() + "\n");
cacheTxt(TEXT, "size: " + obj.value("font_size").toString() + "\n");
if (changed) emit fontChanged();
}
@ -147,7 +147,7 @@ void SetMaxLines::dataIn(const QString &argsLine)
if (valStr.isEmpty())
{
emit mainTxtOut(QString::number(localData->value("max_lines").toInt()) + "\n");
cacheTxt(TEXT, QString::number(localData->value("max_lines").toInt()) + "\n");
}
else
{
@ -156,11 +156,11 @@ void SetMaxLines::dataIn(const QString &argsLine)
if (!ok)
{
emit errTxtOut("err: '" + valStr + "' is not a valid integer.\n");
cacheTxt(ERR, "err: '" + valStr + "' is not a valid integer.\n");
}
else if ((val < 50) || (val > 100000))
{
emit errTxtOut("err: The value must range 50 - 100000.\n");
cacheTxt(ERR, "err: The value must range 50 - 100000.\n");
}
else
{

View File

@ -383,6 +383,7 @@ bool argExists(const QString &key, const QStringList &args)
}
bool *Shared::connectedToHost = nullptr;
bool *Shared::activeDisp = nullptr;
QJsonObject *Shared::localData = nullptr;
QHash<QString, Command*> *Shared::clientCmds = nullptr;
QHash<quint16, QString> *Shared::hostCmds = nullptr;
@ -403,5 +404,75 @@ Genfile *Shared::genFile = nullptr;
MainWindow *Shared::mainWin = nullptr;
Session *Shared::session = nullptr;
TextBody *Shared::textBody = nullptr;
TextWorker *Shared::textWorker = nullptr;
ContextReloader *Shared::contextReloader = nullptr;
QWidget *Shared::mainWidget = nullptr;
QList<quint8> *Shared::idCache = nullptr;
QList<QString> *Shared::txtCache = nullptr;
ThreadKiller *Shared::theadKiller = nullptr;
bool Shared::cacheTxt(CacheOp op, quint8 &typeId, QString &txt)
{
QMutex mutex;
bool ret = false;
mutex.lock();
if (op == TXT_IN)
{
if ((typeId == TEXT) || (typeId == ERR) || (typeId == BIG_TEXT) || (typeId == PRIV_TEXT))
{
idCache->append(typeId);
txtCache->append(txt);
ret = true;
}
}
else if (op == TXT_OUT)
{
if (!idCache->isEmpty() && !txtCache->isEmpty())
{
typeId = idCache->takeFirst();
txt = txtCache->takeFirst();
ret = true;
}
}
else if (op == TXT_CLEAR)
{
*Shared::activeDisp = false;
idCache->clear();
txtCache->clear();
}
else if (op == TXT_IS_EMPTY)
{
ret = idCache->isEmpty() && txtCache->isEmpty();
}
mutex.unlock();
return ret;
}
bool Shared::cacheTxt(CacheOp op)
{
quint8 unusedId = 0;
QString unusedTxt;
return cacheTxt(op, unusedId, unusedTxt);
}
ThreadKiller::ThreadKiller(QObject *parent) : QObject(parent)
{
threadCount = 3;
}
void ThreadKiller::threadFinished()
{
threadCount--;
if (threadCount == 0)
{
QCoreApplication::instance()->quit();
}
}

View File

@ -62,18 +62,19 @@
#include <QFile>
#include <QRegExp>
#include <QFileInfo>
#include <QMutex>
#include "cmd_objs/long_txt.h"
#define DEFAULT_HIST_LIMIT 100
#define DEFAULT_MAX_LINES 5000
#define DEFAULT_MAX_LINES 1000
#define RDBUFF 128000
#define TXT_CODEC "UTF-16LE"
#define BOOKMARK_FOLDER "bookmarks"
#define CONFIG_FILENAME "config.json"
#define APP_NAME "Cmdr"
#define APP_TARGET "cmdr"
#define APP_VERSION "2.0.0"
#define APP_VERSION "2.1.0"
enum TypeID : quint8
{
@ -177,8 +178,10 @@ class Genfile;
class MainWindow;
class Session;
class TextBody;
class TextWorker;
class ContextReloader;
class HostDoc;
class ThreadKiller;
class Shared : public QObject
{
@ -187,6 +190,7 @@ class Shared : public QObject
public:
static bool *connectedToHost;
static bool *activeDisp;
static QByteArray *sessionId;
static ushort *servMajor;
static ushort *servMinor;
@ -200,6 +204,8 @@ public:
static QHash<quint16, QString> *hostCmds;
static QHash<quint16, QString> *genfileCmds;
static QHash<QString, Command*> *hostDocs;
static QList<quint8> *idCache;
static QList<QString> *txtCache;
static QJsonObject *localData;
static quint16 *termCmdId;
static CmdLine *cmdLine;
@ -207,8 +213,21 @@ public:
static MainWindow *mainWin;
static Session *session;
static TextBody *textBody;
static TextWorker *textWorker;
static ContextReloader *contextReloader;
static QWidget *mainWidget;
static ThreadKiller *theadKiller;
enum CacheOp
{
TXT_IN,
TXT_OUT,
TXT_CLEAR,
TXT_IS_EMPTY
};
static bool cacheTxt(CacheOp op);
static bool cacheTxt(CacheOp op, quint8 &typeId, QString &txt);
explicit Shared(QObject *parent = nullptr);
};
@ -226,5 +245,26 @@ public slots:
void reloadCmdLine();
};
class ThreadKiller : public QObject
{
Q_OBJECT
private:
int threadCount;
public:
explicit ThreadKiller(QObject *parent = nullptr);
public slots:
void threadFinished();
signals:
void quitThreads();
};
#endif // COMMON_H

View File

@ -16,16 +16,27 @@
// along with Cmdr under the LICENSE.md file. If not, see
// <http://www.gnu.org/licenses/>.
Genfile::Genfile(QObject *parent) : QObject(parent)
Genfile::Genfile(QObject *parent) : QObject(nullptr)
{
file = new QFile(this);
hook = 0;
finished();
connect(parent, &QObject::destroyed, this, &QObject::deleteLater);
connect(this, &Genfile::rdFileLoop, this, &Genfile::hookedDataIn);
}
void Genfile::cacheTxt(quint8 typeId, QString txt)
{
Shared::cacheTxt(Shared::TXT_IN, typeId, txt);
if (!(*Shared::activeDisp))
{
emit txtInCache();
}
}
void Genfile::finished()
{
flags = 0;
@ -42,6 +53,7 @@ void Genfile::finished()
file->close();
}
emit unsetUserIO(GEN_HOOK);
emit enableGenFile(false);
}
@ -50,7 +62,7 @@ void Genfile::askOverwrite()
flags |= CONFIRM_NEEDED;
emit setUserIO(GEN_HOOK);
emit mainTxtOut("About to overwrite file: '" + localFile + "' do you want to continue? (y/n): ");
emit cacheTxt(TEXT, "About to overwrite file: '" + localFile + "' do you want to continue? (y/n): ");
}
bool Genfile::seekToOffset()
@ -68,7 +80,7 @@ bool Genfile::seekToOffset()
if (!ok)
{
emit errTxtOut("err: Invalid offset was provided: " + offs + "\n");
cacheTxt(ERR, "err: Invalid offset was provided: " + offs + "\n");
}
ret = file->seek(pos);
@ -88,7 +100,7 @@ bool Genfile::lenOk()
}
else if (len.isEmpty())
{
emit errTxtOut("err: The -len parameter is empty.\n");
cacheTxt(ERR, "err: The -len parameter is empty.\n");
}
else
{
@ -113,20 +125,17 @@ void Genfile::setupForWriting()
if (!file->open(mode))
{
emit errTxtOut("err: Could not open the client file for writing. reason: " + file->errorString() + "\n");
cacheTxt(ERR, "err: Could not open the client file for writing. reason: " + file->errorString() + "\n");
finished();
}
else if (!seekToOffset())
{
emit errTxtOut("err: Could not seek to offset postion: " + offs + " of the client file.\n");
cacheTxt(ERR, "err: Could not seek to offset postion: " + offs + " of the client file.\n");
finished();
}
else if (!lenOk())
{
emit errTxtOut("err: The -len parameter (" + len + ") is invalid.\n");
cacheTxt(ERR, "err: The -len parameter (" + len + ") is invalid.\n");
finished();
}
else
@ -143,20 +152,17 @@ void Genfile::setupForReading()
if (!file->open(QFile::ReadOnly))
{
emit errTxtOut("err: Could not open the client file for reading. reason: " + file->errorString() + "\n");
cacheTxt(ERR, "err: Could not open the client file for reading. reason: " + file->errorString() + "\n");
finished();
}
else if (!seekToOffset())
{
emit errTxtOut("err: Could not seek to offset postion: " + offs + " of the client file.\n");
cacheTxt(ERR, "err: Could not seek to offset postion: " + offs + " of the client file.\n");
finished();
}
else if (!lenOk())
{
emit errTxtOut("err: The -len parameter (" + len + ") is invalid.\n");
cacheTxt(ERR, "err: The -len parameter (" + len + ") is invalid.\n");
finished();
}
}
@ -187,7 +193,7 @@ bool Genfile::wrToFile(const QByteArray &data)
}
else
{
emit errTxtOut("err: Client write failure - " + file->errorString() + "\n");
cacheTxt(ERR, "err: Client write failure - " + file->errorString() + "\n");
}
return ret;
@ -231,7 +237,7 @@ bool Genfile::rdFromFile()
}
else
{
emit errTxtOut("err: Client read failure - " + file->errorString() + "\n");
cacheTxt(ERR, "err: Client read failure - " + file->errorString() + "\n");
}
return ret;
@ -322,8 +328,7 @@ void Genfile::dataIn(quint16 cmdId, const QByteArray &data)
}
else
{
emit errTxtOut("err: The host did not return -to_host or -from_host, making this command call ambiguous.\n");
cacheTxt(ERR, "err: The host did not return -to_host or -from_host, making this command call ambiguous.\n");
finished();
}
}

View File

@ -54,6 +54,7 @@ private:
void askOverwrite();
void setupForWriting();
void setupForReading();
void cacheTxt(quint8 typeId, QString txt);
QByteArray autoFill(const QByteArray &data);
qint64 getRdBuff();
@ -69,9 +70,8 @@ public:
signals:
void txtInCache();
void enableGenFile(bool state);
void mainTxtOut(const QString &txt);
void errTxtOut(const QString &txt);
void setUserIO(int flgs);
void unsetUserIO(int flgs);
void dataOut(quint16 cmdId, const QByteArray &data, uchar typeID = GEN_FILE);

View File

@ -37,6 +37,7 @@ void setupClientCmds();
void setupCmdLine();
void setupGenFile();
void setupSession();
void setupText();
int main(int argc, char *argv[])
{
@ -46,6 +47,7 @@ int main(int argc, char *argv[])
app.setApplicationVersion(APP_VERSION);
bool connected = false;
bool activeDisp = false;
ushort servMajor = 0;
ushort servMinor = 0;
ushort servPatch = 0;
@ -59,6 +61,8 @@ int main(int argc, char *argv[])
QHash<QString, Command*> clientCmds;
QHash<QString, Command*> hostDocs;
QHash<quint16, QString> hostCmds;
QList<quint8> idCache;
QList<QString> txtCache;
QJsonObject localData;
loadLocalData(&localData);
@ -79,9 +83,14 @@ int main(int argc, char *argv[])
Shared::termCmdId = &termCmdId;
Shared::localData = &localData;
Shared::clientHookedCmd = &clientHookCmd;
Shared::idCache = &idCache;
Shared::txtCache = &txtCache;
Shared::activeDisp = &activeDisp;
Shared::contextReloader = new ContextReloader(&app);
Shared::theadKiller = new ThreadKiller(&app);
Shared::session = new Session(&app);
Shared::mainWin = new MainWindow();
Shared::textWorker = new TextWorker(&app);
Shared::textBody = new TextBody(Shared::mainWin);
Shared::cmdLine = new CmdLine(Shared::mainWin);
Shared::genFile = new Genfile(&app);
@ -109,6 +118,7 @@ int main(int argc, char *argv[])
setupCmdLine();
setupGenFile();
setupSession();
setupText();
Shared::mainWin->setTextBody(Shared::textBody);
Shared::mainWin->setCmdLine(Shared::cmdLine);
@ -138,18 +148,21 @@ void ContextReloader::reloadCmdLine()
void setupClientCmds()
{
Session *session = Shared::session;
CmdLine *cmdLine = Shared::cmdLine;
TextBody *textBody = Shared::textBody;
Genfile *genFile = Shared::genFile;
MainWindow *mainWin = Shared::mainWin;
ContextReloader *reloader = Shared::contextReloader;
Session *session = Shared::session;
CmdLine *cmdLine = Shared::cmdLine;
TextWorker *textWorker = Shared::textWorker;
TextBody *textBody = Shared::textBody;
Genfile *genFile = Shared::genFile;
MainWindow *mainWin = Shared::mainWin;
ContextReloader *reloader = Shared::contextReloader;
ThreadKiller *killer = Shared::theadKiller;
QObject::connect(mainWin, &MainWindow::closeApp, killer, &ThreadKiller::quitThreads);
for (auto command : Shared::clientCmds->values())
{
QObject::connect(command, &Command::mainTxtOut, textBody, &TextBody::addMainTxt);
QObject::connect(command, &Command::errTxtOut, textBody, &TextBody::addErrTxt);
QObject::connect(command, &Command::bigTxtOut, textBody, &TextBody::addBigTxt);
QObject::connect(command, &Command::colorsChanged, textWorker, &TextWorker::loadSettings);
QObject::connect(command, &Command::txtInCache, textWorker, &TextWorker::dumpTxtCache);
QObject::connect(cmdLine, &CmdLine::dataToCommandObj, command, &Command::cmdCall);
QObject::connect(cmdLine, &CmdLine::dataToHookedCmdObj, command, &Command::hookedCmdCall);
@ -164,7 +177,7 @@ void setupClientCmds()
QObject::connect(command, &Command::quitApp, session, &Session::disconnectFromServ);
QObject::connect(command, &Command::disconnectHost, session, &Session::disconnectFromServ);
QObject::connect(command, &Command::quitApp, QCoreApplication::instance(), &QCoreApplication::quit);
QObject::connect(command, &Command::quitApp, killer, &ThreadKiller::quitThreads);
QObject::connect(command, &Command::termHostCmd, genFile, &Genfile::finished);
QObject::connect(command, &Command::quitApp, genFile, &Genfile::finished);
@ -182,27 +195,28 @@ void setupClientCmds()
void setupCmdLine()
{
Session *session = Shared::session;
CmdLine *cmdLine = Shared::cmdLine;
TextBody *textBody = Shared::textBody;
Genfile *genFile = Shared::genFile;
Session *session = Shared::session;
CmdLine *cmdLine = Shared::cmdLine;
TextWorker *textWorker = Shared::textWorker;
Genfile *genFile = Shared::genFile;
QObject::connect(cmdLine, &CmdLine::dataToHost, session, &Session::binToServer);
QObject::connect(cmdLine, &CmdLine::dataToHookedHost, session, &Session::hookedBinToServer);
QObject::connect(cmdLine, &CmdLine::mainTxtOut, textBody, &TextBody::addMainTxt);
QObject::connect(cmdLine, &CmdLine::errTxtOut, textBody, &TextBody::addErrTxt);
QObject::connect(cmdLine, &CmdLine::dataToGenFile, genFile, &Genfile::dataIn);
QObject::connect(cmdLine, &CmdLine::dataToHookedGenFile, genFile, &Genfile::hookedDataIn);
QObject::connect(cmdLine, &CmdLine::txtInCache, textWorker, &TextWorker::dumpTxtCache);
}
void setupGenFile()
{
Session *session = Shared::session;
CmdLine *cmdLine = Shared::cmdLine;
TextBody *textBody = Shared::textBody;
Genfile *genFile = Shared::genFile;
Session *session = Shared::session;
CmdLine *cmdLine = Shared::cmdLine;
TextWorker *textWorker = Shared::textWorker;
Genfile *genFile = Shared::genFile;
ThreadKiller *killer = Shared::theadKiller;
QThread *genThr = new QThread(QCoreApplication::instance());
QObject::connect(genFile, &Genfile::dataOut, session, &Session::binToServer);
QObject::connect(genFile, &Genfile::hookedDataOut, session, &Session::hookedBinToServer);
@ -211,19 +225,23 @@ void setupGenFile()
QObject::connect(genFile, &Genfile::setUserIO, cmdLine, &CmdLine::setFlags);
QObject::connect(genFile, &Genfile::unsetUserIO, cmdLine, &CmdLine::unsetFlags);
QObject::connect(genFile, &Genfile::mainTxtOut, textBody, &TextBody::addMainTxt);
QObject::connect(genFile, &Genfile::errTxtOut, textBody, &TextBody::addErrTxt);
QObject::connect(genFile, &Genfile::txtInCache, textWorker, &TextWorker::dumpTxtCache);
QObject::connect(genThr, &QThread::finished, killer, &ThreadKiller::threadFinished);
QObject::connect(killer, &ThreadKiller::quitThreads, genThr, &QThread::quit);
genFile->moveToThread(genThr);
genThr->start();
}
void setupSession()
{
Session *session = Shared::session;
CmdLine *cmdLine = Shared::cmdLine;
TextBody *textBody = Shared::textBody;
Genfile *genFile = Shared::genFile;
QThread *sesThr = new QThread(nullptr);
QObject::connect(session, &Session::destroyed, sesThr, &QThread::quit);
Session *session = Shared::session;
CmdLine *cmdLine = Shared::cmdLine;
TextWorker *textWorker = Shared::textWorker;
Genfile *genFile = Shared::genFile;
ThreadKiller *killer = Shared::theadKiller;
QThread *sesThr = new QThread(QCoreApplication::instance());
QObject::connect(session, &Session::hostFinished, genFile, &Genfile::finished);
QObject::connect(session, &Session::toGenFile, genFile, &Genfile::hookedDataIn);
@ -231,12 +249,31 @@ void setupSession()
QObject::connect(session, &Session::setUserIO, cmdLine, &CmdLine::setFlags);
QObject::connect(session, &Session::unsetUserIO, cmdLine, &CmdLine::unsetFlags);
QObject::connect(session, &Session::bigTxtOut, textBody, &TextBody::addBigTxt);
QObject::connect(session, &Session::mainTxtOut, textBody, &TextBody::addMainTxt);
QObject::connect(session, &Session::errTxtOut, textBody, &TextBody::addErrTxt);
QObject::connect(session, &Session::txtInCache, textWorker, &TextWorker::dumpTxtCache);
QObject::connect(sesThr, &QThread::finished, sesThr, &QThread::deleteLater);
QObject::connect(sesThr, &QThread::finished, killer, &ThreadKiller::threadFinished);
QObject::connect(killer, &ThreadKiller::quitThreads, sesThr, &QThread::quit);
session->moveToThread(sesThr);
sesThr->start();
}
void setupText()
{
TextWorker *textWorker = Shared::textWorker;
CmdLine *cmdLine = Shared::cmdLine;
TextBody *textBody = Shared::textBody;
ThreadKiller *killer = Shared::theadKiller;
QThread *txtThr = new QThread(QCoreApplication::instance());
QObject::connect(textWorker, &TextWorker::setUserIO, cmdLine, &CmdLine::setFlags);
QObject::connect(textWorker, &TextWorker::unsetUserIO, cmdLine, &CmdLine::unsetFlags);
QObject::connect(textWorker, &TextWorker::htmlOut, textBody, &TextBody::htmlIn);
QObject::connect(txtThr, &QThread::finished, killer, &ThreadKiller::threadFinished);
QObject::connect(killer, &ThreadKiller::quitThreads, txtThr, &QThread::quit);
textWorker->moveToThread(txtThr);
txtThr->start();
}

View File

@ -75,6 +75,13 @@ void MainWindow::resizeEvent(QResizeEvent *event)
QMainWindow::resizeEvent(event);
}
void MainWindow::closeEvent(QCloseEvent *event)
{
emit closeApp();
event->ignore();
}
void MainWindow::setCmdLine(QWidget *widget)
{
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);

View File

@ -29,6 +29,7 @@ private:
QVBoxLayout *mainLayout;
void resizeEvent(QResizeEvent *event);
void closeEvent(QCloseEvent *event);
void saveWindowSize();
public:
@ -42,6 +43,7 @@ public:
signals:
void startup();
void closeApp();
};
#endif // USER_IO_H

View File

@ -30,6 +30,7 @@ Session::Session(QObject *parent) : QSslSocket(nullptr)
connect(this, &Session::connected, this, &Session::isConnected);
connect(this, &Session::disconnected, this, &Session::isDisconnected);
connect(this, &Session::readyRead, this, &Session::dataIn);
connect(this, &Session::loopDataIn, this, &Session::dataIn);
connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sockerr(QAbstractSocket::SocketError)));
}
@ -42,7 +43,7 @@ void Session::hookedBinToServer(const QByteArray &data, quint8 typeId)
}
else
{
emit errTxtOut("err: not connected to a host.\n");
cacheTxt(ERR, "err: not connected to a host.\n");
}
}
@ -58,7 +59,7 @@ void Session::binToServer(quint16 cmdId, const QByteArray &data, quint8 typeId)
}
else
{
emit errTxtOut("err: not connected to a host.\n");
cacheTxt(ERR, "err: not connected to a host.\n");
}
}
@ -66,11 +67,11 @@ void Session::sockerr(QAbstractSocket::SocketError err)
{
if (err == QAbstractSocket::RemoteHostClosedError)
{
emit mainTxtOut("\nThe remote host closed the session.\n");
cacheTxt(TEXT, "\nThe remote host closed the session.\n");
}
else
{
emit errTxtOut("\nerr: " + errorString() + "\n");
cacheTxt(ERR, "\nerr: " + errorString() + "\n");
if (state() == QAbstractSocket::UnconnectedState)
{
@ -87,7 +88,7 @@ void Session::isConnected()
// appName = UTF16LE string (padded with 0x00)
// coName = UTF16LE string (padded with 0x00)
emit mainTxtOut("Connected.\n");
cacheTxt(TEXT, "Connected.\n");
QByteArray header;
QString appName = QString(APP_NAME) + " v" + QString(APP_VERSION);
@ -102,8 +103,7 @@ void Session::isConnected()
}
else
{
emit errTxtOut("\nerr: client bug! - header len not equal to " + QString::number(CLIENT_HEADER_LEN) + "\n");
cacheTxt(ERR, "\nerr: client bug! - header len not equal to " + QString::number(CLIENT_HEADER_LEN) + "\n");
disconnectFromHost();
}
}
@ -123,7 +123,7 @@ void Session::handShakeDone()
<< " #key_exchange - " << cipher.keyExchangeMethod() << endl
<< " #authentication - " << cipher.authenticationMethod() << endl;
emit mainTxtOut(txt);
cacheTxt(TEXT, txt);
}
void Session::isDisconnected()
@ -142,7 +142,7 @@ void Session::isDisconnected()
Shared::hostCmds->clear();
Shared::genfileCmds->clear();
emit mainTxtOut("\nHost session ended. (disconnected)\n\n");
cacheTxt(TEXT, "\nHost session ended. (disconnected)\n\n");
if (reconnect)
{
@ -162,8 +162,7 @@ void Session::connectToServ()
}
else
{
emit mainTxtOut("Connecting to address: " + *Shared::hostAddress + " port: " + QString::number(*Shared::hostPort) + "\n");
cacheTxt(TEXT, "Connecting to address: " + *Shared::hostAddress + " port: " + QString::number(*Shared::hostPort) + "\n");
connectToHost(*Shared::hostAddress, *Shared::hostPort);
}
}
@ -174,8 +173,7 @@ void Session::disconnectFromServ()
if (state() == QAbstractSocket::ConnectedState)
{
emit mainTxtOut("Disconnecting.\n");
cacheTxt(TEXT, "Disconnecting.\n");
disconnectFromHost();
}
@ -225,22 +223,14 @@ bool Session::isAsync(quint16 id)
void Session::procAsync(const QByteArray &data)
{
if (dType == TEXT)
if ((dType == TEXT) || (dType == BIG_TEXT) || (dType == ERR))
{
cacheTxt(dType, fromTEXT(data));
if (cmdId == ASYNC_RDY)
{
hook = 0;
}
emit mainTxtOut(fromTEXT(data));
}
else if (dType == BIG_TEXT)
{
emit bigTxtOut(fromTEXT(data));
}
else if (dType == ERR)
{
emit errTxtOut(fromTEXT(data));
}
else if (cmdId == ASYNC_SYS_MSG)
{
@ -249,11 +239,11 @@ void Session::procAsync(const QByteArray &data)
QSslCertificate cert(data, QSsl::Pem);
QSslError selfSigned(QSslError::SelfSignedCertificate, cert);
emit mainTxtOut("SSL cert received.\n\n");
cacheTxt(TEXT, "SSL cert received.\n\n");
if (cert.isSelfSigned())
{
emit mainTxtOut("WARNING: the cert is self signed. be careful if in a public network.\n\n");
cacheTxt(TEXT, "WARNING: the host cert is self signed.\n\n");
}
ignoreSslErrors(QList<QSslError>() << selfSigned);
@ -291,6 +281,16 @@ void Session::procAsync(const QByteArray &data)
}
}
void Session::cacheTxt(quint8 typeId, QString txt)
{
Shared::cacheTxt(Shared::TXT_IN, typeId, txt);
if (!(*Shared::activeDisp))
{
emit txtInCache();
}
}
void Session::dataFromHost(const QByteArray &data)
{
if (isAsync(cmdId))
@ -299,22 +299,9 @@ void Session::dataFromHost(const QByteArray &data)
}
else if ((cmdId == hook) && (branId == 0) && (hook != 0))
{
if ((dType == TEXT) || (dType == PRIV_TEXT))
if ((dType == TEXT) || (dType == PRIV_TEXT) || (dType == BIG_TEXT) || (dType == ERR))
{
emit mainTxtOut(fromTEXT(data));
if (dType == PRIV_TEXT)
{
emit setUserIO(HIDDEN);
}
}
else if (dType == BIG_TEXT)
{
emit bigTxtOut(fromTEXT(data));
}
else if (dType == ERR)
{
emit errTxtOut(fromTEXT(data));
cacheTxt(dType, fromTEXT(data));
}
else if (dType == IDLE)
{
@ -325,11 +312,11 @@ void Session::dataFromHost(const QByteArray &data)
if (Shared::hostCmds->contains(cmdId))
{
emit mainTxtOut("\nFinished: " + Shared::hostCmds->value(cmdId) + "\n\n");
cacheTxt(TEXT, "\nFinished: " + Shared::hostCmds->value(cmdId) + "\n\n");
}
else
{
emit mainTxtOut("\nFinished: Unknown\n\n");
cacheTxt(TEXT, "\nFinished: Unknown\n\n");
}
}
else if ((dType == GEN_FILE) && (flags & GEN_FILE_ON))
@ -348,7 +335,8 @@ void Session::dataIn()
flags ^= DSIZE_RDY;
dataFromHost(read(dSize));
dataIn();
emit loopDataIn();
}
}
else if (flags & VER_OK)
@ -363,7 +351,7 @@ void Session::dataIn()
dSize = static_cast<quint32>(rdInt(header.mid(5, 3)));
flags |= DSIZE_RDY;
dataIn();
emit loopDataIn();
}
}
else if (bytesAvailable() >= SERVER_HEADER_LEN)
@ -386,7 +374,7 @@ void Session::dataIn()
*Shared::servMinor = static_cast<quint16>(rdInt(read(2)));
*Shared::servPatch = static_cast<quint16>(rdInt(read(2)));
emit mainTxtOut("Detected host version: " + verText(*Shared::servMajor, *Shared::servMinor, *Shared::servPatch) + "\n");
cacheTxt(TEXT, "Detected host version: " + verText(*Shared::servMajor, *Shared::servMinor, *Shared::servPatch) + "\n");
if (*Shared::servMajor == 2)
{
@ -395,35 +383,34 @@ void Session::dataIn()
flags |= VER_OK;
emit mainTxtOut("Host assigned session id: " + Shared::sessionId->toHex() + "\n");
cacheTxt(TEXT, "Host assigned session id: " + Shared::sessionId->toHex() + "\n");
if (reply == 2)
{
emit mainTxtOut("Awaiting SSL cert from the host.\n");
cacheTxt(TEXT, "Awaiting SSL cert from the host.\n");
}
else
{
emit mainTxtOut("The host claims SSL is not needed. the connection will not be encrypted.\n");
cacheTxt(TEXT, "The host claims SSL is not needed. the connection will not be encrypted.\n");
}
}
else
{
emit errTxtOut("err: Host not compatible.\n");
cacheTxt(ERR, "err: Host not compatible.\n");
disconnectFromHost();
}
}
else if (reply == 4)
{
emit errTxtOut("err: The host was unable to find a SSL cert for common name: " + *Shared::hostAddress + ".\n");
cacheTxt(ERR, "err: The host was unable to find a SSL cert for common name: " + *Shared::hostAddress + ".\n");
}
else
{
emit errTxtOut("err: Invalid reply id from the host: " + QString::number(reply) + ".\n");
cacheTxt(ERR, "err: Invalid reply id from the host: " + QString::number(reply) + ".\n");
}
if ((reply != 1) && (reply != 2)) disconnectFromHost();
dataIn();
emit loopDataIn();
}
}

View File

@ -58,6 +58,7 @@ private:
quint8 dType;
bool reconnect;
void cacheTxt(quint8 typeId, QString txt);
void dataFromHost(const QByteArray &data);
void procAsync(const QByteArray &data);
bool isAsync(quint16 id);
@ -95,13 +96,12 @@ public slots:
signals:
void hostFinished();
void txtInCache();
void loopDataIn();
void setUserIO(int flgs);
void unsetUserIO(int flgs);
void hostCmdRemoved(quint16 id);
void toGenFile(const QByteArray &data);
void mainTxtOut(const QString &txt);
void errTxtOut(const QString &txt);
void bigTxtOut(const QString &txt);
};
#endif // SOCKET_H

View File

@ -16,6 +16,97 @@
// along with Cmdr under the LICENSE.md file. If not, see
// <http://www.gnu.org/licenses/>.
TextWorker::TextWorker(QObject *parent) : QObject(nullptr)
{
localData = Shared::localData;
connect(parent, &QObject::destroyed, this, &QObject::deleteLater);
connect(this, &TextWorker::loop, this, &TextWorker::dumpTxtCache);
loadSettings();
}
QString TextWorker::htmlEsc(const QString &txt)
{
QString ret = txt;
ret.replace(">", "&gt;");
ret.replace("<", "&lt;");
return ret;
}
void TextWorker::dumpTxtCache()
{
QString txt;
quint8 typeId;
if (Shared::cacheTxt(Shared::TXT_OUT, typeId, txt))
{
*Shared::activeDisp = !Shared::cacheTxt(Shared::TXT_IS_EMPTY);
if ((typeId == TEXT) || (typeId == PRIV_TEXT))
{
addMainTxt(txt);
if (typeId == PRIV_TEXT)
{
emit setUserIO(HIDDEN);
}
}
else if (typeId == ERR)
{
addErrTxt(txt);
}
else if (typeId == BIG_TEXT)
{
addBigTxt(txt);
}
if (*Shared::activeDisp)
{
emit loop();
}
}
}
void TextWorker::toHtmlLines(const QString &txt, const QString &color)
{
QStringList lines = htmlEsc(txt).split("\n");
for (int i = 0; i < lines.size(); ++i)
{
emit htmlOut("<div style=\"white-space: pre; color:" + color + ";\">" + lines[i] + "</div>", i == lines.size() - 1);
thread()->usleep(1000);
}
}
void TextWorker::addMainTxt(const QString &txt)
{
toHtmlLines(txt, mainColor);
}
void TextWorker::addErrTxt(const QString &txt)
{
toHtmlLines(txt, errColor);
}
void TextWorker::addBigTxt(const QString &txt)
{
QString lines;
QTextStream txtOut(&lines);
wordWrap("", txtOut, txt, Shared::mainWidget);
addMainTxt(lines);
}
void TextWorker::loadSettings()
{
mainColor = localData->value("text_settings").toObject().value("text_color").toString();
errColor = localData->value("text_settings").toObject().value("err_color").toString();
}
TextBody::TextBody(QWidget *parent) : QTextEdit(parent)
{
localData = Shared::localData;
@ -31,66 +122,14 @@ TextBody::TextBody(QWidget *parent) : QTextEdit(parent)
void TextBody::loadTextBodySettings()
{
mainColor = localData->value("text_settings").toObject().value("text_color").toString();
errColor = localData->value("text_settings").toObject().value("err_color").toString();
txtDocument->setMaximumBlockCount(localData->value("max_lines").toInt());
}
QString TextBody::htmlEsc(const QString &txt)
{
QString ret = txt;
ret.replace(">", "&gt;");
ret.replace("<", "&lt;");
return ret;
}
void TextBody::addTextBlock(const QString &txt, const QString &color)
{
moveCursor(QTextCursor::End);
QStringList lines = htmlEsc(txt).split("\n");
for (int i = 0; i < lines.size(); ++i)
{
txtCursor->insertHtml("<div style=\"white-space: pre; color:" + color + ";\">" + lines[i] + "</div>");
if (i != lines.size() - 1)
{
txtCursor->insertBlock();
}
}
ensureCursorVisible();
}
void TextBody::setMaxLines(int value)
{
txtDocument->setMaximumBlockCount(value);
}
void TextBody::addMainTxt(const QString &txt)
{
addTextBlock(txt, mainColor);
}
void TextBody::addErrTxt(const QString &txt)
{
addTextBlock(txt, errColor);
}
void TextBody::addBigTxt(const QString &txt)
{
QString lines;
QTextStream txtOut(&lines);
wordWrap("", txtOut, txt, Shared::mainWidget);
addMainTxt(lines);
}
void TextBody::reload()
{
clear();
@ -108,3 +147,17 @@ void TextBody::contextMenuEvent(QContextMenuEvent *event)
menu->exec(event->globalPos());
menu->deleteLater();
}
void TextBody::htmlIn(const QString &line, bool lastLine)
{
moveCursor(QTextCursor::End);
txtCursor->insertHtml(line);
if (!lastLine)
{
txtCursor->insertBlock();
}
ensureCursorVisible();
}

View File

@ -19,28 +19,53 @@
#include "common.h"
class TextWorker : public QObject
{
Q_OBJECT
private:
QJsonObject *localData;
QString errColor;
QString mainColor;
QString htmlEsc(const QString &txt);
void toHtmlLines(const QString &txt, const QString &color);
void addMainTxt(const QString &txt);
void addErrTxt(const QString &txt);
void addBigTxt(const QString &txt);
public:
explicit TextWorker(QObject *parent = nullptr);
public slots:
void loadSettings();
void dumpTxtCache();
signals:
void htmlOut(const QString &line, bool lastLine);
void setUserIO(int flgs);
void unsetUserIO(int flgs);
void loop();
};
//-----------------------------------------
class TextBody : public QTextEdit
{
Q_OBJECT
private:
enum TextType
{
ERROR,
MAIN
};
QTextDocument *txtDocument;
QTextCursor *txtCursor;
QJsonObject *localData;
QString errColor;
QString mainColor;
QString htmlEsc(const QString &txt);
void loadTextBodySettings();
void contextMenuEvent(QContextMenuEvent *event);
void addTextBlock(const QString &txt, const QString &color);
void loadTextBodySettings();
void contextMenuEvent(QContextMenuEvent *event);
public:
@ -48,9 +73,7 @@ public:
public slots:
void addMainTxt(const QString &txt);
void addErrTxt(const QString &txt);
void addBigTxt(const QString &txt);
void htmlIn(const QString &line, bool lastLine);
void setMaxLines(int value);
void reload();
};