Versioning system change and support of new MRCI host features

added support for the new PROMPT_TEXT data type id. it is simply treated
like any other TEXT type for this application.

added a hiddden progress bar below the main text body that will only kick
in when it detects a string of the PROG/PROG_LAST data types.

refactored "halt" to "yield" on the client's internal command. also
refactored "text_settings" to "theme" on the json config file. the config
file name will now also include the major version number as a way to
verify compatibility as the client is updated.

changed the client's versioning system to a 2 number system. more info
about this new system was edited into the README.md file.

synced the type_id and async_id values with MRCI host.

added support for the new MRCI host 4 number versioning system. this
client will only care about the tcp_rev part of this new system for now.
any changes to the hosts' core commands shouldn't affect compatibility
with this client.
This commit is contained in:
Maurice ONeal 2020-03-10 19:04:48 -04:00
parent 34753488ff
commit 3f39ba8767
15 changed files with 374 additions and 228 deletions

View File

@ -1,6 +1,6 @@
# Cmdr # # Cmdr #
Cmdr is a command line terminal emulator client for MRCI host using text input/output. This helps administer MRCI host via local or remote TCP connections encrypted with TLS/SSL using the MRCI protocol. It also supports file transfers to/from the client using the GEN_FILE sub-protocol that MRCI understands. Cmdr is a command line terminal emulator client for MRCI host using text input/output. This help administer MRCI host via local or remote TCP connections encrypted with TLS/SSL using the MRCI protocol. It also supports file transfers to/from the client using the GEN_FILE sub-protocol that MRCI understands.
### Usage ### ### Usage ###
@ -8,13 +8,12 @@ Cmdr have it's own terminal display so there is no command line switches to pass
### Versioning System ### ### Versioning System ###
This application uses the typical 3 number versioning system: [major].[minor].[patch] This application uses a 2 number versioning system: [major].[minor]
* Major - this indicates any major changes to the application or changes that render user data of different majors incompatible. * Major - this indicates any changes that cause old configuration or library files to be incompatible.
* Minor - this indicates changes to the code that still maintains compatibility with existing user data. * Minor - this indicates changes to the code that still maintains compatibility with existing config files or libraries.
* Patch - this indicates changes that won't require any behaviour changes at all.
Any increments to major resets minor and patch to 0. Any increments to major resets minor to 0.
### The Protocol ### ### The Protocol ###
@ -58,20 +57,21 @@ notes:
* The **appName** is the name of the client application that is connected to the host. It can also contain the client's app version if needed because it doesn't follow any particular standard. * The **appName** is the name of the client application that is connected to the host. It can also contain the client's app version if needed because it doesn't follow any particular standard.
* The **coName** is the common name of a SSL certificate that is currently installed in the host. Depending on how the host is configured, it can contain more than one installed SSL cert so coName can be used by clients as a way to request which one of the SSL certs to use during the SSL handshake. * The **coName** is the common name of a SSL certificate that is currently installed in the host. Depending on how the host is configured, it can contain more than one installed SSL cert so coName can be used by clients as a way to request which one of the SSL certs to use during the SSL handshake. If the client doesn't know which cert to request, it is good practice to use the address that was used to connect to the host.
### Host Header ### ### Host Header ###
``` ```
Format: Format:
[reply][major][minor][patch][sesId] [reply][major][minor][tcp_rev][mod_rev][sesId]
reply - 1byte - 8bit little endian unsigned int reply - 1byte - 8bit little endian unsigned int
major - 2bytes - 16bit little endian unsigned int major - 2bytes - 16bit little endian unsigned int
minor - 2bytes - 16bit little endian unsigned int minor - 2bytes - 16bit little endian unsigned int
patch - 2bytes - 16bit little endian unsigned int tcp_rev - 2bytes - 16bit little endian unsigned int
sesId - 28bytes - 224bit sha3 hash mod_rev - 2bytes - 16bit little endian unsigned int
sesId - 28bytes - 224bit sha3 hash
``` ```
notes: notes:
@ -82,7 +82,7 @@ notes:
* reply = 2, means the client is acceptable but the host will now send it's Pem formatted SSL cert data in a ```HOST_CERT``` mrci frame just after sending it's header. After receiving the cert, the client will then need to send a STARTTLS signal using this cert. * reply = 2, means the client is acceptable but the host will now send it's Pem formatted SSL cert data in a ```HOST_CERT``` mrci frame just after sending it's header. After receiving the cert, the client will then need to send a STARTTLS signal using this cert.
* reply = 4, means the host was unable to find the SSL cert associated with the common name sent by the client. The session will auto close at this point. * reply = 4, means the host was unable to find the SSL cert associated with the common name sent by the client. The session will auto close at this point.
* **major**, **minor**, **path** these 3 numeric values are the host version number that also use a 3 number versioning system. This can be used by the client to setup backwards compatibility or determine of if supports the host at all. If not supported, the client can simply disconnect form the host. * **major**, **minor**, **tcp_rev**, **mod_rev** these 4 numeric values are the host version number that uses a 4 number versioning system. This can be used by the client to setup backwards compatibility or determine of if supports the host at all. If not supported, the client can simply disconnect form the host and display an error to the user.
* **sesId** is the session id. It is a unique 224bit sha3 hash that can be used by the host and client to uniquely identify the current session or past sessions. * **sesId** is the session id. It is a unique 224bit sha3 hash that can be used by the host and client to uniquely identify the current session or past sessions.
@ -106,7 +106,7 @@ makeself
Linux_build.sh is a custom script designed to build this project from the source code using qmake, make and makeself. You can pass 2 optional arguments: Linux_build.sh is a custom script designed to build this project from the source code using qmake, make and makeself. You can pass 2 optional arguments:
1. The path to the QT bin folder in case you want to compile with a QT install not defined in PATH. 1. The path to the QT bin folder in case you want to compile with a QT install not defined in PATH.
2. Path of the output makeself file (usually has a .run extension). If not given, the outfile will be named cmdr-x.x.x.run in the source code folder. 2. Path of the output makeself file (usually has a .run extension). If not given, the outfile will be named cmdr-x.x.run in the source code folder.
Build: Build:
``` ```
@ -115,6 +115,6 @@ sh ./linux_build.sh
``` ```
Install: Install:
``` ```
chmod +x ./cmdr-x.x.x.run chmod +x ./cmdr-x.x.run
./cmdr-x.x.x.run ./cmdr-x.x.run
``` ```

View File

