Compare commits
No commits in common. "27b03885df3301d83c06766e687799545940dc50" and "a98a6af7362812dd55adafb4e0119514c309a6f0" have entirely different histories.
27b03885df
...
a98a6af736
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -28,10 +28,6 @@ ui_*.h
|
||||||
*.jsc
|
*.jsc
|
||||||
Makefile*
|
Makefile*
|
||||||
*build-*
|
*build-*
|
||||||
/build
|
|
||||||
/app_dir
|
|
||||||
/release
|
|
||||||
/debug
|
|
||||||
|
|
||||||
# Qt unit tests
|
# Qt unit tests
|
||||||
target_wrapper.*
|
target_wrapper.*
|
||||||
|
@ -51,6 +47,3 @@ compile_commands.json
|
||||||
|
|
||||||
# QtCreator local machine specific files for imported projects
|
# QtCreator local machine specific files for imported projects
|
||||||
*creator.user*
|
*creator.user*
|
||||||
|
|
||||||
# VSCode
|
|
||||||
/.vscode
|
|
||||||
|
|
18
Cmdr.pro
18
Cmdr.pro
|
@ -36,28 +36,10 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
win32 {
|
|
||||||
|
|
||||||
LIBS += -llibcrypto -llibssl
|
|
||||||
|
|
||||||
TARGET = cmdr
|
TARGET = cmdr
|
||||||
DESTDIR_TARGET = build\\windows\\cmdr.exe
|
|
||||||
OBJECTS_DIR = build\\windows
|
|
||||||
MOC_DIR = build\\windows
|
|
||||||
RCC_DIR = build\\windows
|
|
||||||
DESTDIR = build\\windows
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
LIBS += -lcrypto -lssl
|
LIBS += -lcrypto -lssl
|
||||||
|
|
||||||
TARGET = build/linux/cmdr
|
|
||||||
OBJECTS_DIR = build/linux
|
|
||||||
MOC_DIR = build/linux
|
|
||||||
RCC_DIR = build/linux
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
SOURCES += src/main.cpp \
|
SOURCES += src/main.cpp \
|
||||||
src/cmd_line.cpp \
|
src/cmd_line.cpp \
|
||||||
src/cmd_objs/host_doc.cpp \
|
src/cmd_objs/host_doc.cpp \
|
||||||
|
|
123
README.md
123
README.md
|
@ -1,123 +0,0 @@
|
||||||
# Cmdr #
|
|
||||||
|
|
||||||
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 ###
|
|
||||||
|
|
||||||
Cmdr have it's own terminal display so there is no command line switches to pass on start up. Instead, all commands are parsed and processed within it's own terminal interface. Any commands not seen as an internal command for the client itself is passed through to the MRCI host if connected. The client will add a numeric identifier to the end of all host command names that clash with the client commands so there is no chance a client and host command can conflict with each other.
|
|
||||||
|
|
||||||
### Versioning System ###
|
|
||||||
|
|
||||||
This application uses a 2 number versioning system: [major].[minor]
|
|
||||||
|
|
||||||
* 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 config files or libraries.
|
|
||||||
|
|
||||||
Any increments to major resets minor to 0.
|
|
||||||
|
|
||||||
### The Protocol ###
|
|
||||||
|
|
||||||
This application being a MRCI client uses the MRCI protocol to transport data to and from the host using TCP in a binary data format called MRCI frames. In general, local connections with the host are not encrypted but all connections outside of the host must be encrypted using TLS/SSL (including the local network).
|
|
||||||
|
|
||||||
Before any MRCI frames can be transported, both the host and client need basic information about each other. This is done by having the client send a fixed length client header when it successfully connects to the host and the host will reply with it's own fixed length host header, described below.
|
|
||||||
|
|
||||||
### MRCI Frame Format ###
|
|
||||||
|
|
||||||
```
|
|
||||||
[type_id][cmd_id][branch_id][data_len][payload]
|
|
||||||
|
|
||||||
type_id - 1byte - 8bit little endian integer type id of the payload.
|
|
||||||
cmd_id - 2bytes - 16bit little endian integer command id.
|
|
||||||
branch_id - 2bytes - 16bit little endian integer branch id.
|
|
||||||
data_len - 3bytes - 24bit little endian integer size of the payload.
|
|
||||||
payload - variable - the actual data to be processed.
|
|
||||||
```
|
|
||||||
|
|
||||||
notes:
|
|
||||||
|
|
||||||
* A full description of the type id's can be found in the [Type_IDs.md](docs/type_ids.md) document.
|
|
||||||
|
|
||||||
* This client call commands by name but the host will assign unique command ids for it's command names and will require the client to use the command id on calling. To track this command id to command name relationship for the host commands, this client will rely on the [ASYNC_ADD_CMD](docs/async.md) and [ASYNC_RM_CMD](docs/async.md) async commands.
|
|
||||||
|
|
||||||
* The branch id is an id that can be used to run multiple instances of the same command on the host. Commands sent by a certain branch id will result in data sent back with that same branch id. For now, this client does not do branching; instead all commands sent to the host using branch id 0 only.
|
|
||||||
|
|
||||||
### Client Header (This Application) ###
|
|
||||||
|
|
||||||
```
|
|
||||||
[tag][appName][mod_instructions][padding]
|
|
||||||
|
|
||||||
tag - 4bytes - 0x4D, 0x52, 0x43, 0x49 (MRCI)
|
|
||||||
appName - 32bytes - UTF8 string (padded with 0x00)
|
|
||||||
modInst - 128bytes - UTF8 string (padded with 0x00)
|
|
||||||
padding - 128bytes - string of (0x00)
|
|
||||||
```
|
|
||||||
|
|
||||||
notes:
|
|
||||||
|
|
||||||
* **tag** is just a fixed ascii string "MRCI" that indicates to the host that the client is indeed attempting to use the MRCI protocol.
|
|
||||||
|
|
||||||
* **appName** is the name of the client application that is connected to the host. It can also contain the client's app version if needed because it doesn't follow any particular standard. This string is accessable to all modules so the commands themselves can be made aware of what app the user is currently using.
|
|
||||||
|
|
||||||
* **modInst** is an additional set of command lines that can be passed onto to all module processes when they are initialized. This can be used by certain clients that want to instruct certain modules that might be installed in the host to do certain actions during initialization. This remains constant for as long as the session is active and cannot be changed at any point.
|
|
||||||
|
|
||||||
### Host Header ###
|
|
||||||
|
|
||||||
```
|
|
||||||
Format:
|
|
||||||
|
|
||||||
[reply][major][minor][tcp_rev][mod_rev][sesId]
|
|
||||||
|
|
||||||
reply - 1byte - 8bit little endian unsigned int
|
|
||||||
major - 2bytes - 16bit little endian unsigned int
|
|
||||||
minor - 2bytes - 16bit little endian unsigned int
|
|
||||||
tcp_rev - 2bytes - 16bit little endian unsigned int
|
|
||||||
mod_rev - 2bytes - 16bit little endian unsigned int
|
|
||||||
sesId - 28bytes - 224bit sha3 hash
|
|
||||||
```
|
|
||||||
|
|
||||||
notes:
|
|
||||||
|
|
||||||
* **reply** is a numeric value that the host returns in it's header to communicate to the client if SSL need to imitated or not.
|
|
||||||
|
|
||||||
* reply = 1, means SSL is not required so the client doesn't need to take any further action.
|
|
||||||
* reply = 2, means SSL is required to continue so the client needs to send a STARTLS signal.
|
|
||||||
|
|
||||||
* **sesId** is the session id. It is a unique 224bit sha3 hash generated against the current date and time of session creation (down to the msec) and the machine id of the host. This can be used by the host or client to uniquely identify the current session or past sessions.
|
|
||||||
|
|
||||||
### Async Commands ###
|
|
||||||
|
|
||||||
Async commands are 'virtual commands' that this application can encounter at any time while connected to the host. More information about this can be found in the [Async.md](docs/async.md) document. This application does act on some of the data carried by the async commands but not all of them.
|
|
||||||
|
|
||||||
### Build Setup ###
|
|
||||||
|
|
||||||
For Linux you need the following packages to successfully build/install:
|
|
||||||
```
|
|
||||||
qtbase5-dev
|
|
||||||
libssl-dev
|
|
||||||
libgl1-mesa-dev
|
|
||||||
libxcb1-dev
|
|
||||||
gcc
|
|
||||||
make
|
|
||||||
python3
|
|
||||||
```
|
|
||||||
|
|
||||||
For Windows support you need to have the following applications installed:
|
|
||||||
```
|
|
||||||
OpenSSL
|
|
||||||
Qt5.12 or newer
|
|
||||||
Python3
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build ###
|
|
||||||
|
|
||||||
To build this project from source you just need to run the build.py and then the install.py python scripts. While running the build the script, it will try to find the Qt API installed in your machine according to the PATH env variable. If not found, it will ask you to input where it can find the Qt bin folder where the qmake executable exists or you can bypass all of this by passing the -qt_dir option on it's command line.
|
|
||||||
|
|
||||||
while running the install script, it will ask you to input 1 of 3 options:
|
|
||||||
|
|
||||||
***local machine*** - This option will install the built application onto the local machine without creating an installer.
|
|
||||||
|
|
||||||
***create installer*** - This option creates an installer that can be distributed to other machines to installation. The resulting installer is just a regular .py script file that the target machine can run if it has Python3 installed. Only Python3 needs to be installed and an internet connection is not required.
|
|
||||||
|
|
||||||
***exit*** - Cancel the installation.
|
|
||||||
|
|
||||||
-local or -installer can be passed as command line options for install.py to explicitly select one of the above options without pausing for user input.
|
|
300
build.py
300
build.py
|
@ -1,300 +0,0 @@
|
||||||
#!/usr/bin/python3
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import shutil
|
|
||||||
import platform
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def get_app_target(text):
|
|
||||||
return re.search(r'(APP_TARGET) +(\"(.*?)\")', text).group(3)
|
|
||||||
|
|
||||||
def get_app_ver(text):
|
|
||||||
return re.search(r'(APP_VERSION) +(\"(.*?)\")', text).group(3)
|
|
||||||
|
|
||||||
def get_app_name(text):
|
|
||||||
return re.search(r'(APP_NAME) +(\"(.*?)\")', text).group(3)
|
|
||||||
|
|
||||||
def get_qt_path():
|
|
||||||
try:
|
|
||||||
return str(subprocess.check_output(["qtpaths", "--binaries-dir"]), 'utf-8').strip()
|
|
||||||
|
|
||||||
except:
|
|
||||||
print("A direct call to 'qtpaths' has failed so automatic retrieval of the QT bin folder is not possible.")
|
|
||||||
|
|
||||||
return input("Please enter the QT bin path (leave blank to cancel the build): ")
|
|
||||||
|
|
||||||
def get_qt_from_cli():
|
|
||||||
for arg in sys.argv:
|
|
||||||
if arg == "-qt_dir":
|
|
||||||
index = sys.argv.index(arg)
|
|
||||||
|
|
||||||
try:
|
|
||||||
return sys.argv[index + 1]
|
|
||||||
|
|
||||||
except:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_ver_header():
|
|
||||||
current_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
if current_dir == "":
|
|
||||||
return "src" + os.sep + "common.h"
|
|
||||||
else:
|
|
||||||
return current_dir + os.sep + "src" + os.sep + "common.h"
|
|
||||||
|
|
||||||
def get_nearest_subdir(path, sub_name):
|
|
||||||
dir_list = os.listdir(path)
|
|
||||||
ret = ""
|
|
||||||
|
|
||||||
for entry in dir_list:
|
|
||||||
if sub_name in entry:
|
|
||||||
ret = entry
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def get_maker(qt_path):
|
|
||||||
ret = ""
|
|
||||||
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
ret = "make"
|
|
||||||
|
|
||||||
elif platform.system() == "Windows":
|
|
||||||
path = os.path.abspath(qt_path + "\\..")
|
|
||||||
name = os.path.basename(path)
|
|
||||||
|
|
||||||
if "mingw" in name:
|
|
||||||
tools_path = os.path.abspath(qt_path + "\\..\\..\\..\\Tools")
|
|
||||||
mingw_ver = name[5:7]
|
|
||||||
mingw_tool_subdir = get_nearest_subdir(tools_path, "mingw" + mingw_ver)
|
|
||||||
mingw_tool_path = tools_path + "\\" + mingw_tool_subdir + "\\bin"
|
|
||||||
|
|
||||||
if not os.environ['PATH'].endswith(";"):
|
|
||||||
os.environ['PATH'] = os.environ['PATH'] + ";"
|
|
||||||
|
|
||||||
os.environ['PATH'] = os.environ['PATH'] + mingw_tool_path
|
|
||||||
|
|
||||||
ret = "mingw32-make"
|
|
||||||
|
|
||||||
elif "msvc" in name:
|
|
||||||
print("Warning: this script will assume you already ran the VsDevCmd.bat or vsvars32.bat script files")
|
|
||||||
print(" for Microsoft Visual Studio. Either way, a call to 'nmake' should be recognizable as ")
|
|
||||||
print(" a shell command otherwise this script will fail.\n")
|
|
||||||
|
|
||||||
ans = input("If that is the case enter 'y' to continue or any other key to cancel the build: ")
|
|
||||||
|
|
||||||
if ans == 'y' or ans == 'Y':
|
|
||||||
ret = "nmake"
|
|
||||||
|
|
||||||
else:
|
|
||||||
exit()
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("The system platform is unknown. Output from platform.system() = " + platform.system())
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def cd():
|
|
||||||
current_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
if current_dir != "":
|
|
||||||
os.chdir(current_dir)
|
|
||||||
|
|
||||||
def verbose_copy(src, dst):
|
|
||||||
print("cpy: " + src + " --> " + dst)
|
|
||||||
|
|
||||||
if os.path.isdir(src):
|
|
||||||
if os.path.exists(dst) and os.path.isdir(dst):
|
|
||||||
shutil.rmtree(dst)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# ignore errors thrown by shutil.copytree()
|
|
||||||
# it's likely not actually failing to copy
|
|
||||||
# the directory but still throws errors if
|
|
||||||
# it fails to apply the same file stats as
|
|
||||||
# the source. this type of errors can be
|
|
||||||
# ignored.
|
|
||||||
shutil.copytree(src, dst)
|
|
||||||
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
elif os.path.exists(src):
|
|
||||||
shutil.copyfile(src, dst)
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("wrn: " + src + " does not exists. skipping.")
|
|
||||||
|
|
||||||
def linux_build_app_dir(app_ver, app_name, app_target, qt_bin):
|
|
||||||
if not os.path.exists("app_dir/linux/platforms"):
|
|
||||||
os.makedirs("app_dir/linux/platforms")
|
|
||||||
|
|
||||||
if not os.path.exists("app_dir/linux/xcbglintegrations"):
|
|
||||||
os.makedirs("app_dir/linux/xcbglintegrations")
|
|
||||||
|
|
||||||
if not os.path.exists("app_dir/linux/lib"):
|
|
||||||
os.makedirs("app_dir/linux/lib")
|
|
||||||
|
|
||||||
if not os.path.exists("app_dir/icons"):
|
|
||||||
os.makedirs("app_dir/icons")
|
|
||||||
|
|
||||||
verbose_copy(qt_bin + "/../plugins/platforms/libqxcb.so", "app_dir/linux/platforms/libqxcb.so")
|
|
||||||
verbose_copy(qt_bin + "/../plugins/xcbglintegrations/libqxcb-egl-integration.so", "app_dir/linux/xcbglintegrations/libqxcb-egl-integration.so")
|
|
||||||
verbose_copy(qt_bin + "/../plugins/xcbglintegrations/libqxcb-glx-integration.so", "app_dir/linux/xcbglintegrations/libqxcb-glx-integration.so")
|
|
||||||
verbose_copy("build/linux/" + app_target, "app_dir/linux/" + app_target)
|
|
||||||
verbose_copy("icons", "app_dir/icons")
|
|
||||||
|
|
||||||
shutil.copyfile("build/linux/" + app_target, "/tmp/" + app_target)
|
|
||||||
# copying the executable file from the build folder to
|
|
||||||
# temp bypasses any -noexe retrictions a linux file
|
|
||||||
# system may have. there is a chance temp is also
|
|
||||||
# restricted in this way but that kind of config is
|
|
||||||
# rare. ldd will not run correctly with -noexe
|
|
||||||
# enabled.
|
|
||||||
|
|
||||||
lines = str(subprocess.check_output(["ldd", "/tmp/" + app_target]), 'utf-8').split("\n")
|
|
||||||
|
|
||||||
os.remove("/tmp/" + app_target)
|
|
||||||
|
|
||||||
for line in lines:
|
|
||||||
if " => " in line:
|
|
||||||
if ("libQt" in line) or ("libicu" in line) or ("libssl" in line) or ("libcrypto" in line) or ("libGL.so" in line) or ("libpcre16.so" in line) or ("libpcre.so" in line):
|
|
||||||
if " (0x0" in line:
|
|
||||||
start_index = line.index("> ") + 2
|
|
||||||
end_index = line.index(" (0x0")
|
|
||||||
src_file = line[start_index:end_index]
|
|
||||||
file_name = os.path.basename(src_file)
|
|
||||||
|
|
||||||
verbose_copy(src_file, "app_dir/linux/lib/" + file_name)
|
|
||||||
|
|
||||||
if "/usr/lib/x86_64-linux-gnu/qt5/bin" == qt_bin:
|
|
||||||
verbose_copy(qt_bin + "/../../libQt5DBus.so.5", "app_dir/linux/lib/libQt5DBus.so.5")
|
|
||||||
verbose_copy(qt_bin + "/../../libQt5XcbQpa.so.5", "app_dir/linux/lib/libQt5XcbQpa.so.5")
|
|
||||||
verbose_copy(qt_bin + "/../../libQt6DBus.so.6", "app_dir/linux/lib/libQt6DBus.so.6")
|
|
||||||
verbose_copy(qt_bin + "/../../libQt6XcbQpa.so.6", "app_dir/linux/lib/libQt6XcbQpa.so.6")
|
|
||||||
|
|
||||||
else:
|
|
||||||
verbose_copy(qt_bin + "/../lib/libQt5DBus.so.5", "app_dir/linux/lib/libQt5DBus.so.5")
|
|
||||||
verbose_copy(qt_bin + "/../lib/libQt5XcbQpa.so.5", "app_dir/linux/lib/libQt5XcbQpa.so.5")
|
|
||||||
verbose_copy(qt_bin + "/../lib/libQt6DBus.so.6", "app_dir/linux/lib/libQt6DBus.so.6")
|
|
||||||
verbose_copy(qt_bin + "/../lib/libQt6XcbQpa.so.6", "app_dir/linux/lib/libQt6XcbQpa.so.6")
|
|
||||||
verbose_copy(qt_bin + "/../lib/libQt6OpenGL.so.6", "app_dir/linux/lib/libQt6OpenGL.so.6")
|
|
||||||
|
|
||||||
verbose_copy("templates/linux_run_script.sh", "app_dir/linux/" + app_target + ".sh")
|
|
||||||
verbose_copy("templates/linux_uninstall.sh", "app_dir/linux/uninstall.sh")
|
|
||||||
verbose_copy("templates/linux_icon.desktop", "app_dir/linux/" + app_target + ".desktop")
|
|
||||||
|
|
||||||
complete(app_ver, app_target)
|
|
||||||
|
|
||||||
def windows_build_app_dir(app_ver, app_name, app_target, qt_bin):
|
|
||||||
if os.path.exists("release"):
|
|
||||||
os.removedirs("release")
|
|
||||||
|
|
||||||
if os.path.exists("debug"):
|
|
||||||
os.removedirs("debug")
|
|
||||||
|
|
||||||
if not os.path.exists("app_dir\\windows"):
|
|
||||||
os.makedirs("app_dir\\windows")
|
|
||||||
|
|
||||||
if not os.path.exists("app_dir\\icons"):
|
|
||||||
os.makedirs("app_dir\\icons")
|
|
||||||
|
|
||||||
verbose_copy("icons", "app_dir\\icons")
|
|
||||||
verbose_copy("build\\windows\\" + app_target + ".exe", "app_dir\\windows\\" + app_target + ".exe")
|
|
||||||
verbose_copy("templates\\windows_uninstall.bat", "app_dir\\windows\\uninstall.bat")
|
|
||||||
verbose_copy("templates\\win_icon.vbs", "app_dir\\windows\\icon.vbs")
|
|
||||||
|
|
||||||
os.chdir("app_dir\\windows\\")
|
|
||||||
|
|
||||||
result = subprocess.run([qt_bin + "\\" + "windeployqt", app_target + ".exe"])
|
|
||||||
|
|
||||||
cd()
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
complete(app_ver, app_target)
|
|
||||||
|
|
||||||
def complete(app_ver, app_target):
|
|
||||||
if os.path.exists("Makefile"):
|
|
||||||
os.remove("Makefile")
|
|
||||||
|
|
||||||
if os.path.exists("Makefile.Debug"):
|
|
||||||
os.remove("Makefile.Debug")
|
|
||||||
|
|
||||||
if os.path.exists("Makefile.Release"):
|
|
||||||
os.remove("Makefile.Release")
|
|
||||||
|
|
||||||
if os.path.exists("object_script." + app_target + ".Debug"):
|
|
||||||
os.remove("object_script." + app_target + ".Debug")
|
|
||||||
|
|
||||||
if os.path.exists("object_script." + app_target + ".Release"):
|
|
||||||
os.remove("object_script." + app_target + ".Release")
|
|
||||||
|
|
||||||
print("Build complete for version: " + app_ver)
|
|
||||||
print("You can now run the install.py script to install onto this machine or create an installer.")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
with open(get_ver_header()) as file:
|
|
||||||
text = file.read()
|
|
||||||
|
|
||||||
app_target = get_app_target(text)
|
|
||||||
app_ver = get_app_ver(text)
|
|
||||||
app_name = get_app_name(text)
|
|
||||||
qt_bin = get_qt_from_cli()
|
|
||||||
|
|
||||||
if qt_bin == "":
|
|
||||||
qt_bin = get_qt_path()
|
|
||||||
|
|
||||||
maker = get_maker(qt_bin)
|
|
||||||
|
|
||||||
if qt_bin != "":
|
|
||||||
print("app_target = " + app_target)
|
|
||||||
print("app_version = " + app_ver)
|
|
||||||
print("app_name = " + app_name)
|
|
||||||
print("qt_bin = " + qt_bin)
|
|
||||||
print("maker = " + maker + "\n")
|
|
||||||
|
|
||||||
if maker == "":
|
|
||||||
print("Could not find a valid maker/compiler on this platform, unable to continue.")
|
|
||||||
|
|
||||||
else:
|
|
||||||
cd()
|
|
||||||
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
if os.path.exists("build/linux"):
|
|
||||||
shutil.rmtree("build/linux")
|
|
||||||
|
|
||||||
elif platform.system() == "Windows":
|
|
||||||
if os.path.exists("build/windows"):
|
|
||||||
shutil.rmtree("build/windows")
|
|
||||||
|
|
||||||
result = subprocess.run([qt_bin + os.sep + "qmake", "-config", "release"])
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
result = subprocess.run([maker])
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
if not os.path.exists("app_dir"):
|
|
||||||
os.makedirs("app_dir")
|
|
||||||
|
|
||||||
with open("app_dir" + os.sep + "info.txt", "w") as info_file:
|
|
||||||
info_file.write(app_target + "\n")
|
|
||||||
info_file.write(app_ver + "\n")
|
|
||||||
info_file.write(app_name + "\n")
|
|
||||||
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
linux_build_app_dir(app_ver, app_name, app_target, qt_bin)
|
|
||||||
|
|
||||||
elif platform.system() == "Windows":
|
|
||||||
windows_build_app_dir(app_ver, app_name, app_target, qt_bin)
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("The platform you are running in is not compatible with the app_dir build out procedure.")
|
|
||||||
print(" output from platform.system() = " + platform.system())
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
120
docs/README.md
Normal file
120
docs/README.md
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
# Cmdr #
|
||||||
|
|
||||||
|
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 ###
|
||||||
|
|
||||||
|
Cmdr have it's own terminal display so there is no command line switches to pass on start up. Instead, all commands are parsed and processed within it's own terminal interface. Any commands not seen as an internal command for the client itself is passed through to the MRCI host if connected. The client will add a numeric identifer to the end of all host command names that clash with the client commands so there is no chance a client and host command can conflict with each other.
|
||||||
|
|
||||||
|
### Versioning System ###
|
||||||
|
|
||||||
|
This application uses a 2 number versioning system: [major].[minor]
|
||||||
|
|
||||||
|
* 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 config files or libraries.
|
||||||
|
|
||||||
|
Any increments to major resets minor to 0.
|
||||||
|
|
||||||
|
### The Protocol ###
|
||||||
|
|
||||||
|
This application being a MRCI client uses the MRCI protocol to transport data to and from the host using TCP in a binary data format called MRCI frames. In general, local connections with the host are not encrypted but all connections outside of the host must be encrypted using TLS/SSL (including the local network).
|
||||||
|
|
||||||
|
Before any MRCI frames can be transported, both the host and client need basic information about each other. This is done by having the client send a fixed length client header when it successfully connects to the host and the host will reply with it's own fixed length host header, described below.
|
||||||
|
|
||||||
|
### MRCI Frame Format ###
|
||||||
|
|
||||||
|
```
|
||||||
|
[type_id][cmd_id][branch_id][data_len][payload]
|
||||||
|
|
||||||
|
type_id - 1byte - 8bit little endian integer type id of the payload.
|
||||||
|
cmd_id - 2bytes - 16bit little endian integer command id.
|
||||||
|
branch_id - 2bytes - 16bit little endian integer branch id.
|
||||||
|
data_len - 3bytes - 24bit little endian integer size of the payload.
|
||||||
|
payload - variable - the actual data to be processed.
|
||||||
|
```
|
||||||
|
|
||||||
|
notes:
|
||||||
|
|
||||||
|
* A full description of the type id's can be found in the [Type_IDs.md](type_ids.md) document.
|
||||||
|
|
||||||
|
* This client call commands by name but the host will assign unique command ids for it's command names and will require the client to use the command id on calling. To track this command id to command name relationship for the host commands, this client will rely on the [ASYNC_ADD_CMD](async.md) and [ASYNC_RM_CMD](async.md) async commands.
|
||||||
|
|
||||||
|
* The branch id is an id that can be used to run muliple instances of the same command on the host. Commands sent by a certain branch id will result in data sent back with that same branch id. For now, this client does not do branching; instead all commands sent to the host using branch id 0 only.
|
||||||
|
|
||||||
|
### Client Header (This Application) ###
|
||||||
|
|
||||||
|
```
|
||||||
|
[tag][appName][coName]
|
||||||
|
|
||||||
|
tag - 4bytes - 0x4D, 0x52, 0x43, 0x49 (MRCI)
|
||||||
|
appName - 134bytes - UTF16LE string (padded with 0x00)
|
||||||
|
coName - 272bytes - UTF16LE string (padded with 0x00)
|
||||||
|
```
|
||||||
|
|
||||||
|
notes:
|
||||||
|
|
||||||
|
* The **tag** is just a fixed ascii string "MRCI" that indicates to the host that the client is indeed attempting to use the MRCI protocol.
|
||||||
|
|
||||||
|
* 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. 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 ###
|
||||||
|
|
||||||
|
```
|
||||||
|
Format:
|
||||||
|
|
||||||
|
[reply][major][minor][tcp_rev][mod_rev][sesId]
|
||||||
|
|
||||||
|
reply - 1byte - 8bit little endian unsigned int
|
||||||
|
major - 2bytes - 16bit little endian unsigned int
|
||||||
|
minor - 2bytes - 16bit little endian unsigned int
|
||||||
|
tcp_rev - 2bytes - 16bit little endian unsigned int
|
||||||
|
mod_rev - 2bytes - 16bit little endian unsigned int
|
||||||
|
sesId - 28bytes - 224bit sha3 hash
|
||||||
|
```
|
||||||
|
|
||||||
|
notes:
|
||||||
|
|
||||||
|
* **reply** is a numeric value that the host returns in it's header to communicate to the client the result of it's evaluation of the client's header.
|
||||||
|
|
||||||
|
* reply = 1, means the client is acceptable and it does not need to take any further action.
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
* **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 if it supports the host at all. If not supported, the client can simply disconnect from 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.
|
||||||
|
|
||||||
|
### Async Commands ###
|
||||||
|
|
||||||
|
Async commands are 'virtual commands' that this application can encounter at any time while connected to the host. More information about this can be found in the [Async.md](Async.md) document. This application does act on some of the data carried by the async commands but not all of them.
|
||||||
|
|
||||||
|
### Development Setup ###
|
||||||
|
|
||||||
|
Linux Required Packages:
|
||||||
|
```
|
||||||
|
qtbase5-dev
|
||||||
|
libssl-dev
|
||||||
|
gcc
|
||||||
|
make
|
||||||
|
makeself
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build From Source (Linux) ###
|
||||||
|
|
||||||
|
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.
|
||||||
|
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:
|
||||||
|
```
|
||||||
|
cd /path/to/source/code
|
||||||
|
sh ./linux_build.sh
|
||||||
|
```
|
||||||
|
Install:
|
||||||
|
```
|
||||||
|
chmod +x ./cmdr-x.x.run
|
||||||
|
./cmdr-x.x.run
|
||||||
|
```
|
|
@ -220,7 +220,7 @@ to_client: [type_id(25)][cmd_id(28)][branch_id(0)][size_of_payload][payload(CH_M
|
||||||
```
|
```
|
||||||
|
|
||||||
```ASYNC_RM_CH_MEMBER (29)```
|
```ASYNC_RM_CH_MEMBER (29)```
|
||||||
This async command carries a combination of the channel id and the 32byte user id of the channel member that has left or kicked from the channel member list. All sessions that are logged in as a member of the channel forwards the data to the clients as a [BYTES](type_ids.md) frame. This async aommand also sent when a user declines an invite to a channel.
|
This async command carries a combination of the channel id and the 32byte user id of the channel member that has left or kicked from the channel member list. All sessions that are logged in as a member of the channel forwards the data to the clients as a [BYTES](type_ids.md) frame.
|
||||||
```
|
```
|
||||||
from_module: [8bytes(chId)][32bytes(userId)]
|
from_module: [8bytes(chId)][32bytes(userId)]
|
||||||
to_client: [type_id(14)][cmd_id(29)][branch_id(0)][size_of_payload][payload(64bit_ch_id + 256bit_user_id)]
|
to_client: [type_id(14)][cmd_id(29)][branch_id(0)][size_of_payload][payload(64bit_ch_id + 256bit_user_id)]
|
||||||
|
|
|
@ -10,6 +10,7 @@ enum TypeID : quint8
|
||||||
ERR = 3,
|
ERR = 3,
|
||||||
PRIV_TEXT = 4,
|
PRIV_TEXT = 4,
|
||||||
IDLE = 5,
|
IDLE = 5,
|
||||||
|
HOST_CERT = 6,
|
||||||
FILE_INFO = 7,
|
FILE_INFO = 7,
|
||||||
PEER_INFO = 8,
|
PEER_INFO = 8,
|
||||||
MY_INFO = 9,
|
MY_INFO = 9,
|
||||||
|
@ -42,7 +43,7 @@ enum TypeID : quint8
|
||||||
```TEXT```
|
```TEXT```
|
||||||
This is text that can be displayed directly to the user, passed as command line arguments to be processed or used to carry text data within other data types.
|
This is text that can be displayed directly to the user, passed as command line arguments to be processed or used to carry text data within other data types.
|
||||||
|
|
||||||
format: ```[UTF-8_string] (no BOM)```
|
format: ```[UTF-16LE_string] (no BOM)```
|
||||||
|
|
||||||
```GEN_FILE```
|
```GEN_FILE```
|
||||||
This is a file transfer type id that can be used to transfer any file type (music, photos, documents, etc...). It operates in its own protocol of sorts. The 1st GEN_FILE frame received by the host or client is TEXT parameters similar to what you see in terminal command lines with at least one of the arguments listed below. The next set of GEN_FILE frames received by the host or client is then the binary data that needs to be written to an open file or streamed until the limit defined in -len is meet.
|
This is a file transfer type id that can be used to transfer any file type (music, photos, documents, etc...). It operates in its own protocol of sorts. The 1st GEN_FILE frame received by the host or client is TEXT parameters similar to what you see in terminal command lines with at least one of the arguments listed below. The next set of GEN_FILE frames received by the host or client is then the binary data that needs to be written to an open file or streamed until the limit defined in -len is meet.
|
||||||
|
@ -77,7 +78,7 @@ This id can be treated exactly like TEXT except this tells the client that the c
|
||||||
This is similar to PRIV_TEXT expect it is not asking for private information. It is simply prompting for non-sensitive information from the user.
|
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 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```
|
||||||
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.
|
All commands started during the session returns this type id when it has finished it's task. It carries a 16bit unsigned integer indicating the result of the task that the command was running.
|
||||||
|
@ -97,13 +98,13 @@ enum RetCode : quint16
|
||||||
notes:
|
notes:
|
||||||
1. the custom return code can be additional data added to the end of the 16bit
|
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
|
integer that can carry additional data about the result of the task. it can
|
||||||
be any format that the module itself decides it to be. nothing is stopping
|
be any format that the module itself decides it can should be. nothing is
|
||||||
modules from defining return codes beyond the value of 7 but it is advised
|
stopping modules from defining return codes beyond the value of 7 but it is
|
||||||
not to because this enum might be expanded in the future.
|
advised not to because this enum might be expanded in the future.
|
||||||
```
|
```
|
||||||
|
|
||||||
```TERM_CMD```
|
```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 will only tell it to stop what it is currently doing. This will also terminate any commands in a prompt state.
|
This type id doesn't carry any actual data. It is used to tell the host to stop/terminate the command id and branch id that was used to send it. It does not actually terminate the module's process within the host, it 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 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.
|
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.
|
||||||
|
@ -114,6 +115,9 @@ This type id doesn't carry any actual data, instead can be used to tell the host
|
||||||
```RESUME_CMD```
|
```RESUME_CMD```
|
||||||
This is the other half of YIELD_CMD that tells the host to resume the command that was running.
|
This is the other half of YIELD_CMD that tells the host to resume the command that was running.
|
||||||
|
|
||||||
|
```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.
|
||||||
|
|
||||||
```HOST_VER```
|
```HOST_VER```
|
||||||
This data structure carries 4 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).
|
||||||
|
|
||||||
|
@ -143,12 +147,12 @@ This is a data structure that carries information about a file system object (fi
|
||||||
2. bytes[1-8] - creation time in msec since Epoch UTC (64bit little endian uint)
|
2. bytes[1-8] - creation time in msec since Epoch UTC (64bit little endian uint)
|
||||||
3. bytes[9-16] - modification time in msec since Epoch UTC (64bit little endian uint)
|
3. bytes[9-16] - modification time in msec since Epoch UTC (64bit little endian uint)
|
||||||
4. bytes[17-24] - file size (64bit little endian uint)
|
4. bytes[17-24] - file size (64bit little endian uint)
|
||||||
5. bytes[25-n] - file name (UTF8 string, NULL terminated)
|
5. bytes[25-n] - file name (UTF16-LE string, 16bit terminated)
|
||||||
6. bytes[n-n] - symmlink target if it is a symmlink (UTF8 string, NULL terminated)
|
6. bytes[n-n] - symmlink target if it is a symmlink (UTF16-LE string, 16bit terminated)
|
||||||
|
|
||||||
notes:
|
notes:
|
||||||
1. NULL terminated UTF-8 strings are basically tailed
|
1. 16bit terminated UTF-16LE strings are basically
|
||||||
by a single 0x00 byte to indicate the end-of-string.
|
terminated by 2 bytes of 0x00.
|
||||||
|
|
||||||
2. the symmlink target is empty if not a symmlink but
|
2. the symmlink target is empty if not a symmlink but
|
||||||
the terminator should still be present.
|
the terminator should still be present.
|
||||||
|
@ -171,9 +175,9 @@ This carry some user account and session information about a peer client connect
|
||||||
format:
|
format:
|
||||||
1. bytes[0-27] 28bytes - session id (224bit hash)
|
1. bytes[0-27] 28bytes - session id (224bit hash)
|
||||||
2. bytes[28-59] 32bytes - user id (256bit hash)
|
2. bytes[28-59] 32bytes - user id (256bit hash)
|
||||||
3. bytes[60-83] 24bytes - user name (TEXT - padded with 0x00)
|
3. bytes[60-107] 48bytes - user name (TEXT - padded with 0x00)
|
||||||
4. bytes[84-115] 32bytes - app name (TEXT - padded with 0x00)
|
4. bytes[108-235] 128bytes - app name (TEXT - padded with 0x00)
|
||||||
5. bytes[116-139] 24bytes - disp name (TEXT - padded with 0x00)
|
5. bytes[236-299] 64bytes - disp name (TEXT - padded with 0x00)
|
||||||
|
|
||||||
notes:
|
notes:
|
||||||
1. the session id is unique to the peer's session connection only. it
|
1. the session id is unique to the peer's session connection only. it
|
||||||
|
@ -183,23 +187,23 @@ This carry some user account and session information about a peer client connect
|
||||||
even when the user name changes and across all clients logged into
|
even when the user name changes and across all clients logged into
|
||||||
the same account.
|
the same account.
|
||||||
|
|
||||||
3. the display name is the preferred display name of the peer. clients
|
3. the display name is the preffered display name of the peer. clients
|
||||||
are encouraged to use this rather than the user name when displaying
|
are encouraged to use this rather than the user name when displaying
|
||||||
peer info to the end user. if empty, then it is ok to fall back to
|
peer info to the user. if empty, it's ok to just fall back to the user
|
||||||
displaying the user name.
|
name.
|
||||||
```
|
```
|
||||||
|
|
||||||
```PING_PEERS```
|
```PING_PEERS```
|
||||||
This is formatted extactly as PEER_INFO except it is used by the ASYNC_LIMITED_CAST [async](async.md) command to tell all peer sessions that receive it to forward the same frame to their own clients and return PEER_INFO frames about themselves to the session that sent the PING_PEERS.
|
This is formatted extactly as PEER_INFO except it is used by the ASYNC_LIMITED_CAST [async](async.md) command to tell all peer sessions that receive it to send a PEER_INFO frame about the local session to their own clients and return PEER_INFO frames about themselves to the local session.
|
||||||
|
|
||||||
```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:
|
||||||
|
|
||||||
```
|
```
|
||||||
format:
|
format:
|
||||||
1. bytes[140-203] 64bytes - email (TEXT - padded with 0x00)
|
1. bytes[300-427] 128bytes - email (TEXT - padded with 0x00)
|
||||||
2. bytes[204-207] 4bytes - host rank (32bit unsigned int)
|
2. bytes[428-431] 4bytes - host rank (32bit unsigned int)
|
||||||
3. bytes[208] 1byte - is email confirmed? (0x00 false, 0x01 true)
|
3. bytes[432] 1byte - is email confirmed? (0x00 false, 0x01 true)
|
||||||
```
|
```
|
||||||
|
|
||||||
```NEW_CMD```
|
```NEW_CMD```
|
||||||
|
@ -209,11 +213,11 @@ This contains information about a new command that was added to the current sess
|
||||||
format:
|
format:
|
||||||
1. bytes[0-1] 2bytes - 16bit LE unsigned int (command id)
|
1. bytes[0-1] 2bytes - 16bit LE unsigned int (command id)
|
||||||
2. bytes[2] 1byte - 8bit LE unsigned int (genfile type)
|
2. bytes[2] 1byte - 8bit LE unsigned int (genfile type)
|
||||||
3. bytes[3-66] 64bytes - command name (TEXT - padded with 0x00)
|
3. bytes[3-130] 128bytes - command name (TEXT - padded with 0x00)
|
||||||
4. bytes[67-130] 64bytes - library name (TEXT - padded with 0x00)
|
4. bytes[131-258] 128bytes - library name (TEXT - padded with 0x00)
|
||||||
5. bytes[131-n] variable - short text (null terminated)
|
5. bytes[259-n] variable - short text (16bit null terminated)
|
||||||
6. bytes[n-n] variable - io text (null terminated)
|
6. bytes[n-n] variable - io text (16bit null terminated)
|
||||||
7. bytes[n-n] variable - long text (null terminated)
|
7. bytes[n-n] variable - long text (16bit null terminated)
|
||||||
|
|
||||||
notes:
|
notes:
|
||||||
1. the genfile type is numerical value of 2, 3 or 0. a value of 2
|
1. the genfile type is numerical value of 2, 3 or 0. a value of 2
|
||||||
|
@ -288,13 +292,13 @@ This contains public information about a channel member.
|
||||||
2. bytes[8-39] 32bytes - user id (256bit hash)
|
2. bytes[8-39] 32bytes - user id (256bit hash)
|
||||||
3. bytes[40] 1byte - is invite? (0x00=false, 0x01=true)
|
3. bytes[40] 1byte - is invite? (0x00=false, 0x01=true)
|
||||||
4. bytes[41] 1byte - member's channel privilege level (8bit unsigned int)
|
4. bytes[41] 1byte - member's channel privilege level (8bit unsigned int)
|
||||||
5. bytes[42-n] variable - user name (TEXT - null terminated)
|
5. bytes[42-n] variable - user name (TEXT - 16bit null terminated)
|
||||||
6. bytes[n-n] variable - display name (TEXT - null terminated)
|
6. bytes[n-n] variable - display name (TEXT - 16bit null terminated)
|
||||||
7. bytes[n-n] variable - channel name (TEXT - null terminated)
|
7. bytes[n-n] variable - channel name (TEXT - 16bit null terminated)
|
||||||
|
|
||||||
notes:
|
notes:
|
||||||
1. a null terminated TEXT formatted string ends with a 0x00 to
|
1. a 16bit null terminated TEXT formatted string ended with 2 bytes of
|
||||||
indicate the end of the string.
|
(0x00) to indicate the end of the string data.
|
||||||
|
|
||||||
2. the member's privilege level can be any of the values discribed in
|
2. the member's privilege level can be any of the values discribed in
|
||||||
section [4.3](host_features.md).
|
section [4.3](host_features.md).
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 16 KiB |
372
install.py
372
install.py
|
@ -1,372 +0,0 @@
|
||||||
#!/usr/bin/python3
|
|
||||||
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import shutil
|
|
||||||
import platform
|
|
||||||
import sys
|
|
||||||
import zipfile
|
|
||||||
import binascii
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
def cd():
|
|
||||||
current_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
if current_dir != "":
|
|
||||||
os.chdir(current_dir)
|
|
||||||
|
|
||||||
def get_default_install_dir(app_target, app_name):
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
return "/opt/" + app_target
|
|
||||||
|
|
||||||
else:
|
|
||||||
return os.environ['ProgramFiles'] + "\\" + app_name
|
|
||||||
|
|
||||||
def get_default_installer_path(app_ver, app_name):
|
|
||||||
return os.path.expanduser("~") + os.sep + app_name + "-" + app_ver + ".py"
|
|
||||||
|
|
||||||
def get_install_dir(app_target, app_name):
|
|
||||||
path = get_default_install_dir(app_target, app_name)
|
|
||||||
|
|
||||||
print("The default install directory is: " + path)
|
|
||||||
|
|
||||||
while(True):
|
|
||||||
ans = input("Do you want to change it? (y/n): ")
|
|
||||||
|
|
||||||
if ans == "y" or ans == "Y":
|
|
||||||
path = input("Enter a new install directory (leave blank to go back to the default): ")
|
|
||||||
path = os.path.normpath(path)
|
|
||||||
break
|
|
||||||
|
|
||||||
elif ans == "n" or ans == "N":
|
|
||||||
break
|
|
||||||
|
|
||||||
if path == "":
|
|
||||||
return get_default_install_dir(app_target, app_name)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return path
|
|
||||||
|
|
||||||
def get_installer_path(app_ver, app_name):
|
|
||||||
path = get_default_installer_path(app_ver, app_name)
|
|
||||||
|
|
||||||
print("The built .py installer will placed here: " + path)
|
|
||||||
|
|
||||||
while(True):
|
|
||||||
ans = input("Do you want to change the path? (y/n): ")
|
|
||||||
|
|
||||||
if ans == "y" or ans == "Y":
|
|
||||||
path = input("Enter a new path (leave blank to go back to the default): ")
|
|
||||||
path = os.path.normpath(path)
|
|
||||||
break
|
|
||||||
|
|
||||||
elif ans == "n" or ans == "N":
|
|
||||||
break
|
|
||||||
|
|
||||||
if path == "":
|
|
||||||
return get_default_installer_path(app_ver, app_name)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return path
|
|
||||||
|
|
||||||
def make_install_dir(path):
|
|
||||||
try:
|
|
||||||
if not os.path.exists(path):
|
|
||||||
os.makedirs(path)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
except:
|
|
||||||
print("Failed to create the install directory, please make sure you are runnning this script with admin rights.")
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def replace_bin(binary, old_bin, new_bin, offs):
|
|
||||||
while(True):
|
|
||||||
try:
|
|
||||||
index = binary.index(old_bin, offs)
|
|
||||||
binary = binary[:index] + new_bin + binary[index + len(old_bin):]
|
|
||||||
|
|
||||||
except ValueError:
|
|
||||||
break
|
|
||||||
|
|
||||||
return binary
|
|
||||||
|
|
||||||
def bin_sub_copy_file(src, dst, old_bin, new_bin, offs):
|
|
||||||
binary = bytearray()
|
|
||||||
|
|
||||||
with open(src, "rb") as rd_file:
|
|
||||||
binary = rd_file.read()
|
|
||||||
binary = replace_bin(binary, old_bin, new_bin, offs)
|
|
||||||
|
|
||||||
with open(dst, "wb") as wr_file:
|
|
||||||
wr_file.write(binary)
|
|
||||||
|
|
||||||
def text_sub_copy_file(src, dst, old_text, new_text, offs):
|
|
||||||
bin_sub_copy_file(src, dst, old_text.encode("utf-8"), new_text.encode("utf-8"), offs)
|
|
||||||
|
|
||||||
def text_template_deploy(src, dst, install_dir, app_name, app_target):
|
|
||||||
print("dep: " + dst)
|
|
||||||
|
|
||||||
text_sub_copy_file(src, dst, "$install_dir", install_dir, 0)
|
|
||||||
text_sub_copy_file(dst, dst, "$app_name", app_name, 0)
|
|
||||||
text_sub_copy_file(dst, dst, "$app_target", app_target, 0)
|
|
||||||
|
|
||||||
def verbose_copy(src, dst):
|
|
||||||
print("cpy: " + src + " --> " + dst)
|
|
||||||
|
|
||||||
if os.path.isdir(src):
|
|
||||||
files = os.listdir(src)
|
|
||||||
|
|
||||||
if not os.path.exists(dst):
|
|
||||||
os.makedirs(dst)
|
|
||||||
|
|
||||||
for file in files:
|
|
||||||
tree_src = src + os.path.sep + file
|
|
||||||
tree_dst = dst + os.path.sep + file
|
|
||||||
|
|
||||||
if os.path.isdir(tree_src):
|
|
||||||
if not os.path.exists(tree_dst):
|
|
||||||
os.makedirs(tree_dst)
|
|
||||||
|
|
||||||
verbose_copy(tree_src, tree_dst)
|
|
||||||
|
|
||||||
else:
|
|
||||||
shutil.copyfile(src, dst)
|
|
||||||
|
|
||||||
def verbose_create_symmlink(src, dst):
|
|
||||||
print("lnk: " + src + " --> " + dst)
|
|
||||||
|
|
||||||
if os.path.exists(dst):
|
|
||||||
os.remove(dst)
|
|
||||||
|
|
||||||
os.symlink(src, dst)
|
|
||||||
|
|
||||||
def local_install(app_target, app_name):
|
|
||||||
if platform.system() == "Linux":
|
|
||||||
if not os.path.exists("app_dir/linux"):
|
|
||||||
print("An app_dir for the Linux platform could not be found.")
|
|
||||||
|
|
||||||
else:
|
|
||||||
install_dir = get_install_dir(app_target, app_name)
|
|
||||||
|
|
||||||
if os.path.exists(install_dir + "/uninstall.sh"):
|
|
||||||
subprocess.run([install_dir + "/uninstall.sh"])
|
|
||||||
|
|
||||||
if make_install_dir(install_dir):
|
|
||||||
if not os.path.exists("/var/opt/" + app_target):
|
|
||||||
os.makedirs("/var/opt/" + app_target)
|
|
||||||
|
|
||||||
text_template_deploy("app_dir/linux/" + app_target + ".sh", install_dir + "/" + app_target + ".sh", install_dir, app_name, app_target)
|
|
||||||
text_template_deploy("app_dir/linux/uninstall.sh", install_dir + "/uninstall.sh", install_dir, app_name, app_target)
|
|
||||||
text_template_deploy("app_dir/linux/" + app_target + ".desktop", "/usr/share/applications/" + app_target + ".desktop", install_dir, app_name, app_target)
|
|
||||||
|
|
||||||
for image_res in ["8x8", "16x16", "22x22", "24x24", "32x32", "36x36", "42x42", "48x48", "64x64", "192x192", "256x256", "512x512"]:
|
|
||||||
src = "app_dir/icons/" + image_res + ".png"
|
|
||||||
dst = "/usr/share/icons/hicolor/" + image_res
|
|
||||||
|
|
||||||
if os.path.exists(dst):
|
|
||||||
verbose_copy(src, dst + "/apps/" + app_target + ".png")
|
|
||||||
|
|
||||||
subprocess.run(["chmod", "644", dst + "/apps/" + app_target + ".png"])
|
|
||||||
|
|
||||||
verbose_copy("app_dir/icons/scalable.svg", "/usr/share/icons/hicolor/scalable/apps/" + app_target + ".svg")
|
|
||||||
verbose_copy("app_dir/linux/" + app_target, install_dir + "/" + app_target)
|
|
||||||
verbose_copy("app_dir/linux/lib", install_dir + "/lib")
|
|
||||||
verbose_copy("app_dir/linux/platforms", install_dir + "/platforms")
|
|
||||||
verbose_copy("app_dir/linux/xcbglintegrations", install_dir + "/xcbglintegrations")
|
|
||||||
|
|
||||||
verbose_create_symmlink(install_dir + "/" + app_target + ".sh", "/usr/bin/" + app_target)
|
|
||||||
|
|
||||||
subprocess.run(["chmod", "644", "/usr/share/icons/hicolor/scalable/apps/" + app_target + ".svg"])
|
|
||||||
subprocess.run(["chmod", "755", install_dir + "/" + app_target + ".sh"])
|
|
||||||
subprocess.run(["chmod", "755", install_dir + "/" + app_target])
|
|
||||||
subprocess.run(["chmod", "755", install_dir + "/uninstall.sh"])
|
|
||||||
|
|
||||||
print("Installation finished. If you ever need to uninstall this application, run this command with root rights:")
|
|
||||||
print(" sh " + install_dir + "/uninstall.sh\n")
|
|
||||||
|
|
||||||
elif platform.system() == "Windows":
|
|
||||||
if not os.path.exists("app_dir\\windows"):
|
|
||||||
print("An app_dir for the Windows platform could not be found.")
|
|
||||||
|
|
||||||
else:
|
|
||||||
install_dir = get_install_dir(app_target, app_name)
|
|
||||||
|
|
||||||
if os.path.exists(install_dir + "\\uninstall.bat"):
|
|
||||||
subprocess.run([install_dir + "\\uninstall.bat"])
|
|
||||||
|
|
||||||
if os.path.exists(install_dir):
|
|
||||||
# this block is here to make sure the install_dir is deleted
|
|
||||||
# if/when the uninstall.bat fails to do so. in my test machine,
|
|
||||||
# the .bat script will delete install_dir if run directly but
|
|
||||||
# not when called through subprocess.run() for some reason.
|
|
||||||
shutil.rmtree(install_dir)
|
|
||||||
|
|
||||||
if make_install_dir(install_dir):
|
|
||||||
verbose_copy("app_dir\\windows", install_dir)
|
|
||||||
verbose_copy("app_dir\\icons\\win_icon.ico", install_dir + "\\icon.ico")
|
|
||||||
|
|
||||||
text_template_deploy("app_dir\\windows\\uninstall.bat", install_dir + "\\uninstall.bat", install_dir, app_name, app_target)
|
|
||||||
text_template_deploy("app_dir\\windows\\icon.vbs", install_dir + "\\icon.vbs", install_dir, app_name, app_target)
|
|
||||||
text_sub_copy_file(install_dir + "\\icon.vbs", install_dir + "\\icon.vbs", "%SystemDrive%", os.environ['SystemDrive'], 0)
|
|
||||||
|
|
||||||
os.system("\"" + install_dir + "\\icon.vbs\"")
|
|
||||||
|
|
||||||
print("Installation finished. If you ever need to uninstall this application, run this batch file with admin rights:")
|
|
||||||
print(" " + install_dir + "\\uninstall.bat\n")
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("The platform you are running in is not compatible.")
|
|
||||||
print(" output from platform.system() = " + platform.system())
|
|
||||||
|
|
||||||
def dir_tree(path):
|
|
||||||
|
|
||||||
ret = []
|
|
||||||
|
|
||||||
if os.path.isdir(path):
|
|
||||||
for entry in os.listdir(path):
|
|
||||||
full_path = os.path.join(path, entry)
|
|
||||||
|
|
||||||
if os.path.isdir(full_path):
|
|
||||||
for sub_dir_file in dir_tree(full_path):
|
|
||||||
ret.append(sub_dir_file)
|
|
||||||
|
|
||||||
else:
|
|
||||||
ret.append(full_path)
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def to_hex(data):
|
|
||||||
return str(binascii.hexlify(data))[2:-1]
|
|
||||||
|
|
||||||
def from_hex(text_line):
|
|
||||||
return binascii.unhexlify(text_line)
|
|
||||||
|
|
||||||
def make_install(app_ver, app_name):
|
|
||||||
path = get_installer_path(app_ver, app_name)
|
|
||||||
|
|
||||||
with zipfile.ZipFile("app_dir.zip", "w", compression=zipfile.ZIP_DEFLATED) as zip_file:
|
|
||||||
print("Compressing app_dir --")
|
|
||||||
|
|
||||||
for file in dir_tree("app_dir"):
|
|
||||||
print("adding file: " + file)
|
|
||||||
zip_file.write(file)
|
|
||||||
|
|
||||||
text_sub_copy_file(__file__, path, "main(is_sfx=False)", "main(is_sfx=True)\n\n\n", 7700)
|
|
||||||
|
|
||||||
with open(path, "a") as dst_file, open("app_dir.zip", "rb") as src_file:
|
|
||||||
print("Packing the compressed app_dir into the sfx script file --")
|
|
||||||
|
|
||||||
dst_file.write("# APP_DIR\n")
|
|
||||||
|
|
||||||
stat = os.stat("app_dir.zip")
|
|
||||||
|
|
||||||
while(True):
|
|
||||||
buffer = src_file.read(4000000)
|
|
||||||
|
|
||||||
if len(buffer) != 0:
|
|
||||||
dst_file.write("# " + to_hex(buffer) + "\n")
|
|
||||||
|
|
||||||
print(str(src_file.tell()) + "/" + str(stat.st_size))
|
|
||||||
|
|
||||||
if len(buffer) < 4000000:
|
|
||||||
break
|
|
||||||
|
|
||||||
os.remove("app_dir.zip")
|
|
||||||
|
|
||||||
print("Finished.")
|
|
||||||
|
|
||||||
def sfx():
|
|
||||||
abs_sfx_path = os.path.abspath(__file__)
|
|
||||||
mark_found = False
|
|
||||||
|
|
||||||
os.chdir(tempfile.gettempdir())
|
|
||||||
|
|
||||||
with open(abs_sfx_path) as packed_file, open("app_dir.zip", "wb") as zip_file:
|
|
||||||
stat = os.stat(abs_sfx_path)
|
|
||||||
|
|
||||||
print("Unpacking the app_dir compressed file from the sfx script.")
|
|
||||||
|
|
||||||
while(True):
|
|
||||||
line = packed_file.readline()
|
|
||||||
|
|
||||||
if not line:
|
|
||||||
break
|
|
||||||
|
|
||||||
elif mark_found:
|
|
||||||
zip_file.write(from_hex(line[2:-1]))
|
|
||||||
|
|
||||||
print(str(packed_file.tell()) + "/" + str(stat.st_size))
|
|
||||||
|
|
||||||
else:
|
|
||||||
if line == "# APP_DIR\n":
|
|
||||||
mark_found = True
|
|
||||||
|
|
||||||
print("Done.")
|
|
||||||
|
|
||||||
if not mark_found:
|
|
||||||
print("The app_dir mark was not found, unable to continue.")
|
|
||||||
|
|
||||||
else:
|
|
||||||
with zipfile.ZipFile("app_dir.zip", "r", compression=zipfile.ZIP_DEFLATED) as zip_file:
|
|
||||||
print("De-compressing app_dir --")
|
|
||||||
|
|
||||||
zip_file.extractall()
|
|
||||||
|
|
||||||
print("Preparing for installation.")
|
|
||||||
|
|
||||||
os.remove("app_dir.zip")
|
|
||||||
|
|
||||||
with open("app_dir" + os.sep + "info.txt") as info_file:
|
|
||||||
info = info_file.read().split("\n")
|
|
||||||
|
|
||||||
local_install(info[0], info[2])
|
|
||||||
shutil.rmtree("app_dir")
|
|
||||||
|
|
||||||
def main(is_sfx):
|
|
||||||
cd()
|
|
||||||
|
|
||||||
app_target = ""
|
|
||||||
app_ver = ""
|
|
||||||
app_name = ""
|
|
||||||
|
|
||||||
if not is_sfx:
|
|
||||||
with open("app_dir" + os.sep + "info.txt") as info_file:
|
|
||||||
info = info_file.read().split("\n")
|
|
||||||
|
|
||||||
app_target = info[0]
|
|
||||||
app_ver = info[1]
|
|
||||||
app_name = info[2]
|
|
||||||
|
|
||||||
if is_sfx:
|
|
||||||
sfx()
|
|
||||||
|
|
||||||
elif "-local" in sys.argv:
|
|
||||||
local_install(app_target, app_name)
|
|
||||||
|
|
||||||
elif "-installer" in sys.argv:
|
|
||||||
make_install(app_ver, app_name)
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("Do you want to install onto this machine or create an installer?")
|
|
||||||
print("[1] local machine")
|
|
||||||
print("[2] create installer")
|
|
||||||
print("[3] exit")
|
|
||||||
|
|
||||||
while(True):
|
|
||||||
opt = input("select an option: ")
|
|
||||||
|
|
||||||
if opt == "1":
|
|
||||||
local_install(app_target, app_name)
|
|
||||||
break
|
|
||||||
|
|
||||||
elif opt == "2":
|
|
||||||
make_install(app_ver, app_name)
|
|
||||||
break
|
|
||||||
|
|
||||||
elif opt == "3":
|
|
||||||
break
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main(is_sfx=False)
|
|
220
linux_build.sh
Normal file
220
linux_build.sh
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
qt_dir="$1"
|
||||||
|
installer_file="$2"
|
||||||
|
|
||||||
|
src_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
|
bin_name="cmdr"
|
||||||
|
app_version="3.0"
|
||||||
|
app_name="Cmdr"
|
||||||
|
install_dir="/opt/$bin_name"
|
||||||
|
bin_dir="/usr/bin"
|
||||||
|
tmp_dir="$HOME/.cache/cmdr_build"
|
||||||
|
|
||||||
|
if [ ! -d "$qt_dir" ]; then
|
||||||
|
|
||||||
|
echo "a valid path to Qt was not provided, falling back to the default: /usr/lib/x86_64-linux-gnu/qt5/bin"
|
||||||
|
|
||||||
|
qt_dir="/usr/lib/x86_64-linux-gnu/qt5/bin"
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
PATH=$qt_dir:$PATH
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$installer_file" = "" ]; then
|
||||||
|
|
||||||
|
installer_file="$src_dir/$bin_name-$app_version.run"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$tmp_dir" ]; then
|
||||||
|
|
||||||
|
rm -rf $tmp_dir
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $? -eq 0 -a -d "$qt_dir" ]; then
|
||||||
|
|
||||||
|
mkdir -vp $tmp_dir
|
||||||
|
cp -r $src_dir/. $tmp_dir
|
||||||
|
cd $tmp_dir
|
||||||
|
qmake -config release
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
|
||||||
|
make
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
|
||||||
|
mkdir -v ./build
|
||||||
|
mkdir -v ./build/lib
|
||||||
|
mkdir -v ./build/platforms
|
||||||
|
ldd ./$bin_name | grep "libQt" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./build/lib
|
||||||
|
ldd ./$bin_name | grep "libicu" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./build/lib
|
||||||
|
ldd ./$bin_name | grep "libssl" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./build/lib
|
||||||
|
ldd ./$bin_name | grep "libcrypto" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./build/lib
|
||||||
|
ldd ./$bin_name | grep "libGL.so" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./build/lib
|
||||||
|
ldd ./$bin_name | grep "libpcre16.so" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./build/lib
|
||||||
|
ldd ./$bin_name | grep "libpcre.so" | awk '{print $3}' | xargs -I '{}' cp -v '{}' ./build/lib
|
||||||
|
mv -v ./$bin_name ./build/$bin_name
|
||||||
|
cp -v $qt_dir/../plugins/platforms/libqxcb.so ./build/platforms/libqxcb.so
|
||||||
|
cp -rfv ./icons ./build
|
||||||
|
cp -rfv $qt_dir/../plugins/xcbglintegrations ./build
|
||||||
|
|
||||||
|
if [ "$qt_dir" = "/usr/lib/x86_64-linux-gnu/qt5/bin" ]; then
|
||||||
|
|
||||||
|
cp -fv $qt_dir/../../libQt5DBus.so.5 ./build/lib/libQt5DBus.so.5
|
||||||
|
cp -fv $qt_dir/../../libQt5XcbQpa.so.5 ./build/lib/libQt5XcbQpa.so.5
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
cp -fv $qt_dir/../lib/libQt5DBus.so.5 ./build/lib/libQt5DBus.so.5
|
||||||
|
cp -fv $qt_dir/../lib/libQt5XcbQpa.so.5 ./build/lib/libQt5XcbQpa.so.5
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
startup_script="./build/$bin_name.sh"
|
||||||
|
setup_script="./build/setup.sh"
|
||||||
|
uninstall_script="./build/uninstall.sh"
|
||||||
|
desktop_file="./build/$bin_name.desktop"
|
||||||
|
|
||||||
|
echo "#!/bin/sh" > $startup_script
|
||||||
|
echo "export QTDIR=$install_dir" >> $startup_script
|
||||||
|
echo "export QT_PLUGIN_PATH=$install_dir" >> $startup_script
|
||||||
|
echo "export LD_LIBRARY_PATH=\"$install_dir/lib:\$LD_LIBRARY_PATH\"" >> $startup_script
|
||||||
|
echo "$install_dir/$bin_name" >> $startup_script
|
||||||
|
|
||||||
|
echo "[Desktop Entry]" > $desktop_file
|
||||||
|
echo "Type=Application" >> $desktop_file
|
||||||
|
echo "Exec=$bin_dir/$bin_name" >> $desktop_file
|
||||||
|
echo "Name=$app_name" >> $desktop_file
|
||||||
|
echo "GenericName=Terminal emulator for MRCI host." >> $desktop_file
|
||||||
|
echo "Icon=$bin_name" >> $desktop_file
|
||||||
|
echo "StartupWMClass=$bin_name" >> $desktop_file
|
||||||
|
echo "Terminal=false" >> $desktop_file
|
||||||
|
echo "Categories=Network;Terminal;MRCI;" >> $desktop_file
|
||||||
|
|
||||||
|
echo "#!/bin/sh" > $setup_script
|
||||||
|
echo "if [ -f \"$install_dir/uninstall.sh\" ]; then" >> $setup_script
|
||||||
|
echo " sh $install_dir/uninstall.sh" >> $setup_script
|
||||||
|
echo "fi" >> $setup_script
|
||||||
|
echo "if [ ! -d \"$install_dir\" ]; then" >> $setup_script
|
||||||
|
echo " sudo mkdir -p $install_dir" >> $setup_script
|
||||||
|
echo "fi" >> $setup_script
|
||||||
|
echo "sudo cp -rv ./lib $install_dir" >> $setup_script
|
||||||
|
echo "sudo cp -rv ./platforms $install_dir" >> $setup_script
|
||||||
|
echo "sudo cp -rv ./xcbglintegrations $install_dir" >> $setup_script
|
||||||
|
echo "sudo cp -v ./$bin_name $install_dir" >> $setup_script
|
||||||
|
echo "sudo cp -v ./$bin_name.sh $install_dir" >> $setup_script
|
||||||
|
echo "sudo cp -v ./uninstall.sh $install_dir" >> $setup_script
|
||||||
|
echo "sudo chmod 755 $install_dir/$bin_name" >> $setup_script
|
||||||
|
echo "sudo chmod 755 $install_dir/$bin_name.sh" >> $setup_script
|
||||||
|
echo "sudo chmod 755 $install_dir/uninstall.sh" >> $setup_script
|
||||||
|
echo "sudo chmod 755 $install_dir" >> $setup_script
|
||||||
|
echo "sudo chmod -R 755 $install_dir/lib" >> $setup_script
|
||||||
|
echo "sudo chmod -R 755 $install_dir/platforms" >> $setup_script
|
||||||
|
echo "sudo chmod -R 755 $install_dir/xcbglintegrations" >> $setup_script
|
||||||
|
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/8x8/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/16x16/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/22x22/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/24x24/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/32x32/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/36x36/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/42x42/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/48x48/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/64x64/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/72x72/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/96x96/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/128x128/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/192x192/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/256x256/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/512x512/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/icons/hicolor/scalable/apps" >> $setup_script
|
||||||
|
echo "sudo mkdir -p /usr/share/applications" >> $setup_script
|
||||||
|
|
||||||
|
echo "sudo cp -v ./icons/8x8.png /usr/share/icons/hicolor/8x8/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/16x16.png /usr/share/icons/hicolor/16x16/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/22x22.png /usr/share/icons/hicolor/22x22/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/24x24.png /usr/share/icons/hicolor/24x24/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/32x32.png /usr/share/icons/hicolor/32x32/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/36x36.png /usr/share/icons/hicolor/36x36/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/42x42.png /usr/share/icons/hicolor/42x42/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/48x48.png /usr/share/icons/hicolor/48x48/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/64x64.png /usr/share/icons/hicolor/64x64/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/72x72.png /usr/share/icons/hicolor/72x72/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/96x96.png /usr/share/icons/hicolor/96x96/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/128x128.png /usr/share/icons/hicolor/128x128/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/192x192.png /usr/share/icons/hicolor/192x192/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/256x256.png /usr/share/icons/hicolor/256x256/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/512x512.png /usr/share/icons/hicolor/512x512/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo cp -v ./icons/scalable.svg /usr/share/icons/hicolor/scalable/apps/$bin_name.svg" >> $setup_script
|
||||||
|
echo "sudo cp -v ./$bin_name.desktop /usr/share/applications/$bin_name.desktop" >> $setup_script
|
||||||
|
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/8x8/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/16x16/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/22x22/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/24x24/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/32x32/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/36x36/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/42x42/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/48x48/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/64x64/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/72x72/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/96x96/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/128x128/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/192x192/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/256x256/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/512x512/apps/$bin_name.png" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/icons/hicolor/scalable/apps/$bin_name.svg" >> $setup_script
|
||||||
|
echo "sudo chmod 644 /usr/share/applications/$bin_name.desktop" >> $setup_script
|
||||||
|
|
||||||
|
echo "sudo ln -sfv $install_dir/$bin_name.sh $bin_dir/$bin_name" >> $setup_script
|
||||||
|
echo "echo \"\nInstallation finished. If you ever need to uninstall this application, run this command:\n\"" >> $setup_script
|
||||||
|
echo "echo \" sh $install_dir/uninstall.sh\n\"" >> $setup_script
|
||||||
|
|
||||||
|
echo "#!/bin/sh" > $uninstall_script
|
||||||
|
echo "sudo rm -v $bin_dir/$bin_name" >> $uninstall_script
|
||||||
|
echo "sudo rm -rv $install_dir" >> $uninstall_script
|
||||||
|
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/8x8/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/16x16/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/22x22/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/24x24/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/32x32/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/36x36/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/42x42/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/48x48/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/64x64/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/72x72/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/96x96/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/128x128/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/192x192/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/256x256/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/512x512/apps/$bin_name.png" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/icons/hicolor/scalable/apps/$bin_name.svg" >> $uninstall_script
|
||||||
|
echo "sudo rm -v /usr/share/applications/$bin_name.desktop" >> $uninstall_script
|
||||||
|
|
||||||
|
echo "echo Finished." >> $uninstall_script
|
||||||
|
|
||||||
|
chmod +x $setup_script
|
||||||
|
|
||||||
|
makeself ./build $installer_file "$app_name Installation" ./setup.sh
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
echo "err: A valid Qt dir could not be found."
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d "$tmp_dir" ]; then
|
||||||
|
|
||||||
|
rm -rf $tmp_dir
|
||||||
|
|
||||||
|
fi
|
|
@ -164,7 +164,7 @@ void CmdLine::toHost(const QString &cmdName, const QString &args)
|
||||||
{
|
{
|
||||||
if (flags & HOST_HOOK)
|
if (flags & HOST_HOOK)
|
||||||
{
|
{
|
||||||
emit dataToHookedHost(args.toUtf8(), TEXT);
|
emit dataToHookedHost(toTEXT(args), TEXT);
|
||||||
}
|
}
|
||||||
else if (!cmdName.isEmpty())
|
else if (!cmdName.isEmpty())
|
||||||
{
|
{
|
||||||
|
@ -178,11 +178,11 @@ void CmdLine::toHost(const QString &cmdName, const QString &args)
|
||||||
{
|
{
|
||||||
emit setHostCmdId(cmdId);
|
emit setHostCmdId(cmdId);
|
||||||
emit setGenfileType(Shared::genfileTypes->value(cmdId));
|
emit setGenfileType(Shared::genfileTypes->value(cmdId));
|
||||||
emit dataToGenFile(args.toUtf8());
|
emit dataToGenFile(toTEXT(args));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
emit dataToHost(cmdId, args.toUtf8(), TEXT);
|
emit dataToHost(cmdId, toTEXT(args), TEXT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,7 @@ void CmdLine::procCmdLine(const QString &line)
|
||||||
|
|
||||||
if (flags & GEN_HOOK)
|
if (flags & GEN_HOOK)
|
||||||
{
|
{
|
||||||
emit dataToGenFile(argsLine.toUtf8());
|
emit dataToGenFile(toTEXT(argsLine));
|
||||||
}
|
}
|
||||||
else if (Shared::clientCmds->contains(cmdName))
|
else if (Shared::clientCmds->contains(cmdName))
|
||||||
{
|
{
|
||||||
|
|
|
@ -111,12 +111,12 @@ void SaveBookmark::dataIn(const QString &argsLine)
|
||||||
{
|
{
|
||||||
if (activeHook())
|
if (activeHook())
|
||||||
{
|
{
|
||||||
if ("y" == argsLine.toLower())
|
if (QRegExp("y", Qt::CaseInsensitive).exactMatch(argsLine))
|
||||||
{
|
{
|
||||||
run(baseName, baseArgs);
|
run(baseName, baseArgs);
|
||||||
term();
|
term();
|
||||||
}
|
}
|
||||||
else if ("n" == argsLine.toLower())
|
else if (QRegExp("n", Qt::CaseInsensitive).exactMatch(argsLine))
|
||||||
{
|
{
|
||||||
term();
|
term();
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,6 +130,10 @@ void Connect::dataIn(const QString &argsLine)
|
||||||
{
|
{
|
||||||
cacheTxt(ERR, "err: Host address is empty.\n");
|
cacheTxt(ERR, "err: Host address is empty.\n");
|
||||||
}
|
}
|
||||||
|
else if (QHostAddress(*Shared::hostAddress).isNull())
|
||||||
|
{
|
||||||
|
cacheTxt(ERR, "err: '" + *Shared::hostAddress + "' is not a valid address.\n");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
emit connectToHost();
|
emit connectToHost();
|
||||||
|
|
|
@ -18,27 +18,13 @@
|
||||||
|
|
||||||
HostDoc::HostDoc(const QByteArray &import, QObject *parent) : Command(parent)
|
HostDoc::HostDoc(const QByteArray &import, QObject *parent) : Command(parent)
|
||||||
{
|
{
|
||||||
// ```NEW_CMD```
|
|
||||||
// This contains information about a new command that was added to the current session.
|
|
||||||
|
|
||||||
// ```
|
|
||||||
// format:
|
|
||||||
// 1. bytes[0-1] 2bytes - 16bit LE unsigned int (command id)
|
|
||||||
// 2. bytes[2] 1byte - 8bit LE unsigned int (genfile type)
|
|
||||||
// 3. bytes[3-66] 64bytes - command name (TEXT - padded with 0x00)
|
|
||||||
// 4. bytes[67-130] 64bytes - library name (TEXT - padded with 0x00)
|
|
||||||
// 5. bytes[131-n] variable - short text (null terminated)
|
|
||||||
// 6. bytes[n-n] variable - io text (null terminated)
|
|
||||||
// 7. bytes[n-n] variable - long text (null terminated)
|
|
||||||
|
|
||||||
valid = false;
|
valid = false;
|
||||||
|
|
||||||
if (import.size() >= 131) // within safe import size
|
if (import.size() >= 259)
|
||||||
{
|
{
|
||||||
cmdId = static_cast<quint16>(rdInt(import.mid(0, 2))); // 1.
|
cmdId = static_cast<quint16>(rdInt(import.mid(0, 2)));
|
||||||
|
|
||||||
auto cmdName = QString(import.mid(3, 64)).toLower(); // 3.
|
|
||||||
|
|
||||||
|
QString cmdName = fromTEXT(import.mid(3, 128)).toLower();
|
||||||
QString num;
|
QString num;
|
||||||
|
|
||||||
for (int i = 1; Shared::clientCmds->contains(cmdName + num); ++i)
|
for (int i = 1; Shared::clientCmds->contains(cmdName + num); ++i)
|
||||||
|
@ -48,18 +34,18 @@ HostDoc::HostDoc(const QByteArray &import, QObject *parent) : Command(parent)
|
||||||
|
|
||||||
setObjectName(cmdName + num);
|
setObjectName(cmdName + num);
|
||||||
|
|
||||||
if ((import[2] == GEN_UPLOAD) || (import[2] == GEN_DOWNLOAD)) // 2.
|
if ((import[2] == GEN_UPLOAD) || (import[2] == GEN_DOWNLOAD))
|
||||||
{
|
{
|
||||||
Shared::genfileCmds->insert(cmdId, objectName());
|
Shared::genfileCmds->insert(cmdId, objectName());
|
||||||
Shared::genfileTypes->insert(cmdId, static_cast<quint8>(import[2]));
|
Shared::genfileTypes->insert(cmdId, static_cast<quint8>(import[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
quint32 offs = 131; // 5.
|
quint32 offs = 259;
|
||||||
|
|
||||||
libTxt = QString(import.mid(67, 64)); // 4.
|
libTxt = fromTEXT(import.mid(131, 128));
|
||||||
shortTxt = readNullTermText(import, &offs);
|
shortTxt = readNullTermText(import, &offs);
|
||||||
ioTxt = readNullTermText(import, &offs); // 6.
|
ioTxt = readNullTermText(import, &offs);
|
||||||
longTxt = readNullTermText(import, &offs); // 7.
|
longTxt = readNullTermText(import, &offs);
|
||||||
valid = true;
|
valid = true;
|
||||||
|
|
||||||
Shared::hostCmds->insert(cmdId, objectName());
|
Shared::hostCmds->insert(cmdId, objectName());
|
||||||
|
@ -69,23 +55,29 @@ HostDoc::HostDoc(const QByteArray &import, QObject *parent) : Command(parent)
|
||||||
|
|
||||||
QString HostDoc::readNullTermText(const QByteArray &data, quint32 *offs)
|
QString HostDoc::readNullTermText(const QByteArray &data, quint32 *offs)
|
||||||
{
|
{
|
||||||
|
static const QByteArray null16(2, 0x00);
|
||||||
|
|
||||||
QString ret;
|
QString ret;
|
||||||
|
quint32 len = static_cast<quint32>(data.size());
|
||||||
|
|
||||||
auto len = static_cast<quint32>(data.size());
|
for (; *offs < len; *offs += 2)
|
||||||
|
|
||||||
for (; *offs < len; *offs += 1)
|
|
||||||
{
|
{
|
||||||
if (data[*offs] == 0x00)
|
if ((*offs + 2) < len)
|
||||||
|
{
|
||||||
|
QByteArray chr = QByteArray::fromRawData(data.data() + *offs, 2);
|
||||||
|
|
||||||
|
if (chr == null16)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret.append(data[*offs]);
|
ret.append(fromTEXT(chr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*offs += 1;
|
*offs += 2;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,9 +45,9 @@ void About::dispInfo(Command *cmdObj)
|
||||||
QTextStream txtOut(&txt);
|
QTextStream txtOut(&txt);
|
||||||
|
|
||||||
wordWrap("i/o: ", txtOut, cmdObj->ioText(), Shared::mainWidget);
|
wordWrap("i/o: ", txtOut, cmdObj->ioText(), Shared::mainWidget);
|
||||||
txtOut << "" << Qt::endl;
|
txtOut << "" << endl;
|
||||||
wordWrap("library: ", txtOut, cmdObj->libText(), Shared::mainWidget);
|
wordWrap("library: ", txtOut, cmdObj->libText(), Shared::mainWidget);
|
||||||
txtOut << "" << Qt::endl;
|
txtOut << "" << endl;
|
||||||
wordWrap("usage: ", txtOut, cmdObj->longText(), Shared::mainWidget);
|
wordWrap("usage: ", txtOut, cmdObj->longText(), Shared::mainWidget);
|
||||||
|
|
||||||
cacheTxt(TEXT, txt);
|
cacheTxt(TEXT, txt);
|
||||||
|
@ -115,12 +115,12 @@ void About::dataIn(const QString &argsLine)
|
||||||
QString txt;
|
QString txt;
|
||||||
QTextStream txtOut(&txt);
|
QTextStream txtOut(&txt);
|
||||||
|
|
||||||
txtOut << libText() << Qt::endl << Qt::endl;
|
txtOut << libText() << endl << endl;
|
||||||
txtOut << "Based on QT " << QT_VERSION_STR << " " << 8 * QT_POINTER_SIZE << "bit" << Qt::endl << Qt::endl;
|
txtOut << "Based on QT " << QT_VERSION_STR << " " << 8 * QT_POINTER_SIZE << "bit" << endl << endl;
|
||||||
txtOut << "The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE" << Qt::endl;
|
txtOut << "The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE" << endl;
|
||||||
txtOut << "WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE." << Qt::endl << Qt::endl;
|
txtOut << "WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE." << endl << endl;
|
||||||
txtOut << "run: 'ls_cmds' to see all available commands." << Qt::endl << Qt::endl;
|
txtOut << "run: 'ls_cmds' to see all available commands." << endl << endl;
|
||||||
txtOut << "for more detailed information about a command run: 'about <command>'" << Qt::endl << Qt::endl;
|
txtOut << "for more detailed information about a command run: 'about <command>'" << endl << endl;
|
||||||
|
|
||||||
cacheTxt(TEXT, txt);
|
cacheTxt(TEXT, txt);
|
||||||
}
|
}
|
||||||
|
@ -145,8 +145,8 @@ void ListCmds::ls(QHash<QString, Command *> *cmdObjs, QTextStream &txtOut, const
|
||||||
|
|
||||||
cmdNames.sort(Qt::CaseInsensitive);
|
cmdNames.sort(Qt::CaseInsensitive);
|
||||||
|
|
||||||
txtOut << Qt::endl;
|
txtOut << endl;
|
||||||
txtOut << title << Qt::endl << Qt::endl;
|
txtOut << title << endl << endl;
|
||||||
|
|
||||||
for (int i = 0; i < cmdNames.size(); ++i)
|
for (int i = 0; i < cmdNames.size(); ++i)
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,20 +34,20 @@ void Status::dataIn(const QString &argsLine)
|
||||||
QString txt;
|
QString txt;
|
||||||
QTextStream txtOut(&txt);
|
QTextStream txtOut(&txt);
|
||||||
|
|
||||||
txtOut << "--Local data" << Qt::endl << Qt::endl;
|
txtOut << "--Local data" << endl << endl;
|
||||||
txtOut << " Client version : " << QCoreApplication::applicationVersion() << Qt::endl;
|
txtOut << " Client version : " << QCoreApplication::applicationVersion() << endl;
|
||||||
txtOut << " Connected? : " << boolText(*Shared::connectedToHost) << Qt::endl;
|
txtOut << " Connected? : " << boolText(*Shared::connectedToHost) << endl;
|
||||||
txtOut << " Address : " << *Shared::hostAddress << Qt::endl;
|
txtOut << " Address : " << *Shared::hostAddress << endl;
|
||||||
txtOut << " Port : " << *Shared::hostPort << Qt::endl;
|
txtOut << " Port : " << *Shared::hostPort << endl;
|
||||||
|
|
||||||
if (*Shared::connectedToHost)
|
if (*Shared::connectedToHost)
|
||||||
{
|
{
|
||||||
txtOut << "" << Qt::endl;
|
txtOut << "" << endl;
|
||||||
txtOut << "--Session data" << Qt::endl << Qt::endl;
|
txtOut << "--Session data" << endl << endl;
|
||||||
txtOut << " Client address : " << Shared::socket->localAddress().toString() << Qt::endl;
|
txtOut << " Client address : " << Shared::socket->localAddress().toString() << endl;
|
||||||
txtOut << " Host address : " << Shared::socket->peerAddress().toString() << Qt::endl;
|
txtOut << " Host address : " << Shared::socket->peerAddress().toString() << endl;
|
||||||
txtOut << " Session id : " << Shared::sessionId->toHex() << Qt::endl;
|
txtOut << " Session id : " << Shared::sessionId->toHex() << endl;
|
||||||
txtOut << " Host version : " << verText() << Qt::endl;
|
txtOut << " Host version : " << verText() << endl;
|
||||||
txtOut << " GEN_FILE commands : ";
|
txtOut << " GEN_FILE commands : ";
|
||||||
|
|
||||||
QStringList genCmds = Shared::genfileCmds->values();
|
QStringList genCmds = Shared::genfileCmds->values();
|
||||||
|
@ -56,13 +56,13 @@ void Status::dataIn(const QString &argsLine)
|
||||||
{
|
{
|
||||||
genCmds.sort(Qt::CaseInsensitive);
|
genCmds.sort(Qt::CaseInsensitive);
|
||||||
|
|
||||||
txtOut << "" << Qt::endl;
|
txtOut << "" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < genCmds.size(); ++i)
|
for (int i = 0; i < genCmds.size(); ++i)
|
||||||
{
|
{
|
||||||
if (i != 0) txtOut << " " << genCmds[i] << Qt::endl;
|
if (i != 0) txtOut << " " << genCmds[i] << endl;
|
||||||
else txtOut << genCmds[i] << Qt::endl;
|
else txtOut << genCmds[i] << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,9 +138,25 @@ QByteArray wrFrame(quint16 cmdId, const QByteArray &data, uchar dType)
|
||||||
return typeBa + cmdBa + branBa + sizeBa + data;
|
return typeBa + cmdBa + branBa + sizeBa + data;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray toFixedTEXT(const QString &txt, int len)
|
QByteArray toTEXT(const QString &txt)
|
||||||
{
|
{
|
||||||
return txt.toUtf8().leftJustified(len, 0, true);
|
QByteArray ret = QTextCodec::codecForName(TXT_CODEC)->fromUnicode(txt);
|
||||||
|
|
||||||
|
return ret.mid(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray fixedToTEXT(const QString &txt, int len)
|
||||||
|
{
|
||||||
|
return toTEXT(txt).leftJustified(len, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString fromTEXT(const QByteArray &txt)
|
||||||
|
{
|
||||||
|
QByteArray ba = txt;
|
||||||
|
|
||||||
|
ba.replace(QByteArray(2, 0x00), QByteArray());
|
||||||
|
|
||||||
|
return QTextCodec::codecForName(TXT_CODEC)->toUnicode(ba);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray wrInt(quint64 num, int numOfBits)
|
QByteArray wrInt(quint64 num, int numOfBits)
|
||||||
|
@ -195,11 +211,10 @@ QStringList parseArgs(const QByteArray &data, int maxArgs)
|
||||||
{
|
{
|
||||||
QStringList ret;
|
QStringList ret;
|
||||||
QString arg;
|
QString arg;
|
||||||
|
QString line = fromTEXT(data);
|
||||||
auto line = QString(data);
|
bool inDQuotes = false;
|
||||||
auto inDQuotes = false;
|
bool inSQuotes = false;
|
||||||
auto inSQuotes = false;
|
bool escaped = false;
|
||||||
auto escaped = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < line.size(); ++i)
|
for (int i = 0; i < line.size(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -265,7 +280,7 @@ QStringList parseArgs(const QByteArray &data, int maxArgs)
|
||||||
|
|
||||||
QStringList parseArgs(const QString &line)
|
QStringList parseArgs(const QString &line)
|
||||||
{
|
{
|
||||||
return parseArgs(line.toUtf8(), -1);
|
return parseArgs(toTEXT(line), -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wordWrap(const QString &label, QTextStream &txtOut, const QString &txtIn, QWidget *measureWid)
|
void wordWrap(const QString &label, QTextStream &txtOut, const QString &txtIn, QWidget *measureWid)
|
||||||
|
@ -298,8 +313,8 @@ void wordWrap(const QString &label, QTextStream &txtOut, const QString &txtIn, Q
|
||||||
|
|
||||||
line.chop(1);
|
line.chop(1);
|
||||||
|
|
||||||
if (labelWr) txtOut << label << line << Qt::endl;
|
if (labelWr) txtOut << label << line << endl;
|
||||||
else txtOut << indent << line << Qt::endl;
|
else txtOut << indent << line << endl;
|
||||||
|
|
||||||
labelWr = false;
|
labelWr = false;
|
||||||
}
|
}
|
||||||
|
@ -310,8 +325,8 @@ void wordWrap(const QString &label, QTextStream &txtOut, const QString &txtIn, Q
|
||||||
// the case, the line is added to the return and then
|
// the case, the line is added to the return and then
|
||||||
// cleared.
|
// cleared.
|
||||||
|
|
||||||
if (labelWr) txtOut << label << line << Qt::endl;
|
if (labelWr) txtOut << label << line << endl;
|
||||||
else txtOut << indent << line << Qt::endl;
|
else txtOut << indent << line << endl;
|
||||||
|
|
||||||
line.clear();
|
line.clear();
|
||||||
|
|
||||||
|
@ -350,7 +365,7 @@ QString getParam(const QString &key, const QStringList &args)
|
||||||
|
|
||||||
QString ret;
|
QString ret;
|
||||||
|
|
||||||
int pos = args.indexOf(QRegularExpression(key, QRegularExpression::CaseInsensitiveOption));
|
int pos = args.indexOf(QRegExp(key, Qt::CaseInsensitive));
|
||||||
|
|
||||||
if (pos != -1)
|
if (pos != -1)
|
||||||
{
|
{
|
||||||
|
|
12
src/common.h
12
src/common.h
|
@ -50,6 +50,7 @@
|
||||||
#include <QTextBlockFormat>
|
#include <QTextBlockFormat>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QTextCodec>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
|
@ -58,7 +59,8 @@
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QSslSocket>
|
#include <QSslSocket>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QRegularExpression>
|
#include <QFile>
|
||||||
|
#include <QRegExp>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QProgressBar>
|
#include <QProgressBar>
|
||||||
|
@ -69,11 +71,12 @@
|
||||||
#define DEFAULT_MAX_LINES 1000
|
#define DEFAULT_MAX_LINES 1000
|
||||||
#define RDBUFF 16777215
|
#define RDBUFF 16777215
|
||||||
#define DEFAULT_PORT 35516
|
#define DEFAULT_PORT 35516
|
||||||
|
#define TXT_CODEC "UTF-16LE"
|
||||||
#define BOOKMARK_FOLDER "bookmarks"
|
#define BOOKMARK_FOLDER "bookmarks"
|
||||||
#define CONFIG_FILENAME "config_v3.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 "3.5"
|
#define APP_VERSION "3.2"
|
||||||
|
|
||||||
enum TypeID : quint8
|
enum TypeID : quint8
|
||||||
{
|
{
|
||||||
|
@ -82,6 +85,7 @@ enum TypeID : quint8
|
||||||
ERR = 3,
|
ERR = 3,
|
||||||
PRIV_TEXT = 4,
|
PRIV_TEXT = 4,
|
||||||
IDLE = 5,
|
IDLE = 5,
|
||||||
|
HOST_CERT = 6,
|
||||||
FILE_INFO = 7,
|
FILE_INFO = 7,
|
||||||
PEER_INFO = 8,
|
PEER_INFO = 8,
|
||||||
MY_INFO = 9,
|
MY_INFO = 9,
|
||||||
|
@ -165,9 +169,11 @@ void wordWrap(const QString &label, QTextStream &txtOut, const QString &t
|
||||||
bool argExists(const QString &key, const QStringList &args);
|
bool argExists(const QString &key, const QStringList &args);
|
||||||
QByteArray wrInt(quint64 num, int numOfBits);
|
QByteArray wrInt(quint64 num, int numOfBits);
|
||||||
QByteArray wrFrame(quint16 cmdId, const QByteArray &data, uchar dType);
|
QByteArray wrFrame(quint16 cmdId, const QByteArray &data, uchar dType);
|
||||||
QByteArray toFixedTEXT(const QString &txt, int len);
|
QByteArray toTEXT(const QString &txt);
|
||||||
|
QByteArray fixedToTEXT(const QString &txt, int len);
|
||||||
QStringList parseArgs(const QByteArray &data, int maxArgs);
|
QStringList parseArgs(const QByteArray &data, int maxArgs);
|
||||||
QStringList parseArgs(const QString &line);
|
QStringList parseArgs(const QString &line);
|
||||||
|
QString fromTEXT(const QByteArray &txt);
|
||||||
QString appDataDir();
|
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);
|
||||||
|
|
|
@ -259,8 +259,8 @@ void Genfile::setGenfileType(quint8 typeId)
|
||||||
|
|
||||||
QByteArray Genfile::autoFill(const QByteArray &data)
|
QByteArray Genfile::autoFill(const QByteArray &data)
|
||||||
{
|
{
|
||||||
auto args = parseArgs(data, -1);
|
QStringList args = parseArgs(data, -1);
|
||||||
auto ind = args.indexOf(QRegularExpression("-len", QRegularExpression::CaseInsensitiveOption));
|
int ind = args.indexOf(QRegExp("-len", Qt::CaseInsensitive));
|
||||||
|
|
||||||
if (ind == -1)
|
if (ind == -1)
|
||||||
{
|
{
|
||||||
|
@ -269,7 +269,7 @@ QByteArray Genfile::autoFill(const QByteArray &data)
|
||||||
args.append("-len");
|
args.append("-len");
|
||||||
args.append(len);
|
args.append(len);
|
||||||
|
|
||||||
return args.join(' ').toUtf8();
|
return toTEXT(args.join(' '));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -372,9 +372,9 @@ void Genfile::dataIn(const QByteArray &data)
|
||||||
}
|
}
|
||||||
else if (flags & CONFIRM_NEEDED)
|
else if (flags & CONFIRM_NEEDED)
|
||||||
{
|
{
|
||||||
auto ans = QString(data);
|
QString ans = fromTEXT(data);
|
||||||
|
|
||||||
if ("y" == ans.toLower())
|
if (QRegExp("y", Qt::CaseInsensitive).exactMatch(ans))
|
||||||
{
|
{
|
||||||
emit unsetUserIO(GEN_HOOK);
|
emit unsetUserIO(GEN_HOOK);
|
||||||
|
|
||||||
|
@ -382,7 +382,7 @@ void Genfile::dataIn(const QByteArray &data)
|
||||||
|
|
||||||
setupForWriting();
|
setupForWriting();
|
||||||
}
|
}
|
||||||
else if ("n" == ans.toLower())
|
else if (QRegExp("n", Qt::CaseInsensitive).exactMatch(ans))
|
||||||
{
|
{
|
||||||
finished();
|
finished();
|
||||||
}
|
}
|
||||||
|
|
131
src/session.cpp
131
src/session.cpp
|
@ -36,7 +36,6 @@ 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)));
|
||||||
connect(this, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrs(QList<QSslError>)));
|
|
||||||
|
|
||||||
progResetDelay->setInterval(200);
|
progResetDelay->setInterval(200);
|
||||||
progResetDelay->setSingleShot(true);
|
progResetDelay->setSingleShot(true);
|
||||||
|
@ -83,14 +82,7 @@ void Session::sockerr(QAbstractSocket::SocketError err)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (sslHandshakeErrors().isEmpty())
|
|
||||||
{
|
|
||||||
// a non-empty sslErrors() can assume the errors were already displayed
|
|
||||||
// by the sslErrs() slot so this conditional block is here to prevent
|
|
||||||
// the same error from getting displayed twice.
|
|
||||||
|
|
||||||
cacheTxt(ERR, "\nerr: " + errorString() + "\n");
|
cacheTxt(ERR, "\nerr: " + errorString() + "\n");
|
||||||
}
|
|
||||||
|
|
||||||
if (state() == QAbstractSocket::UnconnectedState)
|
if (state() == QAbstractSocket::UnconnectedState)
|
||||||
{
|
{
|
||||||
|
@ -99,41 +91,18 @@ void Session::sockerr(QAbstractSocket::SocketError err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::sslErrs(const QList<QSslError> &errors)
|
|
||||||
{
|
|
||||||
cacheTxt(ERR, "\n");
|
|
||||||
|
|
||||||
auto canIgnore = true;
|
|
||||||
|
|
||||||
for (auto err : errors)
|
|
||||||
{
|
|
||||||
if ((err.error() == QSslError::SelfSignedCertificate) ||
|
|
||||||
(err.error() == QSslError::SelfSignedCertificateInChain))
|
|
||||||
{
|
|
||||||
cacheTxt(TEXT, "WARNING: the host cert is self signed.\n\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
canIgnore = false;
|
|
||||||
|
|
||||||
cacheTxt(ERR, "err: " + err.errorString() + "\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canIgnore)
|
|
||||||
{
|
|
||||||
ignoreSslErrors();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Session::isConnected()
|
void Session::isConnected()
|
||||||
{
|
{
|
||||||
// client header format: [4bytes(tag)][32bytes(appName)][128bytes(modInst)][128bytes(padding)]
|
// client header format: [4bytes(tag)][134bytes(appName)][272bytes(coName)]
|
||||||
|
|
||||||
// tag = 0x4D, 0x52, 0x43, 0x49 (MRCI)
|
// tag = 0x4D, 0x52, 0x43, 0x49 (MRCI)
|
||||||
// appName = UTF8 string (padded with 0x00)
|
// appName = UTF16LE string (padded with 0x00)
|
||||||
// modInst = UTF8 string (padded with 0x00)
|
// coName = UTF16LE string (padded with 0x00)
|
||||||
// padding = 128 bytes of (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");
|
||||||
|
|
||||||
|
@ -142,11 +111,19 @@ void Session::isConnected()
|
||||||
QByteArray header;
|
QByteArray header;
|
||||||
|
|
||||||
header.append(SERVER_HEADER_TAG);
|
header.append(SERVER_HEADER_TAG);
|
||||||
header.append(toFixedTEXT(appName, 32));
|
header.append(fixedToTEXT(appName, 134));
|
||||||
header.append(QByteArray(256, 0));
|
header.append(fixedToTEXT(*Shared::hostAddress, 272));
|
||||||
|
|
||||||
|
if (header.size() == CLIENT_HEADER_LEN)
|
||||||
|
{
|
||||||
write(header);
|
write(header);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cacheTxt(ERR, "\nerr: client bug! - header len not equal to " + QString::number(CLIENT_HEADER_LEN) + "\n");
|
||||||
|
disconnectFromHost();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Session::handShakeDone()
|
void Session::handShakeDone()
|
||||||
{
|
{
|
||||||
|
@ -155,13 +132,13 @@ void Session::handShakeDone()
|
||||||
QString txt;
|
QString txt;
|
||||||
QTextStream txtOut(&txt);
|
QTextStream txtOut(&txt);
|
||||||
|
|
||||||
txtOut << "SSL Handshake sucessful." << Qt::endl
|
txtOut << "SSL Handshake sucessful." << endl
|
||||||
<< "cipher details:" << Qt::endl
|
<< "cipher details:" << endl
|
||||||
<< " #cipher - " << cipher.name() << Qt::endl
|
<< " #cipher - " << cipher.name() << endl
|
||||||
<< " #protocol - " << cipher.protocolString() << Qt::endl
|
<< " #protocol - " << cipher.protocolString() << endl
|
||||||
<< " #bit_depth - " << cipher.usedBits() << Qt::endl
|
<< " #bit_depth - " << cipher.usedBits() << endl
|
||||||
<< " #key_exchange - " << cipher.keyExchangeMethod() << Qt::endl
|
<< " #key_exchange - " << cipher.keyExchangeMethod() << endl
|
||||||
<< " #authentication - " << cipher.authenticationMethod() << Qt::endl;
|
<< " #authentication - " << cipher.authenticationMethod() << endl;
|
||||||
|
|
||||||
cacheTxt(TEXT, txt);
|
cacheTxt(TEXT, txt);
|
||||||
}
|
}
|
||||||
|
@ -269,24 +246,43 @@ void Session::procAsync(const QByteArray &data)
|
||||||
{
|
{
|
||||||
if (dType == TEXT)
|
if (dType == TEXT)
|
||||||
{
|
{
|
||||||
cacheTxt(dType, "\ncast_text: " + QString(data) + "\n");
|
cacheTxt(dType, "\ncast_text: " + fromTEXT(data) + "\n");
|
||||||
}
|
}
|
||||||
else if (dType == BIG_TEXT)
|
else if (dType == BIG_TEXT)
|
||||||
{
|
{
|
||||||
QString txt;
|
QString txt;
|
||||||
QTextStream stream(&txt);
|
QTextStream stream(&txt);
|
||||||
|
|
||||||
wordWrap("cast_text: ", stream, QString(data), Shared::mainWidget);
|
wordWrap("cast_text: ", stream, fromTEXT(data), Shared::mainWidget);
|
||||||
cacheTxt(TEXT, "\n");
|
cacheTxt(TEXT, "\n");
|
||||||
cacheTxt(TEXT, txt);
|
cacheTxt(TEXT, txt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (cmdId == ASYNC_RDY)
|
else if (cmdId == ASYNC_RDY)
|
||||||
{
|
{
|
||||||
cacheTxt(dType, QString(data));
|
cacheTxt(dType, fromTEXT(data));
|
||||||
|
|
||||||
hook = 0;
|
hook = 0;
|
||||||
}
|
}
|
||||||
|
else if (cmdId == ASYNC_SYS_MSG)
|
||||||
|
{
|
||||||
|
if (dType == HOST_CERT)
|
||||||
|
{
|
||||||
|
QSslCertificate cert(data, QSsl::Pem);
|
||||||
|
QSslError selfSigned(QSslError::SelfSignedCertificate, cert);
|
||||||
|
|
||||||
|
cacheTxt(TEXT, "SSL cert received.\n\n");
|
||||||
|
|
||||||
|
if (cert.isSelfSigned())
|
||||||
|
{
|
||||||
|
cacheTxt(TEXT, "WARNING: the host cert is self signed.\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
ignoreSslErrors(QList<QSslError>() << selfSigned);
|
||||||
|
addCaCertificate(cert);
|
||||||
|
startClientEncryption();
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (cmdId == ASYNC_ADD_CMD)
|
else if (cmdId == ASYNC_ADD_CMD)
|
||||||
{
|
{
|
||||||
if (dType == NEW_CMD)
|
if (dType == NEW_CMD)
|
||||||
|
@ -379,7 +375,7 @@ void Session::dataFromHost(const QByteArray &data)
|
||||||
{
|
{
|
||||||
if ((dType == TEXT) || (dType == PRIV_TEXT) || (dType == BIG_TEXT) || (dType == ERR) || (dType == PROMPT_TEXT))
|
if ((dType == TEXT) || (dType == PRIV_TEXT) || (dType == BIG_TEXT) || (dType == ERR) || (dType == PROMPT_TEXT))
|
||||||
{
|
{
|
||||||
cacheTxt(dType, QString(data));
|
cacheTxt(dType, fromTEXT(data));
|
||||||
}
|
}
|
||||||
else if (dType == PROG)
|
else if (dType == PROG)
|
||||||
{
|
{
|
||||||
|
@ -439,29 +435,32 @@ void Session::dataIn()
|
||||||
{
|
{
|
||||||
// host header format: [1byte(reply)][2bytes(major)][2bytes(minor)][2bytes(tcp_rev)][2bytes(mod_rev)][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
|
||||||
|
// sent to the host from the isConnected() function.
|
||||||
|
|
||||||
// sesId = 224bit sha3 hash
|
// sesId = 224bit sha3 hash
|
||||||
// major = 16bit little endian uint (host ver major)
|
// major = 16bit little endian uint (host ver major)
|
||||||
// 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)
|
||||||
|
|
||||||
// reply [1]: SSL handshake not needed.
|
// reply 1: client header acceptable, SSL handshake not needed.
|
||||||
// reply [2]: SSL handshake needed (STARTTLS).
|
// reply 2: client header acceptable, SSL handshake needed (STARTTLS).
|
||||||
|
// reply 4: the common name sent by the client header was not found.
|
||||||
|
|
||||||
auto servHeader = read(SERVER_HEADER_LEN);
|
auto reply = static_cast<quint8>(rdInt(read(1)));
|
||||||
auto reply = static_cast<quint8>(servHeader[0]);
|
|
||||||
|
|
||||||
if ((reply == 1) || (reply == 2))
|
if ((reply == 1) || (reply == 2))
|
||||||
{
|
{
|
||||||
*Shared::servMajor = static_cast<quint16>(rdInt(servHeader.mid(1, 2)));
|
*Shared::servMajor = static_cast<quint16>(rdInt(read(2)));
|
||||||
*Shared::servMinor = static_cast<quint16>(rdInt(servHeader.mid(3, 2)));
|
*Shared::servMinor = static_cast<quint16>(rdInt(read(2)));
|
||||||
*Shared::tcpRev = static_cast<quint16>(rdInt(servHeader.mid(5, 2)));
|
*Shared::tcpRev = static_cast<quint16>(rdInt(read(2)));
|
||||||
*Shared::modRev = static_cast<quint16>(rdInt(servHeader.mid(7, 2)));
|
*Shared::modRev = static_cast<quint16>(rdInt(read(2)));
|
||||||
|
|
||||||
cacheTxt(TEXT, "Detected host version: " + verText() + "\n");
|
cacheTxt(TEXT, "Detected host version: " + verText() + "\n");
|
||||||
|
|
||||||
if (*Shared::tcpRev == 2)
|
if (*Shared::tcpRev == 0)
|
||||||
{
|
{
|
||||||
*Shared::sessionId = servHeader.mid(9, 28);
|
*Shared::sessionId = read(28);
|
||||||
*Shared::connectedToHost = true;
|
*Shared::connectedToHost = true;
|
||||||
|
|
||||||
flags |= VER_OK;
|
flags |= VER_OK;
|
||||||
|
@ -470,9 +469,7 @@ void Session::dataIn()
|
||||||
|
|
||||||
if (reply == 2)
|
if (reply == 2)
|
||||||
{
|
{
|
||||||
cacheTxt(TEXT, "Starting SSL handshake.\n");
|
cacheTxt(TEXT, "Awaiting SSL cert from the host.\n");
|
||||||
|
|
||||||
startClientEncryption();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -485,6 +482,10 @@ void Session::dataIn()
|
||||||
disconnectFromHost();
|
disconnectFromHost();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (reply == 4)
|
||||||
|
{
|
||||||
|
cacheTxt(ERR, "err: The host was unable to find a SSL cert for common name: " + *Shared::hostAddress + ".\n");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cacheTxt(ERR, "err: Invalid reply id from the host: " + QString::number(reply) + ".\n");
|
cacheTxt(ERR, "err: Invalid reply id from the host: " + QString::number(reply) + ".\n");
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QAbstractSocket>
|
#include <QAbstractSocket>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QTextCodec>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QXmlStreamWriter>
|
#include <QXmlStreamWriter>
|
||||||
#include <QSsl>
|
#include <QSsl>
|
||||||
|
@ -33,7 +34,6 @@
|
||||||
#include <QSslCipher>
|
#include <QSslCipher>
|
||||||
#include <QSslCertificate>
|
#include <QSslCertificate>
|
||||||
#include <QSocketNotifier>
|
#include <QSocketNotifier>
|
||||||
#include <QSslConfiguration>
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "cmd_objs/command.h"
|
#include "cmd_objs/command.h"
|
||||||
|
@ -41,7 +41,8 @@
|
||||||
#include "cmd_objs/host_doc.h"
|
#include "cmd_objs/host_doc.h"
|
||||||
|
|
||||||
#define SERVER_HEADER_TAG "MRCI"
|
#define SERVER_HEADER_TAG "MRCI"
|
||||||
#define SERVER_HEADER_LEN 37
|
#define CLIENT_HEADER_LEN 410
|
||||||
|
#define SERVER_HEADER_LEN 35
|
||||||
#define FRAME_HEADER_LEN 8
|
#define FRAME_HEADER_LEN 8
|
||||||
|
|
||||||
class Session : public QSslSocket
|
class Session : public QSslSocket
|
||||||
|
@ -75,7 +76,6 @@ private slots:
|
||||||
void resetProg();
|
void resetProg();
|
||||||
void startProg();
|
void startProg();
|
||||||
void sockerr(QAbstractSocket::SocketError err);
|
void sockerr(QAbstractSocket::SocketError err);
|
||||||
void sslErrs(const QList<QSslError> &errors);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Exec=/usr/bin/$app_target
|
|
||||||
Name=$app_name
|
|
||||||
GenericName=Terminal emulator for MRCI host.
|
|
||||||
Icon=$app_target
|
|
||||||
StartupWMClass=$app_target
|
|
||||||
Terminal=false
|
|
||||||
Categories=Network;Terminal;MRCI;
|
|
|
@ -1,5 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
export QTDIR=$install_dir
|
|
||||||
export QT_PLUGIN_PATH=$install_dir
|
|
||||||
export LD_LIBRARY_PATH="$install_dir/lib:\$LD_LIBRARY_PATH"
|
|
||||||
$install_dir/$app_target $1 $2 $3
|
|
|
@ -1,21 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
rm -v /usr/bin/$app_target
|
|
||||||
rm -rv $install_dir
|
|
||||||
rm -v /usr/share/icons/hicolor/8x8/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/16x16/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/22x22/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/24x24/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/32x32/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/36x36/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/42x42/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/48x48/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/64x64/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/72x72/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/96x96/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/128x128/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/192x192/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/256x256/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/512x512/apps/$app_target.png
|
|
||||||
rm -v /usr/share/icons/hicolor/scalable/apps/$app_target.svg
|
|
||||||
rm -v /usr/share/applications/$app_target.desktop
|
|
||||||
echo "Uninstallation Complete"
|
|
|
@ -1,6 +0,0 @@
|
||||||
set WshShell = CreateObject("Wscript.shell")
|
|
||||||
set oMyShortcut = WshShell.CreateShortcut("%SystemDrive%\ProgramData\Microsoft\Windows\Start Menu\Programs\$app_name.lnk")
|
|
||||||
|
|
||||||
oMyShortcut.IconLocation = "$install_dir\icon.ico"
|
|
||||||
OMyShortcut.TargetPath = "$install_dir\$app_target.exe"
|
|
||||||
oMyShortCut.Save
|
|
|
@ -1,6 +0,0 @@
|
||||||
$app_target -stop
|
|
||||||
schtasks /delete /f /tn $app_name
|
|
||||||
del /f "%windir%\$app_target.exe"
|
|
||||||
del /f "%SystemDrive%\ProgramData\Microsoft\Windows\Start Menu\Programs\$app_name.lnk"
|
|
||||||
rd /q /s "$install_dir"
|
|
||||||
echo "Uninstallation Complete"
|
|
Loading…
Reference in New Issue
Block a user