@ -2,40 +2,36 @@
An async command is a virtual command that the host can use to send data to the client at any time while connected to the host. As the name implies, the occurance of a client receiving data from an async command is not always the result of running a regular command in the current session. This can occur for example when information in your account is changed by another client connected to the host; your client would not know about this change until an async command is sent notify it of the change. These commands can be called directly or indirectly by a module and are considered "virtual" commands because there is no defined objects attached to them. Instead, async commands are best identified by command id values 1-255. An async command is a virtual command that the host can use to send data to the client at any time while connected to the host. As the name implies, the occurance of a client receiving data from an async command is not always the result of running a regular command in the current session. This can occur for example when information in your account is changed by another client connected to the host; your client would not know about this change until an async command is sent notify it of the change. These commands can be called directly or indirectly by a module and are considered "virtual" commands because there is no defined objects attached to them. Instead, async commands are best identified by command id values 1-255.
Async commands are not only used to send data to the client but also used internally within the host to help objects operating in different processes to communicate with each other. Some async commands in fact are considered internal only because the client should never see any data come from them at anytime. There is also data flow contriants for aysnc commands, meaning some gets blocked if sent from the module or has no effect if sent with the unexpected [IPC](type_ids.md) type id. The list below shows the various data flow contriants each of these async commands have. Async commands are not only used to send data to the client but also used internally within the host to help objects operating in different processes to communicate with each other. Some async commands in fact are considered internal only because the client should never see any data come from them at anytime. There is also data flow contriants for async commands, meaning some gets blocked if sent from the module or has no effect if sent with the unexpected [IPC](type_ids.md) type id. The list below shows the various data flow contriants each of these async commands have.
Here is a describtion of what the keywords in the list mean: Here is a describtion of what the keywords in the list mean:
``` ```
client - this means the async command id will be used to forward client - this means the async command id will be used to forward
data of any type to client if needed. data of any type to client if needed.
internal - this means the async command will be responded by the internal - this means the async command used by internal host
session object but the data will not be forwarded to the objects only so it never gets forwarded to a client at
client or converted to an entirely different async any time.
command before sending to the client.
public - this means the session objects will respond to this async public - this means the aysnc command will be broadcasted to all
command if sent with PUB_IPC or PUB_IPC_WITH_FEEDBACK session objects currently connected to the host. which
from the module. sessions will resond to it depends on the data being
sent. some async's broadcasted this way will also feed
back to source session depending on the data.
private - this means only the session object that has a direct IPC private - this means the async command will only be sent to the
connection with the module that sends the async command local session.
via PRIV_IPC will respond to it.
none - this means none of the session objects will respond to retricted - this means the async command will be used by the local
this async command no matter which of the IPC data types session object only. any modules/commands sending it
are used. it is resevered for just the session object to will be ignored or blocked.
send to the client.
retricted - this means the session object will actively block this
async command from being sent from the module (any mode).
``` ```
``` ```
enum AsyncCommands : quint16 enum AsyncCommands : quint16
{ {
ASYNC_RDY = 1, // client | none ASYNC_RDY = 1, // client | retricted
ASYNC_SYS_MSG = 2, // client | none ASYNC_SYS_MSG = 2, // client | retricted
ASYNC_EXIT = 3, // internal | private ASYNC_EXIT = 3, // internal | private
ASYNC_CAST = 4, // client | public ASYNC_CAST = 4, // client | public
ASYNC_MAXSES = 5, // internal | private ASYNC_MAXSES = 5, // internal | private
@ -49,7 +45,7 @@ enum AsyncCommands : quint16
ASYNC_DISABLE_MOD = 13, // internal | public ASYNC_DISABLE_MOD = 13, // internal | public
ASYNC_END_SESSION = 14, // internal | private ASYNC_END_SESSION = 14, // internal | private
ASYNC_USER_LOGIN = 15, // internal | private ASYNC_USER_LOGIN = 15, // internal | private
ASYNC_TO_PEER = 16, // client | public | retricted ASYNC_TO_PEER = 16, // client | retricted
ASYNC_LIMITED_CAST = 17, // client | public ASYNC_LIMITED_CAST = 17, // client | public
ASYNC_RW_MY_INFO = 18, // internal | public ASYNC_RW_MY_INFO = 18, // internal | public
ASYNC_P2P = 19, // client | public ASYNC_P2P = 19, // client | public
@ -68,16 +64,17 @@ enum AsyncCommands : quint16
ASYNC_SUB_CH_LEVEL_CHG = 32, // client | public ASYNC_SUB_CH_LEVEL_CHG = 32, // client | public
ASYNC_ADD_RDONLY = 33, // client | public ASYNC_ADD_RDONLY = 33, // client | public
ASYNC_RM_RDONLY = 34, // client | public ASYNC_RM_RDONLY = 34, // client | public
ASYNC_ADD_CMD = 35, // client | none ASYNC_ADD_CMD = 35, // client | retricted
ASYNC_RM_CMD = 36, // client | none ASYNC_RM_CMD = 36, // client | retricted
ASYNC_USER_RENAMED = 37, // internal | public ASYNC_USER_RENAMED = 37, // internal | public
ASYNC_PING_PEERS = 38, // internal | private ASYNC_PING_PEERS = 38, // internal | private
ASYNC_OPEN_SUBCH = 39, // internal | private ASYNC_OPEN_SUBCH = 39, // internal | private
ASYNC_CLOSE_SUBCH = 40, // internal | private ASYNC_CLOSE_SUBCH = 40, // internal | private
ASYNC_UPDATE_BANS = 41, // internal | private
ASYNC_KEEP_ALIVE = 42, // internal | private ASYNC_KEEP_ALIVE = 42, // internal | private
ASYNC_SET_DIR = 43, // internal | private ASYNC_SET_DIR = 43, // internal | private
ASYNC_DEBUG_TEXT = 44 // internal | private ASYNC_DEBUG_TEXT = 44, // internal | private
ASYNC_HOOK_INPUT = 45, // internal | private
ASYNC_UNHOOK = 46 // internal | private
}; };
``` ```
@ -141,7 +138,7 @@ This internal only async commmand doesn't carry any data. It is used by modules
This command carries a 32byte user id hash. This can be used by modules to tell the session object to login as this user. This command carries a 32byte user id hash. This can be used by modules to tell the session object to login as this user.
```ASYNC_TO_PEER (16)``` ```ASYNC_TO_PEER (16)```
This is an async command that carry an embedded data frame directly to/from peer sessions without any restrictions. It is prepended with the 224bit hash of the target session id; however, it drops that session id before arriving at the client so it will apppear as a regular mrci frame of any data type. Modules do not have direct access to this, the host internal objects will handle this. This is an async command that carry an embedded data frame directly to/from peer sessions without any restrictions. It is prepended with the 224bit hash of the target session id; however, it drops that session id before arriving at the client so it will apppear as a regular mrci frame of any data type. modules do not have direct access to this, the host internal objects will handle it.
``` ```
from_module: [28bytes(sessionId)][1byte(typeId)][rest-of-bytes(payload)] from_module: [28bytes(sessionId)][1byte(typeId)][rest-of-bytes(payload)]
to_client: [type_id][cmd_id(16)][branch_id(0)][size_of_payload][payload] to_client: [type_id][cmd_id(16)][branch_id(0)][size_of_payload][payload]
@ -289,18 +286,21 @@ format: [8bytes(64bit_ch_id)][1byte(8bit_sub_ch_id)]
```ASYNC_CLOSE_SUBCH (40)``` ```ASYNC_CLOSE_SUBCH (40)```
This is the other half to ASYNC_OPEN_SUBCH that tells the session to close the requested sub-channel. This is the other half to ASYNC_OPEN_SUBCH that tells the session to close the requested sub-channel.
```ASYNC_UPDATE_BANS (41)```
This internal only async command doesn't carry any data. It can be use by modules to tell the TCPServer object to update it's ip ban list cache from the database. This generally only needs to be used if the ip ban list in the database has changed in anyway.
```ASYNC_KEEP_ALIVE (42)``` ```ASYNC_KEEP_ALIVE (42)```
This internal only async command doesn't carry any data. The session object normally sends a [KILL_CMD](type_ids.md) to the module when it detects that the module process has not sent an IPC frame in 2 minutes to terminate the module process. If desired, the module can send this async command in regular intervals to reset this 2 minute idle timer to prevent auto termination. This internal only async command doesn't carry any data. The session object normally sends a [KILL_CMD](type_ids.md) to the module when it detects that the module process has not sent an IPC frame in 2 minutes to terminate the module process. If desired, the module can send this async command in regular intervals to reset this 2 minute idle timer to prevent auto termination.
```ASYNC_SET_DIR (43)``` ```ASYNC_SET_DIR (43)```
This internal only async command carries a [TEXT](type_ids.md) path that sets the working directory for the session object. 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. 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)``` ```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 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.
```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.
```ASYNC_UNHOOK (46)```
This async command doesn't carry any data. Any module command that sends it tells the local session to unhook the tcp input data from the client if there is an active hook. This doesn't need to come from the command that initiated the hook.
### 5.3 Open Sub-Channel List ### ### 5.3 Open Sub-Channel List ###
An open sub-channel list is a binary data structure that string togeather up to 6 channel-sub combinations that indicate which channel id and sub id combinations are currently open. Each sub-channel are 9bytes long and the list itself maintians a fixed length of 54bytes so it is padded with 0x00 chars to maintain the fixed length (this padding can appear anywhere in 9byte increments within the list). Each sub-channel is formatted like this: An open sub-channel list is a binary data structure that string togeather up to 6 channel-sub combinations that indicate which channel id and sub id combinations are currently open. Each sub-channel are 9bytes long and the list itself maintians a fixed length of 54bytes so it is padded with 0x00 chars to maintain the fixed length (this padding can appear anywhere in 9byte increments within the list). Each sub-channel is formatted like this:

View File

@ -5,35 +5,36 @@ All mrci frames transferred throughout this application have an 8bit numeric val
``` ```
enum TypeID : quint8 enum TypeID : quint8
{ {
GEN_FILE = 1, GEN_FILE = 1,
TEXT = 2, TEXT = 2,
ERR = 3, ERR = 3,
PRIV_TEXT = 4, PRIV_TEXT = 4,
IDLE = 5, IDLE = 5,
HOST_CERT = 6, HOST_CERT = 6,
FILE_INFO = 7, FILE_INFO = 7,
PEER_INFO = 8, PEER_INFO = 8,
MY_INFO = 9, MY_INFO = 9,
PEER_STAT = 10, PEER_STAT = 10,
P2P_REQUEST = 11, P2P_REQUEST = 11,
P2P_CLOSE = 12, P2P_CLOSE = 12,
P2P_OPEN = 13, P2P_OPEN = 13,
BYTES = 14, BYTES = 14,
SESSION_ID = 15, SESSION_ID = 15,
NEW_CMD = 16, NEW_CMD = 16,
CMD_ID = 17, CMD_ID = 17,
BIG_TEXT = 18, BIG_TEXT = 18,
TERM_CMD = 19, TERM_CMD = 19,
HOST_VER = 20, HOST_VER = 20,
PRIV_IPC = 21, PING_PEERS = 21,
PUB_IPC = 22, CH_MEMBER_INFO = 22,
PUB_IPC_WITH_FEEDBACK = 23, CH_ID = 23,
PING_PEERS = 24, KILL_CMD = 24,
CH_MEMBER_INFO = 25, YIELD_CMD = 25,
CH_ID = 26, RESUME_CMD = 26,
KILL_CMD = 27, PROMPT_TEXT = 27,
HALT_CMD = 28, PROG = 28,
RESUME_CMD = 29 PROG_LAST = 29,
ASYNC_PAYLOAD = 30
}; };
``` ```
@ -71,38 +72,65 @@ arguments:
This type id is similar to TEXT except it indicates that this is an error message that can be displayed directly to the user if needed. This type id is similar to TEXT except it indicates that this is an error message that can be displayed directly to the user if needed.
```PRIV_TEXT``` ```PRIV_TEXT```
This id can be treated exactly like TEXT except this should tell the client to hide or do not echo the next TEXT data that the host is expecting, like a password or other sensitive text data. This id can be treated exactly like TEXT except this tells the client that the command is prompting for private information from the user so it sould hide or do not echo the next TEXT data that the command is expecting, like a password or other sensitive data.
```PROMPT_TEXT```
This is similar to PRIV_TEXT expect it is not asking for private information. It is simply prompting for non-sensitive information from the user.
```BIG_TEXT``` ```BIG_TEXT```
Also formatted exactly like TEXT but this indicates to the client that this is a large body of text that is recommended to be word wrapped when displaying to the user. It can contain line breaks so clients are also recommended to honor those line breaks. Also formatted exactly like TEXT but this indicates to the client that this is a large body of text that is recommended to be word wrapped when displaying to the user. It can contain line breaks so clients are also recommended to honor those line breaks.
```IDLE``` ```IDLE```
This doesn't carry any actual data, instead this indicates that the command id and branch id that sent it has finished it's task. Modules that send this doesn't need to terminate it's process. All commands started during the session returns this type id when it has finished it's task. It carries a 16bit unsigned integer indicating the result of the task that the command was running.
```
enum RetCode : quint16
{
NO_ERRORS = 1, // task execution completed without any issues.
ABORTED = 2, // the task aborted via user or host intervention.
INVALID_PARAMS = 3, // invalid/missing parameters prevented the task from executing.
CRASH = 4, // the command process has crashed.
FAILED_TO_START = 5, // the command process could not start.
EXECUTION_FAIL = 6, // command specific error prevented execution of the task.
CUSTOM = 7 // indicates a custom return code.
};
notes:
1. the custom return code can be additional data added to the end of the 16bit
integer that can carry additional data about the result of the task. it can
be any format that the module itself decides it can should be. nothing is
stopping modules from defining return codes beyond the value of 7 but it is
advised not to because this enum might be expanded in the future.
```
```TERM_CMD```
This type id doesn't carry any actual data. It is used to tell the host to stop/terminate the command id and branch id that was used to send it. It does not actually terminate the module's process within the host, it only simply tells it to stop what it is currently doing. This will also terminate any commands in a prompt/more input state.
```KILL_CMD``` ```KILL_CMD```
This doesn't carry any actual data, instead can be sent by the client or session object to tell the command-branch id sent in the frame to terminate the module process. Modules that receive this need to send a IDLE frame if a command is still running and then terminate itself. The module will have 3 seconds to do this before it is force killed by the session. This works similarly to TERM_CMD except it will also terminate the module process. The module process will have 3 seconds to shutdown gracefully before it is force killed by the host session.
```HALT_CMD``` ```YIELD_CMD```
This doesn't carry any actual data, instead can be sent by the client or session object to tell the command-branch id sent in the frame to pause/halt the current task that the command is currently running. All modules are not obligated to support this feature but highly recommended. This type id doesn't carry any actual data, instead can be used to tell the host to pause/yield the command id and branch id that was used to send it. All modules are not obligated to support this feature but it is highly recommended to do so.
```RESUME_CMD``` ```RESUME_CMD```
This is the other half of HALT_CMD that tells the module to resume the command task it was running. This is the other half of YIELD_CMD that tells the host to resume the command that was running.
```HOST_CERT``` ```HOST_CERT```
Just as the name implies, this data type is used by the host to send the host SSL certificate while setting up an SSL connection. Just as the name implies, this data type is used by the host to send the host SSL certificate while setting up an SSL connection.
```HOST_VER``` ```HOST_VER```
This data structure carries 3 numeric values that represent the host version as described in section [1.3](protocol.md). This data structure carries 4 numeric values that represent the host version as described in section [1.3](protocol.md).
``` ```
format: format:
1. bytes[0-1] - version major (16bit little endian uint) 1. bytes[0-1] - version major (16bit little endian uint)
2. bytes[2-3] - version minor (16bit little endian uint) 2. bytes[2-3] - version minor (16bit little endian uint)
3. bytes[4-5] - version patch (16bit little endian uint) 3. bytes[4-5] - tcp interface rev (16bit little endian uint)
4. bytes[6-7] - module interface rev (16bit little endian uint)
``` ```
```PRIV_IPC``` ```ASYNC_PAYLOAD```
This is a data structure used to by modules to run async commands on the local session object only. This is a data structure that can be used by modules to run async commands. Each async command have a specific 16bit command id followed by a payload that can contain any data of any format depending on the async command id. A full discribtion of the async commands and the data they carry can found in section [5.1](async.md) and [5.2](async.md).
``` ```
format: format:
@ -110,12 +138,6 @@ This is a data structure used to by modules to run async commands on the local s
2. bytes[2-n] - payload (data to be processed by async command) 2. bytes[2-n] - payload (data to be processed by async command)
``` ```
```PUB_IPC```
This is formatted exactly like PRIV_IPC except it is used by modules to run async commands on all peer session objects in the host while avoiding a run on the local session object.
```PUB_IPC_WITH_FEEDBACK```
This has the same functionality as PUB_IPC except it is also feedback into the local session object.
```FILE_INFO``` ```FILE_INFO```
This is a data structure that carries information about a file system object (file,dir,link). This is a data structure that carries information about a file system object (file,dir,link).
@ -172,7 +194,7 @@ This carry some user account and session information about a peer client connect
``` ```
```PING_PEERS``` ```PING_PEERS```
This is formatted extactly as PEER_INFO except it can be used the ASYNC_LIMITED_CAST [async](async.md) command to tell all peer sessions that receive it to send a PEER_INFO frame about you to their own clients and return PEER_INFO frames about themselves to you. This is formatted extactly as PEER_INFO except it is used by the ASYNC_LIMITED_CAST [async](async.md) command to tell all peer sessions that receive it to send a PEER_INFO frame about the local session to their own clients and return PEER_INFO frames about themselves to the local session.
```MY_INFO``` ```MY_INFO```
This contains all of the information found in ```PEER_INFO``` for the local session but also includes the following: This contains all of the information found in ```PEER_INFO``` for the local session but also includes the following:
@ -220,7 +242,7 @@ This type id carries a 64bit unsighed LE int indicating the channel id.
format: ```8bytes - 64bit LE unsigned int (channel id)``` format: ```8bytes - 64bit LE unsigned int (channel id)```
```SESSION_ID``` ```SESSION_ID```
This is a fixed length 28byte(224bit) sha3 hash of a client's session id connected to the host. This is unique to just the client's tcp connection with the host. This can change upon re-connection. This is a fixed length 28byte(224bit) sha3 hash of a client's session id connected to the host. This is unique to just the client's tcp connection with the host. This changes upon re-connection.
format: ```28bytes - session id (224bit sha3 hash)``` format: ```28bytes - session id (224bit sha3 hash)```
@ -278,12 +300,23 @@ This contains public information about a channel member.
1. a 16bit null terminated TEXT formatted string ended with 2 bytes of 1. a 16bit null terminated TEXT formatted string ended with 2 bytes of
(0x00) to indicate the end of the string data. (0x00) to indicate the end of the string data.
2. is invite? indicates if this user has received an invite to join 2. the member's privilege level can be any of the values discribed in
section [4.3](host_features.md).
3. is invite? indicates if this user has received an invite to join
that channel by has not accepted yet. if, accepted the user will that channel by has not accepted yet. if, accepted the user will
become a full member of the channel at the level indicated by this become a full member of the channel at the level indicated by this
data type. data type.
``` ```
```PROG```
This is a 8bit integer value from 0-100 indicating the percentage progress of the command. All long running module commands are encouraged to use this to update the client on command progress; sending it in a 1-5 second pulse rate.
format: ```1byte - 8bit integer (0-100)```
```PROG_LAST```
This is formatted and treated exactly like PROG except it indicates to the client that this is the last progress update for the current string of progress updates. So at the client side, progress strings/pulses should appear with a single or multiple PROG frame(s) and then end with PROG_LAST. Receiving an IDLE should also end the progress string if seen before PROG_LAST.
### 3.3 GEN_FILE Example ### ### 3.3 GEN_FILE Example ###
Setup: Setup:

View File

@ -5,7 +5,7 @@ installer_file="$2"
src_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" src_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
bin_name="cmdr" bin_name="cmdr"
app_version="2.2.0" app_version="3.0"
app_name="Cmdr" app_name="Cmdr"
install_dir="/opt/$bin_name" install_dir="/opt/$bin_name"
bin_dir="/usr/bin" bin_dir="/usr/bin"
@ -38,7 +38,7 @@ fi
if [ $? -eq 0 -a -d "$qt_dir" ]; then if [ $? -eq 0 -a -d "$qt_dir" ]; then
mkdir -vp $tmp_dir mkdir -vp $tmp_dir
cp -rv $src_dir/. $tmp_dir cp -r $src_dir/. $tmp_dir
cd $tmp_dir cd $tmp_dir
qmake -config release qmake -config release

View File

@ -23,7 +23,7 @@ CmdLine::CmdLine(QWidget *parent) : QComboBox(parent)
setEditable(true); setEditable(true);
setDuplicatesEnabled(true); setDuplicatesEnabled(true);
loadTextSettings(localData, this); loadTheme(localData, this);
if (!localData->contains("command_hist")) if (!localData->contains("command_hist"))
{ {

View File

@ -65,13 +65,13 @@ QString Term::longText() {return TXT_Term;}
Halt::Halt(QObject *parent) : Command(parent) Halt::Halt(QObject *parent) : Command(parent)
{ {
setObjectName("halt"); setObjectName("yield");
Shared::hookBypass->append(objectName()); Shared::hookBypass->append(objectName());
Shared::clientCmds->insert(objectName(), this); Shared::clientCmds->insert(objectName(), this);
} }
QString Halt::shortText() {return tr("halt/pause the currently running host command.");} QString Halt::shortText() {return tr("yield/pause the currently running host command.");}
QString Halt::ioText() {return tr("[none]/[CMD_ID]");} QString Halt::ioText() {return tr("[none]/[CMD_ID]");}
QString Halt::longText() {return TXT_Term;} QString Halt::longText() {return TXT_Term;}

View File

@ -44,10 +44,10 @@ void Status::dataIn(const QString &argsLine)
{ {
txtOut << "" << endl; txtOut << "" << endl;
txtOut << "--Session data" << endl << endl; txtOut << "--Session data" << endl << endl;
txtOut << " Client address : " << Shared::socket->localAddress().toString() << endl; txtOut << " Client address : " << Shared::socket->localAddress().toString() << endl;
txtOut << " Host address : " << Shared::socket->peerAddress().toString() << endl; txtOut << " Host address : " << Shared::socket->peerAddress().toString() << endl;
txtOut << " Session id : " << Shared::sessionId->toHex() << endl; txtOut << " Session id : " << Shared::sessionId->toHex() << endl;
txtOut << " Host version : " << verText(*Shared::servMajor, *Shared::servMinor, *Shared::servPatch) << endl; txtOut << " Host version : " << verText() << endl;
txtOut << " GEN_FILE commands : "; txtOut << " GEN_FILE commands : ";
QStringList genCmds = Shared::genfileCmds->values(); QStringList genCmds = Shared::genfileCmds->values();

View File

@ -20,8 +20,8 @@ void checkArgs(bool *changed, QJsonObject *jsonData, const QString &arg, const Q
{ {
if (argExists(arg, args)) if (argExists(arg, args))
{ {
QString data = getParam(arg, args); auto data = getParam(arg, args);
QJsonObject obj = jsonData->value("text_settings").toObject(); auto obj = jsonData->value("theme").toObject();
if (data.isEmpty()) if (data.isEmpty())
{ {
@ -62,7 +62,7 @@ void checkArgs(bool *changed, QJsonObject *jsonData, const QString &arg, const Q
if (*changed) if (*changed)
{ {
jsonData->insert("text_settings", obj); jsonData->insert("theme", obj);
} }
} }
} }
@ -108,14 +108,14 @@ QString SetMaxLines::longText() {return TXT_SetMaxLines;}
void SetColors::dataIn(const QString &argsLine) void SetColors::dataIn(const QString &argsLine)
{ {
bool changed = false; auto changed = false;
QStringList args = parseArgs(argsLine); auto args = parseArgs(argsLine);
checkArgs(&changed, localData, "-text", args, "text_color", "#FBFBD7"); checkArgs(&changed, localData, "-text", args, "text_color", "#FBFBD7");
checkArgs(&changed, localData, "-error", args, "err_color", "#BB55D8"); checkArgs(&changed, localData, "-error", args, "err_color", "#BB55D8");
checkArgs(&changed, localData, "-bg", args, "bg_color", "#363A49"); checkArgs(&changed, localData, "-bg", args, "bg_color", "#363A49");
QJsonObject obj = localData->value("text_settings").toObject(); auto obj = localData->value("theme").toObject();
cacheTxt(TEXT, "text: " + obj.value("text_color").toString() + "\n"); cacheTxt(TEXT, "text: " + obj.value("text_color").toString() + "\n");
cacheTxt(TEXT, "error: " + obj.value("err_color").toString() + "\n"); cacheTxt(TEXT, "error: " + obj.value("err_color").toString() + "\n");
@ -126,13 +126,13 @@ void SetColors::dataIn(const QString &argsLine)
void SetFont::dataIn(const QString &argsLine) void SetFont::dataIn(const QString &argsLine)
{ {
bool changed = false; auto changed = false;
QStringList args = parseArgs(argsLine); auto args = parseArgs(argsLine);
checkArgs(&changed, localData, "-name", args, "font_family", "courier"); checkArgs(&changed, localData, "-name", args, "font_family", "courier");
checkArgs(&changed, localData, "-size", args, "font_size", "9"); checkArgs(&changed, localData, "-size", args, "font_size", "9");
QJsonObject obj = localData->value("text_settings").toObject(); auto obj = localData->value("theme").toObject();
cacheTxt(TEXT, "family: " + obj.value("font_family").toString() + "\n"); cacheTxt(TEXT, "family: " + obj.value("font_family").toString() + "\n");
cacheTxt(TEXT, "size: " + obj.value("font_size").toString() + "\n"); cacheTxt(TEXT, "size: " + obj.value("font_size").toString() + "\n");
@ -142,8 +142,8 @@ void SetFont::dataIn(const QString &argsLine)
void SetMaxLines::dataIn(const QString &argsLine) void SetMaxLines::dataIn(const QString &argsLine)
{ {
QStringList args = parseArgs(argsLine); auto args = parseArgs(argsLine);
QString valStr = getParam("-value", args); auto valStr = getParam("-value", args);
if (valStr.isEmpty()) if (valStr.isEmpty())
{ {
@ -152,7 +152,7 @@ void SetMaxLines::dataIn(const QString &argsLine)
else else
{ {
bool ok; bool ok;
int val = valStr.toInt(&ok); auto val = valStr.toInt(&ok);
if (!ok) if (!ok)
{ {

View File

@ -16,7 +16,7 @@
// along with Cmdr under the LICENSE.md file. If not, see // along with Cmdr under the LICENSE.md file. If not, see
// <http://www.gnu.org/licenses/>. // <http://www.gnu.org/licenses/>.
void setupTextSettings(QJsonObject *data) void setupThemeSettings(QJsonObject *data)
{ {
QString txtColorStr = "#FBFBD7"; QString txtColorStr = "#FBFBD7";
QString bgColorStr = "#363A49"; QString bgColorStr = "#363A49";
@ -26,9 +26,9 @@ void setupTextSettings(QJsonObject *data)
QJsonObject obj; QJsonObject obj;
if (data->contains("text_settings")) if (data->contains("theme"))
{ {
obj = data->value("text_settings").toObject(); obj = data->value("theme").toObject();
if (!obj.contains("text_color")) obj.insert("text_color", txtColorStr); if (!obj.contains("text_color")) obj.insert("text_color", txtColorStr);
if (!obj.contains("err_color")) obj.insert("err_color", errColorStr); if (!obj.contains("err_color")) obj.insert("err_color", errColorStr);
@ -45,7 +45,7 @@ void setupTextSettings(QJsonObject *data)
obj.insert("font_size", fontSize); obj.insert("font_size", fontSize);
} }
data->insert("text_settings", obj); data->insert("theme", obj);
if (!data->contains("max_lines")) if (!data->contains("max_lines"))
{ {
@ -55,18 +55,26 @@ void setupTextSettings(QJsonObject *data)
saveLocalData(data); saveLocalData(data);
} }
void loadTextSettings(QJsonObject *data, QWidget *widget) void loadTheme(QJsonObject *data, QWidget *widget, bool setHighlight)
{ {
QString bgColor = data->value("text_settings").toObject().value("bg_color").toString(); QString bgColor = data->value("theme").toObject().value("bg_color").toString();
QString txtColor = data->value("text_settings").toObject().value("text_color").toString(); QString txtColor = data->value("theme").toObject().value("text_color").toString();
QString fntFamily = data->value("text_settings").toObject().value("font_family").toString(); QString fntFamily = data->value("theme").toObject().value("font_family").toString();
QString fntSize = data->value("text_settings").toObject().value("font_size").toString(); QString fntSize = data->value("theme").toObject().value("font_size").toString();
QPalette pal = widget->palette(); QPalette pal = widget->palette();
QFont fnt = widget->font(); QFont fnt = widget->font();
pal.setColor(QPalette::Active, QPalette::Base, QColor(bgColor)); pal.setColor(QPalette::Active, QPalette::Base, QColor(bgColor));
pal.setColor(QPalette::Inactive, QPalette::Base, QColor(bgColor)); pal.setColor(QPalette::Inactive, QPalette::Base, QColor(bgColor));
pal.setColor(QPalette::Active, QPalette::Text, QColor(txtColor)); pal.setColor(QPalette::Active, QPalette::Text, QColor(txtColor));
pal.setColor(QPalette::Inactive, QPalette::Text, QColor(txtColor));
if (setHighlight)
{
pal.setColor(QPalette::Active, QPalette::Highlight, QColor(txtColor));
pal.setColor(QPalette::Inactive, QPalette::Highlight, QColor(txtColor));
}
fnt.setFamily(fntFamily); fnt.setFamily(fntFamily);
fnt.setPointSize(fntSize.toInt()); fnt.setPointSize(fntSize.toInt());
@ -335,9 +343,16 @@ QString boolText(bool state)
else return QString("False"); else return QString("False");
} }
QString verText(quint16 maj, quint16 min, quint16 patch) QString verText()
{ {
return QString::number(maj) + "." + QString::number(min) + "." + QString::number(patch); QString ret;
ret.append(QString::number(*Shared::servMajor) + ".");
ret.append(QString::number(*Shared::servMinor) + ".");
ret.append(QString::number(*Shared::tcpRev) + ".");
ret.append(QString::number(*Shared::modRev));
return ret;
} }
QString getParam(const QString &key, const QStringList &args) QString getParam(const QString &key, const QStringList &args)
@ -395,7 +410,8 @@ QByteArray *Shared::sessionId = nullptr;
QString *Shared::clientHookedCmd = nullptr; QString *Shared::clientHookedCmd = nullptr;
ushort *Shared::servMajor = nullptr; ushort *Shared::servMajor = nullptr;
ushort *Shared::servMinor = nullptr; ushort *Shared::servMinor = nullptr;
ushort *Shared::servPatch = nullptr; ushort *Shared::tcpRev = nullptr;
ushort *Shared::modRev = nullptr;
QString *Shared::hostAddress = nullptr; QString *Shared::hostAddress = nullptr;
quint16 *Shared::hostPort = nullptr; quint16 *Shared::hostPort = nullptr;
quint16 *Shared::termCmdId = nullptr; quint16 *Shared::termCmdId = nullptr;
@ -411,6 +427,7 @@ QWidget *Shared::mainWidget = nullptr;
QList<quint8> *Shared::idCache = nullptr; QList<quint8> *Shared::idCache = nullptr;
QList<QString> *Shared::txtCache = nullptr; QList<QString> *Shared::txtCache = nullptr;
ThreadKiller *Shared::theadKiller = nullptr; ThreadKiller *Shared::theadKiller = nullptr;
QProgressBar *Shared::prog = nullptr;
bool Shared::cacheTxt(CacheOp op, quint8 &typeId, QString &txt) bool Shared::cacheTxt(CacheOp op, quint8 &typeId, QString &txt)
{ {
@ -421,7 +438,8 @@ bool Shared::cacheTxt(CacheOp op, quint8 &typeId, QString &txt)
if (op == TXT_IN) if (op == TXT_IN)
{ {
if ((typeId == TEXT) || (typeId == ERR) || (typeId == BIG_TEXT) || (typeId == PRIV_TEXT)) if ((typeId == TEXT) || (typeId == ERR) || (typeId == BIG_TEXT) ||
(typeId == PRIV_TEXT) || (typeId == PROMPT_TEXT))
{ {
idCache->append(typeId); idCache->append(typeId);
txtCache->append(txt); txtCache->append(txt);

View File

@ -63,6 +63,7 @@
#include <QRegExp> #include <QRegExp>
#include <QFileInfo> #include <QFileInfo>
#include <QMutex> #include <QMutex>
#include <QProgressBar>
#include "cmd_objs/long_txt.h" #include "cmd_objs/long_txt.h"
@ -71,42 +72,43 @@
#define RDBUFF 16777215 #define RDBUFF 16777215
#define TXT_CODEC "UTF-16LE" #define TXT_CODEC "UTF-16LE"
#define BOOKMARK_FOLDER "bookmarks" #define BOOKMARK_FOLDER "bookmarks"
#define CONFIG_FILENAME "config.json" #define CONFIG_FILENAME "config_v3.json"
#define APP_NAME "Cmdr" #define APP_NAME "Cmdr"
#define APP_TARGET "cmdr" #define APP_TARGET "cmdr"
#define APP_VERSION "2.2.0" #define APP_VERSION "3.0"
enum TypeID : quint8 enum TypeID : quint8
{ {
GEN_FILE = 1, GEN_FILE = 1,
TEXT = 2, TEXT = 2,
ERR = 3, ERR = 3,
PRIV_TEXT = 4, PRIV_TEXT = 4,
IDLE = 5, IDLE = 5,
HOST_CERT = 6, HOST_CERT = 6,
FILE_INFO = 7, FILE_INFO = 7,
PEER_INFO = 8, PEER_INFO = 8,
MY_INFO = 9, MY_INFO = 9,
PEER_STAT = 10, PEER_STAT = 10,
P2P_REQUEST = 11, P2P_REQUEST = 11,
P2P_CLOSE = 12, P2P_CLOSE = 12,
P2P_OPEN = 13, P2P_OPEN = 13,
BYTES = 14, BYTES = 14,
SESSION_ID = 15, SESSION_ID = 15,
NEW_CMD = 16, NEW_CMD = 16,
CMD_ID = 17, CMD_ID = 17,
BIG_TEXT = 18, BIG_TEXT = 18,
TERM_CMD = 19, TERM_CMD = 19,
HOST_VER = 20, HOST_VER = 20,
PRIV_IPC = 21, PING_PEERS = 21,
PUB_IPC = 22, CH_MEMBER_INFO = 22,
PUB_IPC_WITH_FEEDBACK = 23, CH_ID = 23,
PING_PEERS = 24, KILL_CMD = 24,
CH_MEMBER_INFO = 25, YIELD_CMD = 25,
CH_ID = 26, RESUME_CMD = 26,
KILL_CMD = 27, PROMPT_TEXT = 27,
HALT_CMD = 28, PROG = 28,
RESUME_CMD = 29 PROG_LAST = 29,
ASYNC_PAYLOAD = 30
}; };
enum GenFileType : quint8 enum GenFileType : quint8
@ -117,11 +119,11 @@ enum GenFileType : quint8
enum AsyncCommands : quint16 enum AsyncCommands : quint16
{ {
ASYNC_RDY = 1, // client | none ASYNC_RDY = 1, // client | retricted
ASYNC_SYS_MSG = 2, // client | none ASYNC_SYS_MSG = 2, // client | retricted
ASYNC_CAST = 4, // client | public ASYNC_CAST = 4, // client | public
ASYNC_USER_DELETED = 7, // client | public ASYNC_USER_DELETED = 7, // client | public
ASYNC_TO_PEER = 16, // client | public | retricted ASYNC_TO_PEER = 16, // client | retricted
ASYNC_LIMITED_CAST = 17, // client | public ASYNC_LIMITED_CAST = 17, // client | public
ASYNC_P2P = 19, // client | public ASYNC_P2P = 19, // client | public
ASYNC_NEW_CH_MEMBER = 21, // client | public ASYNC_NEW_CH_MEMBER = 21, // client | public
@ -137,8 +139,8 @@ enum AsyncCommands : quint16
ASYNC_SUB_CH_LEVEL_CHG = 32, // client | public ASYNC_SUB_CH_LEVEL_CHG = 32, // client | public
ASYNC_ADD_RDONLY = 33, // client | public ASYNC_ADD_RDONLY = 33, // client | public
ASYNC_RM_RDONLY = 34, // client | public ASYNC_RM_RDONLY = 34, // client | public
ASYNC_ADD_CMD = 35, // client | none ASYNC_ADD_CMD = 35, // client | retricted
ASYNC_RM_CMD = 36, // client | none ASYNC_RM_CMD = 36, // client | retricted
}; };
enum ChannelMemberLevel enum ChannelMemberLevel
@ -158,8 +160,8 @@ enum UserIOFlags
HIDDEN = 1 << 4 HIDDEN = 1 << 4
}; };
void setupTextSettings(QJsonObject *data); void setupThemeSettings(QJsonObject *data);
void loadTextSettings(QJsonObject *data, QWidget *widget); void loadTheme(QJsonObject *data, QWidget *widget, bool setHighlight = false);
void saveLocalData(QJsonObject *obj); void saveLocalData(QJsonObject *obj);
void loadLocalData(QJsonObject *obj); void loadLocalData(QJsonObject *obj);
void wordWrap(const QString &label, QTextStream &txtOut, const QString &txtIn, QWidget *measureWid); void wordWrap(const QString &label, QTextStream &txtOut, const QString &txtIn, QWidget *measureWid);
@ -175,7 +177,7 @@ QString appDataDir();
QString getParam(const QString &key, const QStringList &args); QString getParam(const QString &key, const QStringList &args);
QString extractCmdName(const QByteArray &data); QString extractCmdName(const QByteArray &data);
QString boolText(bool state); QString boolText(bool state);
QString verText(quint16 maj, quint16 min, quint16 patch); QString verText();
quint64 rdInt(const QByteArray &bytes); quint64 rdInt(const QByteArray &bytes);
class Command; class Command;
@ -200,7 +202,8 @@ public:
static QByteArray *sessionId; static QByteArray *sessionId;
static ushort *servMajor; static ushort *servMajor;
static ushort *servMinor; static ushort *servMinor;
static ushort *servPatch; static ushort *tcpRev;
static ushort *modRev;
static QString *hostAddress; static QString *hostAddress;
static QString *clientHookedCmd; static QString *clientHookedCmd;
static quint16 *hostPort; static quint16 *hostPort;
@ -224,6 +227,7 @@ public:
static ContextReloader *contextReloader; static ContextReloader *contextReloader;
static QWidget *mainWidget; static QWidget *mainWidget;
static ThreadKiller *theadKiller; static ThreadKiller *theadKiller;
static QProgressBar *prog;
enum CacheOp enum CacheOp
{ {

View File

@ -38,6 +38,7 @@ void setupCmdLine();
void setupGenFile(); void setupGenFile();
void setupSession(); void setupSession();
void setupText(); void setupText();
void setupProgBar();
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -50,7 +51,8 @@ int main(int argc, char *argv[])
bool activeDisp = false; bool activeDisp = false;
ushort servMajor = 0; ushort servMajor = 0;
ushort servMinor = 0; ushort servMinor = 0;
ushort servPatch = 0; ushort tcpRev = 0;
ushort modRev = 0;
quint16 hostPort = 0; quint16 hostPort = 0;
quint16 termCmdId = 0; quint16 termCmdId = 0;
QByteArray sessionId; QByteArray sessionId;
@ -67,7 +69,7 @@ int main(int argc, char *argv[])
QJsonObject localData; QJsonObject localData;
loadLocalData(&localData); loadLocalData(&localData);
setupTextSettings(&localData); setupThemeSettings(&localData);
Shared::connectedToHost = &connected; Shared::connectedToHost = &connected;
Shared::genfileCmds = &genfileCmds; Shared::genfileCmds = &genfileCmds;
@ -78,7 +80,8 @@ int main(int argc, char *argv[])
Shared::sessionId = &sessionId; Shared::sessionId = &sessionId;
Shared::servMajor = &servMajor; Shared::servMajor = &servMajor;
Shared::servMinor = &servMinor; Shared::servMinor = &servMinor;
Shared::servPatch = &servPatch; Shared::tcpRev = &tcpRev;
Shared::modRev = &modRev;
Shared::hostAddress = &hostAddress; Shared::hostAddress = &hostAddress;
Shared::hostPort = &hostPort; Shared::hostPort = &hostPort;
Shared::hookBypass = &hookBypass; Shared::hookBypass = &hookBypass;
@ -152,14 +155,14 @@ void ContextReloader::reloadCmdLine()
void setupClientCmds() void setupClientCmds()
{ {
Session *session = Shared::session; auto *session = Shared::session;
CmdLine *cmdLine = Shared::cmdLine; auto *cmdLine = Shared::cmdLine;
TextWorker *textWorker = Shared::textWorker; auto *textWorker = Shared::textWorker;
TextBody *textBody = Shared::textBody; auto *textBody = Shared::textBody;
Genfile *genFile = Shared::genFile; auto *genFile = Shared::genFile;
MainWindow *mainWin = Shared::mainWin; auto *mainWin = Shared::mainWin;
ContextReloader *reloader = Shared::contextReloader; auto *reloader = Shared::contextReloader;
ThreadKiller *killer = Shared::theadKiller; auto *killer = Shared::theadKiller;
QObject::connect(mainWin, &MainWindow::closeApp, killer, &ThreadKiller::quitThreads); QObject::connect(mainWin, &MainWindow::closeApp, killer, &ThreadKiller::quitThreads);
@ -175,7 +178,7 @@ void setupClientCmds()
QObject::connect(command, &Command::unsetUserIO, cmdLine, &CmdLine::unsetFlags); QObject::connect(command, &Command::unsetUserIO, cmdLine, &CmdLine::unsetFlags);
QObject::connect(command, &Command::termHostCmd, session, &Session::termHostCmd); QObject::connect(command, &Command::termHostCmd, session, &Session::termHostCmd);
QObject::connect(command, &Command::haltHostcmd, session, &Session::haltHostCmd); QObject::connect(command, &Command::haltHostcmd, session, &Session::yieldHostCmd);
QObject::connect(command, &Command::resumeHostCmd, session, &Session::resumeHostCmd); QObject::connect(command, &Command::resumeHostCmd, session, &Session::resumeHostCmd);
QObject::connect(command, &Command::connectToHost, session, &Session::connectToServ); QObject::connect(command, &Command::connectToHost, session, &Session::connectToServ);
QObject::connect(command, &Command::quitApp, session, &Session::disconnectFromServ); QObject::connect(command, &Command::quitApp, session, &Session::disconnectFromServ);
@ -199,10 +202,10 @@ void setupClientCmds()
void setupCmdLine() void setupCmdLine()
{ {
Session *session = Shared::session; auto *session = Shared::session;
CmdLine *cmdLine = Shared::cmdLine; auto *cmdLine = Shared::cmdLine;
TextWorker *textWorker = Shared::textWorker; auto *textWorker = Shared::textWorker;
Genfile *genFile = Shared::genFile; auto *genFile = Shared::genFile;
QObject::connect(cmdLine, &CmdLine::dataToHost, session, &Session::binToServer); QObject::connect(cmdLine, &CmdLine::dataToHost, session, &Session::binToServer);
QObject::connect(cmdLine, &CmdLine::dataToHookedHost, session, &Session::hookedBinToServer); QObject::connect(cmdLine, &CmdLine::dataToHookedHost, session, &Session::hookedBinToServer);
@ -216,12 +219,12 @@ void setupCmdLine()
void setupGenFile() void setupGenFile()
{ {
Session *session = Shared::session; auto *session = Shared::session;
CmdLine *cmdLine = Shared::cmdLine; auto *cmdLine = Shared::cmdLine;
TextWorker *textWorker = Shared::textWorker; auto *textWorker = Shared::textWorker;
Genfile *genFile = Shared::genFile; auto *genFile = Shared::genFile;
ThreadKiller *killer = Shared::theadKiller; auto *killer = Shared::theadKiller;
QThread *genThr = new QThread(QCoreApplication::instance()); auto *genThr = new QThread(QCoreApplication::instance());
QObject::connect(genFile, &Genfile::termHostCmd, session, &Session::termHostCmd); QObject::connect(genFile, &Genfile::termHostCmd, session, &Session::termHostCmd);
QObject::connect(genFile, &Genfile::dataOut, session, &Session::hookedBinToServer); QObject::connect(genFile, &Genfile::dataOut, session, &Session::hookedBinToServer);
@ -242,12 +245,12 @@ void setupGenFile()
void setupSession() void setupSession()
{ {
Session *session = Shared::session; auto *session = Shared::session;
CmdLine *cmdLine = Shared::cmdLine; auto *cmdLine = Shared::cmdLine;
TextWorker *textWorker = Shared::textWorker; auto *textWorker = Shared::textWorker;
Genfile *genFile = Shared::genFile; auto *genFile = Shared::genFile;
ThreadKiller *killer = Shared::theadKiller; auto *killer = Shared::theadKiller;
QThread *sesThr = new QThread(QCoreApplication::instance()); auto *sesThr = new QThread(QCoreApplication::instance());
QObject::connect(session, &Session::hostFinished, genFile, &Genfile::finished); QObject::connect(session, &Session::hostFinished, genFile, &Genfile::finished);
QObject::connect(session, &Session::toGenFile, genFile, &Genfile::dataIn); QObject::connect(session, &Session::toGenFile, genFile, &Genfile::dataIn);
@ -266,11 +269,11 @@ void setupSession()
void setupText() void setupText()
{ {
TextWorker *textWorker = Shared::textWorker; auto *textWorker = Shared::textWorker;
CmdLine *cmdLine = Shared::cmdLine; auto *cmdLine = Shared::cmdLine;
TextBody *textBody = Shared::textBody; auto *textBody = Shared::textBody;
ThreadKiller *killer = Shared::theadKiller; auto *killer = Shared::theadKiller;
QThread *txtThr = new QThread(QCoreApplication::instance()); auto *txtThr = new QThread(QCoreApplication::instance());
QObject::connect(textWorker, &TextWorker::setUserIO, cmdLine, &CmdLine::setFlags); QObject::connect(textWorker, &TextWorker::setUserIO, cmdLine, &CmdLine::setFlags);
QObject::connect(textWorker, &TextWorker::unsetUserIO, cmdLine, &CmdLine::unsetFlags); QObject::connect(textWorker, &TextWorker::unsetUserIO, cmdLine, &CmdLine::unsetFlags);
@ -283,3 +286,12 @@ void setupText()
textWorker->moveToThread(txtThr); textWorker->moveToThread(txtThr);
txtThr->start(); txtThr->start();
} }
void setupProgBar()
{
auto *session = Shared::session;
auto *prog = Shared::prog;
QObject::connect(session, &Session::setProg, prog, &QProgressBar::setValue);
QObject::connect(session, &Session::showProg, prog, &QProgressBar::setVisible);
}

View File

@ -85,13 +85,26 @@ void MainWindow::closeEvent(QCloseEvent *event)
void MainWindow::setCmdLine(QWidget *widget) void MainWindow::setCmdLine(QWidget *widget)
{ {
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
mainLayout->insertWidget(1, widget); mainLayout->insertWidget(2, widget);
} }
void MainWindow::setTextBody(QWidget *widget) void MainWindow::setTextBody(QWidget *widget)
{ {
Shared::prog = new QProgressBar(this);
Shared::prog->setVisible(false);
Shared::prog->setFixedHeight(5);
Shared::prog->setTextVisible(false);
Shared::prog->setMaximum(100);
Shared::prog->setMinimum(0);
Shared::prog->setValue(0);
Shared::prog->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
loadTheme(localData, Shared::prog, true);
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
mainLayout->insertWidget(0, widget); mainLayout->insertWidget(0, widget);
mainLayout->insertWidget(1, Shared::prog);
} }
void MainWindow::showUi() void MainWindow::showUi()

View File

@ -18,13 +18,16 @@
Session::Session(QObject *parent) : QSslSocket(nullptr) Session::Session(QObject *parent) : QSslSocket(nullptr)
{ {
dSize = 0; dSize = 0;
hook = 0; hook = 0;
dType = 0; dType = 0;
flags = 0; flags = 0;
reconnect = false; progResetDelay = new QTimer(this);
activeProg = false;
reconnect = false;
connect(parent, &QSslSocket::destroyed, this, &Session::deleteLater); connect(parent, &QSslSocket::destroyed, this, &Session::deleteLater);
connect(progResetDelay, &QTimer::timeout, this, &Session::resetProg);
connect(this, &Session::encrypted, this, &Session::handShakeDone); connect(this, &Session::encrypted, this, &Session::handShakeDone);
connect(this, &Session::connected, this, &Session::isConnected); connect(this, &Session::connected, this, &Session::isConnected);
@ -33,6 +36,9 @@ Session::Session(QObject *parent) : QSslSocket(nullptr)
connect(this, &Session::loopDataIn, this, &Session::dataIn); connect(this, &Session::loopDataIn, this, &Session::dataIn);
connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sockerr(QAbstractSocket::SocketError))); connect(this, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(sockerr(QAbstractSocket::SocketError)));
progResetDelay->setInterval(200);
progResetDelay->setSingleShot(true);
} }
void Session::hookedBinToServer(const QByteArray &data, quint8 typeId) void Session::hookedBinToServer(const QByteArray &data, quint8 typeId)
@ -93,10 +99,16 @@ void Session::isConnected()
// appName = UTF16LE string (padded with 0x00) // appName = UTF16LE string (padded with 0x00)
// coName = UTF16LE string (padded with 0x00) // coName = UTF16LE string (padded with 0x00)
// for MRCI host, if the client doesn't need to request a special SSL
// cert from the host, it is good practice to fallback to using the
// address used to connect to the host as the common name (coName).
// in this case, it is used all of the time.
cacheTxt(TEXT, "Connected.\n"); cacheTxt(TEXT, "Connected.\n");
auto appName = QString(APP_NAME) + " v" + QString(APP_VERSION);
QByteArray header; QByteArray header;
QString appName = QString(APP_NAME) + " v" + QString(APP_VERSION);
header.append(SERVER_HEADER_TAG); header.append(SERVER_HEADER_TAG);
header.append(fixedToTEXT(appName, 134)); header.append(fixedToTEXT(appName, 134));
@ -115,9 +127,9 @@ void Session::isConnected()
void Session::handShakeDone() void Session::handShakeDone()
{ {
QSslCipher cipher = sessionCipher(); auto cipher = sessionCipher();
QString txt;
QString txt;
QTextStream txtOut(&txt); QTextStream txtOut(&txt);
txtOut << "SSL Handshake sucessful." << endl txtOut << "SSL Handshake sucessful." << endl
@ -140,7 +152,8 @@ void Session::isDisconnected()
*Shared::servMajor = 0; *Shared::servMajor = 0;
*Shared::servMinor = 0; *Shared::servMinor = 0;
*Shared::servPatch = 0; *Shared::tcpRev = 0;
*Shared::modRev = 0;
*Shared::connectedToHost = false; *Shared::connectedToHost = false;
Shared::sessionId->clear(); Shared::sessionId->clear();
@ -194,11 +207,11 @@ void Session::termHostCmd()
} }
} }
void Session::haltHostCmd() void Session::yieldHostCmd()
{ {
if (hook != 0) if (hook != 0)
{ {
binToServer(hook, QByteArray(), HALT_CMD); binToServer(hook, QByteArray(), YIELD_CMD);
} }
} }
@ -278,7 +291,7 @@ void Session::procAsync(const QByteArray &data)
{ {
if ((dType == CMD_ID) && (data.size() == 2)) if ((dType == CMD_ID) && (data.size() == 2))
{ {
quint16 id = static_cast<quint16>(rdInt(data.mid(0, 2))); auto id = static_cast<quint16>(rdInt(data.mid(0, 2)));
if (id == hook) hook = 0; if (id == hook) hook = 0;
@ -314,6 +327,31 @@ void Session::idle()
hook = 0; hook = 0;
} }
void Session::resetProg()
{
emit setProg(0);
emit showProg(false);
activeProg = false;
}
void Session::startProg()
{
emit showProg(true);
activeProg = true;
}
void Session::updateProg(int value)
{
emit setProg(value);
if (!activeProg)
{
startProg();
}
}
void Session::dataFromHost(const QByteArray &data) void Session::dataFromHost(const QByteArray &data)
{ {
if (isAsync(cmdId)) if (isAsync(cmdId))
@ -322,12 +360,27 @@ void Session::dataFromHost(const QByteArray &data)
} }
else if ((cmdId == hook) && (branId == 0) && (hook != 0)) else if ((cmdId == hook) && (branId == 0) && (hook != 0))
{ {
if ((dType == TEXT) || (dType == PRIV_TEXT) || (dType == BIG_TEXT) || (dType == ERR)) if ((dType == TEXT) || (dType == PRIV_TEXT) || (dType == BIG_TEXT) || (dType == ERR) || (dType == PROMPT_TEXT))
{ {
cacheTxt(dType, fromTEXT(data)); cacheTxt(dType, fromTEXT(data));
} }
else if (dType == PROG)
{
updateProg(rdInt(data));
}
else if (dType == PROG_LAST)
{
progResetDelay->start();
updateProg(rdInt(data));
}
else if (dType == IDLE) else if (dType == IDLE)
{ {
if (activeProg && !progResetDelay->isActive())
{
resetProg();
}
idle(); idle();
} }
else if ((dType == GEN_FILE) && (flags & GEN_FILE_ON)) else if ((dType == GEN_FILE) && (flags & GEN_FILE_ON))
@ -354,7 +407,7 @@ void Session::dataIn()
{ {
if (bytesAvailable() >= FRAME_HEADER_LEN) if (bytesAvailable() >= FRAME_HEADER_LEN)
{ {
QByteArray header = read(FRAME_HEADER_LEN); auto header = read(FRAME_HEADER_LEN);
dType = static_cast<quint8>(header[0]); dType = static_cast<quint8>(header[0]);
cmdId = static_cast<quint16>(rdInt(header.mid(1, 2))); cmdId = static_cast<quint16>(rdInt(header.mid(1, 2)));
@ -367,7 +420,7 @@ void Session::dataIn()
} }
else if (bytesAvailable() >= SERVER_HEADER_LEN) else if (bytesAvailable() >= SERVER_HEADER_LEN)
{ {
// host header format: [1byte(reply)][2bytes(major)][2bytes(minor)][2bytes(patch)][28bytes(sesId)] // host header format: [1byte(reply)][2bytes(major)][2bytes(minor)][2bytes(tcp_rev)][2bytes(mod_rev)][28bytes(sesId)]
// reply is an 8bit little endian int the indicates the result of the client header // reply is an 8bit little endian int the indicates the result of the client header
// sent to the host from the isConnected() function. // sent to the host from the isConnected() function.
@ -377,17 +430,22 @@ void Session::dataIn()
// minor = 16bit little endian uint (host ver minor) // minor = 16bit little endian uint (host ver minor)
// patch = 16bit little endian uint (host ver patch) // patch = 16bit little endian uint (host ver patch)
quint8 reply = static_cast<quint8>(rdInt(read(1))); // reply 1: client header acceptable, SSL handshake not needed.
// reply 2: client header acceptable, SSL handshake needed (STARTTLS).
// reply 4: the common name sent by the client header was not found.
auto reply = static_cast<quint8>(rdInt(read(1)));
if ((reply == 1) || (reply == 2)) if ((reply == 1) || (reply == 2))
{ {
*Shared::servMajor = static_cast<quint16>(rdInt(read(2))); *Shared::servMajor = static_cast<quint16>(rdInt(read(2)));
*Shared::servMinor = static_cast<quint16>(rdInt(read(2))); *Shared::servMinor = static_cast<quint16>(rdInt(read(2)));
*Shared::servPatch = static_cast<quint16>(rdInt(read(2))); *Shared::tcpRev = static_cast<quint16>(rdInt(read(2)));
*Shared::modRev = static_cast<quint16>(rdInt(read(2)));
cacheTxt(TEXT, "Detected host version: " + verText(*Shared::servMajor, *Shared::servMinor, *Shared::servPatch) + "\n"); cacheTxt(TEXT, "Detected host version: " + verText() + "\n");
if (*Shared::servMajor == 2) if (*Shared::tcpRev == 0)
{ {
*Shared::sessionId = read(28); *Shared::sessionId = read(28);
*Shared::connectedToHost = true; *Shared::connectedToHost = true;

View File

@ -34,6 +34,7 @@
#include <QSslCipher> #include <QSslCipher>
#include <QSslCertificate> #include <QSslCertificate>
#include <QSocketNotifier> #include <QSocketNotifier>
#include <QTimer>
#include "cmd_objs/command.h" #include "cmd_objs/command.h"
#include "cmd_objs/bookmarks.h" #include "cmd_objs/bookmarks.h"
@ -50,17 +51,20 @@ class Session : public QSslSocket
private: private:
QTimer *progResetDelay;
quint32 flags; quint32 flags;
quint32 dSize; quint32 dSize;
quint16 cmdId; quint16 cmdId;
quint16 branId; quint16 branId;
quint16 hook; quint16 hook;
quint8 dType; quint8 dType;
bool activeProg;
bool reconnect; bool reconnect;
void cacheTxt(quint8 typeId, QString txt); void cacheTxt(quint8 typeId, QString txt);
void dataFromHost(const QByteArray &data); void dataFromHost(const QByteArray &data);
void procAsync(const QByteArray &data); void procAsync(const QByteArray &data);
void updateProg(int value);
bool isAsync(quint16 id); bool isAsync(quint16 id);
private slots: private slots:
@ -69,6 +73,8 @@ private slots:
void isConnected(); void isConnected();
void isDisconnected(); void isDisconnected();
void handShakeDone(); void handShakeDone();
void resetProg();
void startProg();
void sockerr(QAbstractSocket::SocketError err); void sockerr(QAbstractSocket::SocketError err);
public: public:
@ -90,7 +96,7 @@ public slots:
void setCmdHook(quint16 cmdId); void setCmdHook(quint16 cmdId);
void idle(); void idle();
void termHostCmd(); void termHostCmd();
void haltHostCmd(); void yieldHostCmd();
void resumeHostCmd(); void resumeHostCmd();
void connectToServ(); void connectToServ();
void disconnectFromServ(); void disconnectFromServ();
@ -100,6 +106,8 @@ signals:
void hostFinished(); void hostFinished();
void txtInCache(); void txtInCache();
void loopDataIn(); void loopDataIn();
void showProg(bool);
void setProg(int);
void setUserIO(int flgs); void setUserIO(int flgs);
void unsetUserIO(int flgs); void unsetUserIO(int flgs);
void hostCmdRemoved(quint16 id); void hostCmdRemoved(quint16 id);

View File

@ -44,7 +44,7 @@ void TextWorker::dumpTxtCache()
{ {
*Shared::activeDisp = !Shared::cacheTxt(Shared::TXT_IS_EMPTY); *Shared::activeDisp = !Shared::cacheTxt(Shared::TXT_IS_EMPTY);
if ((typeId == TEXT) || (typeId == PRIV_TEXT)) if ((typeId == TEXT) || (typeId == PRIV_TEXT) | (typeId == PROMPT_TEXT))
{ {
addMainTxt(txt); addMainTxt(txt);
@ -103,8 +103,8 @@ void TextWorker::addBigTxt(const QString &txt)
void TextWorker::loadSettings() void TextWorker::loadSettings()
{ {
mainColor = localData->value("text_settings").toObject().value("text_color").toString(); mainColor = localData->value("theme").toObject().value("text_color").toString();
errColor = localData->value("text_settings").toObject().value("err_color").toString(); errColor = localData->value("theme").toObject().value("err_color").toString();
} }
TextBody::TextBody(QWidget *parent) : QTextEdit(parent) TextBody::TextBody(QWidget *parent) : QTextEdit(parent)
@ -116,7 +116,7 @@ TextBody::TextBody(QWidget *parent) : QTextEdit(parent)
setDocument(txtDocument); setDocument(txtDocument);
setReadOnly(true); setReadOnly(true);
setWordWrapMode(QTextOption::NoWrap); setWordWrapMode(QTextOption::NoWrap);
loadTextSettings(localData, this); loadTheme(localData, this);
loadTextBodySettings(); loadTextBodySettings();
} }
@ -134,7 +134,7 @@ void TextBody::reload()
{ {
clear(); clear();
loadTextSettings(localData, this); loadTheme(localData, this);
saveLocalData(localData); saveLocalData(localData);
loadTextBodySettings(); loadTextBodySettings();
} }