diff --git a/.gitignore b/.gitignore index 15361cf..d4b29bf 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,10 @@ ui_*.h *.jsc Makefile* *build-* +/build +/app_dir +/release +/debug # Qt unit tests target_wrapper.* @@ -47,3 +51,6 @@ compile_commands.json # QtCreator local machine specific files for imported projects *creator.user* + +# VSCode +/.vscode diff --git a/Cmdr.pro b/Cmdr.pro index 6bfd8ee..799bbff 100644 --- a/Cmdr.pro +++ b/Cmdr.pro @@ -36,9 +36,27 @@ DEFINES += QT_DEPRECATED_WARNINGS # 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 -TARGET = cmdr +win32 { -LIBS += -lcrypto -lssl + LIBS += -llibcrypto -llibssl + + 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 + + TARGET = build/linux/cmdr + OBJECTS_DIR = build/linux + MOC_DIR = build/linux + RCC_DIR = build/linux + +} SOURCES += src/main.cpp \ src/cmd_line.cpp \ diff --git a/build.py b/build.py new file mode 100644 index 0000000..fde8650 --- /dev/null +++ b/build.py @@ -0,0 +1,281 @@ +#!/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): + files = os.listdir(src) + + 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 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") + + 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("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() + + 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() \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 7913457..64e6945 100644 --- a/docs/README.md +++ b/docs/README.md @@ -44,18 +44,21 @@ notes: ### Client Header (This Application) ### ``` -[tag][appName][padding] +[tag][appName][mod_instructions][padding] tag - 4bytes - 0x4D, 0x52, 0x43, 0x49 (MRCI) -appName - 134bytes - UTF16LE string (padded with 0x00) -padding - 272bytes - padding of 0x00 bytes reserved for future expansion +appName - 32bytes - UTF8 string (padded with 0x00) +modInst - 128bytes - UTF8 string (padded with 0x00) +padding - 128bytes - string of (0x00) ``` notes: -* The **tag** is just a fixed ascii string "MRCI" that indicates to the host that the client is indeed attempting to use the MRCI protocol. +* **tag** is just a fixed ascii string "MRCI" that indicates to the host that the client is indeed attempting to use the MRCI protocol. -* The **appName** is the name of the client application that is connected to the host. It can also contain the client's app version if needed because it doesn't follow any particular standard. This string is accessable to all modules so the commands themselves can be made aware of what app the user is currently using. +* **appName** is the name of the client application that is connected to the host. It can also contain the client's app version if needed because it doesn't follow any particular standard. This string is accessable to all modules so the commands themselves can be made aware of what app the user is currently using. + +* **modInst** is an additional set of command lines that can be passed onto to all module processes when they are intialized. This can be used by certain clients that want to intruct certain modules that might be installed in the host to do certain actions during intialization. This remains constant for as long as the session is active and cannot be changed at any point. ### Host Header ### @@ -85,31 +88,34 @@ notes: 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 ### +### Build Setup ### -Linux Required Packages: +For Linux you need the following packages to successfully build/install: ``` qtbase5-dev libssl-dev gcc make -makeself +python3 ``` -### Build From Source (Linux) ### +For Windows support you need to have the following applications installed: +``` +OpenSSL +Qt5.12 or newer +Python3 +``` -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: +### Build ### -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. +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. -Build: -``` -cd /path/to/source/code -sh ./linux_build.sh -``` -Install: -``` -chmod +x ./cmdr-x.x.run -./cmdr-x.x.run -``` +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 insalled. 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. \ No newline at end of file diff --git a/docs/async.md b/docs/async.md index 40571fb..f642727 100644 --- a/docs/async.md +++ b/docs/async.md @@ -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)``` -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 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. ``` 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)] diff --git a/docs/type_ids.md b/docs/type_ids.md index cee2ed3..7e81a4a 100644 --- a/docs/type_ids.md +++ b/docs/type_ids.md @@ -42,7 +42,7 @@ enum TypeID : quint8 ```TEXT``` This is text that can be displayed directly to the user, passed as command line arguments to be processed or used to carry text data within other data types. -format: ```[UTF-16LE_string] (no BOM)``` +format: ```[UTF-8_string] (no BOM)``` ```GEN_FILE``` This is a file transfer type id that can be used to transfer any file type (music, photos, documents, etc...). It operates in its own protocol of sorts. The 1st GEN_FILE frame received by the host or client is TEXT parameters similar to what you see in terminal command lines with at least one of the arguments listed below. The next set of GEN_FILE frames received by the host or client is then the binary data that needs to be written to an open file or streamed until the limit defined in -len is meet. @@ -77,7 +77,7 @@ This id can be treated exactly like TEXT except this tells the client that the c This is similar to PRIV_TEXT expect it is not asking for private information. It is simply prompting for non-sensitive information from the user. ```BIG_TEXT``` -Also formatted exactly like TEXT but this indicates to the client that this is a large body of text that is recommended to be word wrapped when displaying to the user. It can contain line breaks so clients are also recommended to honor those line breaks. +Also formatted exactly like TEXT but this indicates to the client that this is a large body of text that is recommended to be word wrapped when displaying to the user. It can contain line breaks so clients are recommended to honor those line breaks. ```IDLE``` All commands started during the session returns this type id when it has finished it's task. It carries a 16bit unsigned integer indicating the result of the task that the command was running. @@ -97,13 +97,13 @@ enum RetCode : quint16 notes: 1. the custom return code can be additional data added to the end of the 16bit integer that can carry additional data about the result of the task. it can - be any format that the module itself decides it can should be. nothing is - stopping modules from defining return codes beyond the value of 7 but it is - advised not to because this enum might be expanded in the future. + be any format that the module itself decides it to be. nothing is stopping + modules from defining return codes beyond the value of 7 but it is advised + not to because this enum might be expanded in the future. ``` ```TERM_CMD``` -This type id doesn't carry any actual data. It is used to tell the host to stop/terminate the command id and branch id that was used to send it. It does not actually terminate the module's process within the host, it only simply tells it to stop what it is currently doing. This will also terminate any commands in a prompt/more input state. +This type id doesn't carry any actual data. It is used to tell the host to stop/terminate the command id and branch id that was used to send it. It does not actually terminate the module's process within the host, it will only tell it to stop what it is currently doing. This will also terminate any commands in a prompt state. ```KILL_CMD``` This works similarly to TERM_CMD except it will also terminate the module process. The module process will have 3 seconds to shutdown gracefully before it is force killed by the host session. @@ -143,12 +143,12 @@ This is a data structure that carries information about a file system object (fi 2. bytes[1-8] - creation time in msec since Epoch UTC (64bit little endian uint) 3. bytes[9-16] - modification time in msec since Epoch UTC (64bit little endian uint) 4. bytes[17-24] - file size (64bit little endian uint) - 5. bytes[25-n] - file name (UTF16-LE string, 16bit terminated) - 6. bytes[n-n] - symmlink target if it is a symmlink (UTF16-LE string, 16bit terminated) + 5. bytes[25-n] - file name (UTF8 string, NULL terminated) + 6. bytes[n-n] - symmlink target if it is a symmlink (UTF8 string, NULL terminated) notes: - 1. 16bit terminated UTF-16LE strings are basically - terminated by 2 bytes of 0x00. + 1. NULL terminated UTF-8 strings are basically tailed + by a single 0x00 byte to indicate the end-of-string. 2. the symmlink target is empty if not a symmlink but the terminator should still be present. @@ -169,11 +169,11 @@ This carry some user account and session information about a peer client connect ``` format: - 1. bytes[0-27] 28bytes - session id (224bit hash) - 2. bytes[28-59] 32bytes - user id (256bit hash) - 3. bytes[60-107] 48bytes - user name (TEXT - padded with 0x00) - 4. bytes[108-241] 134bytes - app name (TEXT - padded with 0x00) - 5. bytes[242-305] 64bytes - disp name (TEXT - padded with 0x00) + 1. bytes[0-27] 28bytes - session id (224bit hash) + 2. bytes[28-59] 32bytes - user id (256bit hash) + 3. bytes[60-83] 24bytes - user name (TEXT - padded with 0x00) + 4. bytes[84-115] 32bytes - app name (TEXT - padded with 0x00) + 5. bytes[116-139] 24bytes - disp name (TEXT - padded with 0x00) notes: 1. the session id is unique to the peer's session connection only. it @@ -183,23 +183,23 @@ This carry some user account and session information about a peer client connect even when the user name changes and across all clients logged into the same account. - 3. the display name is the preffered display name of the peer. clients + 3. the display name is the preferred display name of the peer. clients are encouraged to use this rather than the user name when displaying - peer info to the user. if empty, it's ok to just fall back to the user - name. + peer info to the end user. if empty, then it is ok to fall back to + displaying the user name. ``` ```PING_PEERS``` -This is formatted extactly as PEER_INFO except it is used by the ASYNC_LIMITED_CAST [async](async.md) command to tell all peer sessions that receive it to send a PEER_INFO frame about the local session to their own clients and return PEER_INFO frames about themselves to the local session. +This is formatted extactly as PEER_INFO except it is used by the ASYNC_LIMITED_CAST [async](async.md) command to tell all peer sessions that receive it to forward the same frame to their own clients and return PEER_INFO frames about themselves to the session that sent the PING_PEERS. ```MY_INFO``` This contains all of the information found in ```PEER_INFO``` for the local session but also includes the following: ``` format: - 1. bytes[306-433] 128bytes - email (TEXT - padded with 0x00) - 2. bytes[434-437] 4bytes - host rank (32bit unsigned int) - 3. bytes[438] 1byte - is email confirmed? (0x00 false, 0x01 true) + 1. bytes[140-203] 64bytes - email (TEXT - padded with 0x00) + 2. bytes[204-207] 4bytes - host rank (32bit unsigned int) + 3. bytes[208] 1byte - is email confirmed? (0x00 false, 0x01 true) ``` ```NEW_CMD``` @@ -207,13 +207,13 @@ This contains information about a new command that was added to the current sess ``` format: - 1. bytes[0-1] 2bytes - 16bit LE unsigned int (command id) - 2. bytes[2] 1byte - 8bit LE unsigned int (genfile type) - 3. bytes[3-130] 128bytes - command name (TEXT - padded with 0x00) - 4. bytes[131-258] 128bytes - library name (TEXT - padded with 0x00) - 5. bytes[259-n] variable - short text (16bit null terminated) - 6. bytes[n-n] variable - io text (16bit null terminated) - 7. bytes[n-n] variable - long text (16bit null terminated) + 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) notes: 1. the genfile type is numerical value of 2, 3 or 0. a value of 2 @@ -288,13 +288,13 @@ This contains public information about a channel member. 2. bytes[8-39] 32bytes - user id (256bit hash) 3. bytes[40] 1byte - is invite? (0x00=false, 0x01=true) 4. bytes[41] 1byte - member's channel privilege level (8bit unsigned int) - 5. bytes[42-n] variable - user name (TEXT - 16bit null terminated) - 6. bytes[n-n] variable - display name (TEXT - 16bit null terminated) - 7. bytes[n-n] variable - channel name (TEXT - 16bit null terminated) + 5. bytes[42-n] variable - user name (TEXT - null terminated) + 6. bytes[n-n] variable - display name (TEXT - null terminated) + 7. bytes[n-n] variable - channel name (TEXT - null terminated) notes: - 1. a 16bit null terminated TEXT formatted string ended with 2 bytes of - (0x00) to indicate the end of the string data. + 1. a null terminated TEXT formatted string ends with a 0x00 to + indicate the end of the string. 2. the member's privilege level can be any of the values discribed in section [4.3](host_features.md). diff --git a/icons/win_icon.ico b/icons/win_icon.ico new file mode 100644 index 0000000..34e88c6 Binary files /dev/null and b/icons/win_icon.ico differ diff --git a/install.py b/install.py new file mode 100644 index 0000000..6254fba --- /dev/null +++ b/install.py @@ -0,0 +1,372 @@ +#!/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) \ No newline at end of file diff --git a/src/cmd_line.cpp b/src/cmd_line.cpp index 3648ed5..6e864aa 100644 --- a/src/cmd_line.cpp +++ b/src/cmd_line.cpp @@ -164,7 +164,7 @@ void CmdLine::toHost(const QString &cmdName, const QString &args) { if (flags & HOST_HOOK) { - emit dataToHookedHost(toTEXT(args), TEXT); + emit dataToHookedHost(args.toUtf8(), TEXT); } else if (!cmdName.isEmpty()) { @@ -178,11 +178,11 @@ void CmdLine::toHost(const QString &cmdName, const QString &args) { emit setHostCmdId(cmdId); emit setGenfileType(Shared::genfileTypes->value(cmdId)); - emit dataToGenFile(toTEXT(args)); + emit dataToGenFile(args.toUtf8()); } else { - emit dataToHost(cmdId, toTEXT(args), TEXT); + emit dataToHost(cmdId, args.toUtf8(), TEXT); } } } @@ -225,7 +225,7 @@ void CmdLine::procCmdLine(const QString &line) if (flags & GEN_HOOK) { - emit dataToGenFile(toTEXT(argsLine)); + emit dataToGenFile(argsLine.toUtf8()); } else if (Shared::clientCmds->contains(cmdName)) { diff --git a/src/cmd_objs/host_doc.cpp b/src/cmd_objs/host_doc.cpp index cfccfcb..e45927f 100644 --- a/src/cmd_objs/host_doc.cpp +++ b/src/cmd_objs/host_doc.cpp @@ -18,13 +18,27 @@ 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; - if (import.size() >= 259) + if (import.size() >= 131) // within safe import size { - cmdId = static_cast(rdInt(import.mid(0, 2))); + cmdId = static_cast(rdInt(import.mid(0, 2))); // 1. + + auto cmdName = QString(import.mid(3, 64)).toLower(); // 3. - QString cmdName = fromTEXT(import.mid(3, 128)).toLower(); QString num; for (int i = 1; Shared::clientCmds->contains(cmdName + num); ++i) @@ -34,18 +48,18 @@ HostDoc::HostDoc(const QByteArray &import, QObject *parent) : Command(parent) setObjectName(cmdName + num); - if ((import[2] == GEN_UPLOAD) || (import[2] == GEN_DOWNLOAD)) + if ((import[2] == GEN_UPLOAD) || (import[2] == GEN_DOWNLOAD)) // 2. { Shared::genfileCmds->insert(cmdId, objectName()); Shared::genfileTypes->insert(cmdId, static_cast(import[2])); } - quint32 offs = 259; + quint32 offs = 131; // 5. - libTxt = fromTEXT(import.mid(131, 128)); + libTxt = QString(import.mid(67, 64)); // 4. shortTxt = readNullTermText(import, &offs); - ioTxt = readNullTermText(import, &offs); - longTxt = readNullTermText(import, &offs); + ioTxt = readNullTermText(import, &offs); // 6. + longTxt = readNullTermText(import, &offs); // 7. valid = true; Shared::hostCmds->insert(cmdId, objectName()); @@ -55,29 +69,23 @@ HostDoc::HostDoc(const QByteArray &import, QObject *parent) : Command(parent) QString HostDoc::readNullTermText(const QByteArray &data, quint32 *offs) { - static const QByteArray null16(2, 0x00); - QString ret; - quint32 len = static_cast(data.size()); - for (; *offs < len; *offs += 2) + auto len = static_cast(data.size()); + + for (; *offs < len; *offs += 1) { - if ((*offs + 2) < len) + if (data[*offs] == 0x00) { - QByteArray chr = QByteArray::fromRawData(data.data() + *offs, 2); - - if (chr == null16) - { - break; - } - else - { - ret.append(fromTEXT(chr)); - } + break; + } + else + { + ret.append(data[*offs]); } } - *offs += 2; + *offs += 1; return ret; } diff --git a/src/cmd_objs/info.cpp b/src/cmd_objs/info.cpp index 34b8ad0..5b5fd37 100644 --- a/src/cmd_objs/info.cpp +++ b/src/cmd_objs/info.cpp @@ -45,9 +45,9 @@ void About::dispInfo(Command *cmdObj) QTextStream txtOut(&txt); wordWrap("i/o: ", txtOut, cmdObj->ioText(), Shared::mainWidget); - txtOut << "" << endl; + txtOut << "" << Qt::endl; wordWrap("library: ", txtOut, cmdObj->libText(), Shared::mainWidget); - txtOut << "" << endl; + txtOut << "" << Qt::endl; wordWrap("usage: ", txtOut, cmdObj->longText(), Shared::mainWidget); cacheTxt(TEXT, txt); @@ -115,12 +115,12 @@ void About::dataIn(const QString &argsLine) QString txt; QTextStream txtOut(&txt); - txtOut << libText() << endl << 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" << endl; - txtOut << "WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE." << endl << endl; - txtOut << "run: 'ls_cmds' to see all available commands." << endl << endl; - txtOut << "for more detailed information about a command run: 'about '" << endl << endl; + txtOut << libText() << Qt::endl << Qt::endl; + txtOut << "Based on QT " << QT_VERSION_STR << " " << 8 * QT_POINTER_SIZE << "bit" << Qt::endl << Qt::endl; + txtOut << "The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE" << Qt::endl; + txtOut << "WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE." << Qt::endl << Qt::endl; + txtOut << "run: 'ls_cmds' to see all available commands." << Qt::endl << Qt::endl; + txtOut << "for more detailed information about a command run: 'about '" << Qt::endl << Qt::endl; cacheTxt(TEXT, txt); } @@ -145,8 +145,8 @@ void ListCmds::ls(QHash *cmdObjs, QTextStream &txtOut, const cmdNames.sort(Qt::CaseInsensitive); - txtOut << endl; - txtOut << title << endl << endl; + txtOut << Qt::endl; + txtOut << title << Qt::endl << Qt::endl; for (int i = 0; i < cmdNames.size(); ++i) { diff --git a/src/cmd_objs/status.cpp b/src/cmd_objs/status.cpp index adfe5ec..ef056ea 100644 --- a/src/cmd_objs/status.cpp +++ b/src/cmd_objs/status.cpp @@ -34,20 +34,20 @@ void Status::dataIn(const QString &argsLine) QString txt; QTextStream txtOut(&txt); - txtOut << "--Local data" << endl << endl; - txtOut << " Client version : " << QCoreApplication::applicationVersion() << endl; - txtOut << " Connected? : " << boolText(*Shared::connectedToHost) << endl; - txtOut << " Address : " << *Shared::hostAddress << endl; - txtOut << " Port : " << *Shared::hostPort << endl; + txtOut << "--Local data" << Qt::endl << Qt::endl; + txtOut << " Client version : " << QCoreApplication::applicationVersion() << Qt::endl; + txtOut << " Connected? : " << boolText(*Shared::connectedToHost) << Qt::endl; + txtOut << " Address : " << *Shared::hostAddress << Qt::endl; + txtOut << " Port : " << *Shared::hostPort << Qt::endl; if (*Shared::connectedToHost) { - txtOut << "" << endl; - txtOut << "--Session data" << endl << endl; - txtOut << " Client address : " << Shared::socket->localAddress().toString() << endl; - txtOut << " Host address : " << Shared::socket->peerAddress().toString() << endl; - txtOut << " Session id : " << Shared::sessionId->toHex() << endl; - txtOut << " Host version : " << verText() << endl; + txtOut << "" << Qt::endl; + txtOut << "--Session data" << Qt::endl << Qt::endl; + txtOut << " Client address : " << Shared::socket->localAddress().toString() << Qt::endl; + txtOut << " Host address : " << Shared::socket->peerAddress().toString() << Qt::endl; + txtOut << " Session id : " << Shared::sessionId->toHex() << Qt::endl; + txtOut << " Host version : " << verText() << Qt::endl; txtOut << " GEN_FILE commands : "; QStringList genCmds = Shared::genfileCmds->values(); @@ -56,13 +56,13 @@ void Status::dataIn(const QString &argsLine) { genCmds.sort(Qt::CaseInsensitive); - txtOut << "" << endl; + txtOut << "" << Qt::endl; } for (int i = 0; i < genCmds.size(); ++i) { - if (i != 0) txtOut << " " << genCmds[i] << endl; - else txtOut << genCmds[i] << endl; + if (i != 0) txtOut << " " << genCmds[i] << Qt::endl; + else txtOut << genCmds[i] << Qt::endl; } } diff --git a/src/common.cpp b/src/common.cpp index 7c268ad..caefc18 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -138,25 +138,9 @@ QByteArray wrFrame(quint16 cmdId, const QByteArray &data, uchar dType) return typeBa + cmdBa + branBa + sizeBa + data; } -QByteArray toTEXT(const QString &txt) +QByteArray toFixedTEXT(const QString &txt, int len) { - 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); + return txt.toUtf8().leftJustified(len, 0, true); } QByteArray wrInt(quint64 num, int numOfBits) @@ -211,10 +195,11 @@ QStringList parseArgs(const QByteArray &data, int maxArgs) { QStringList ret; QString arg; - QString line = fromTEXT(data); - bool inDQuotes = false; - bool inSQuotes = false; - bool escaped = false; + + auto line = QString(data); + auto inDQuotes = false; + auto inSQuotes = false; + auto escaped = false; for (int i = 0; i < line.size(); ++i) { @@ -280,7 +265,7 @@ QStringList parseArgs(const QByteArray &data, int maxArgs) QStringList parseArgs(const QString &line) { - return parseArgs(toTEXT(line), -1); + return parseArgs(line.toUtf8(), -1); } void wordWrap(const QString &label, QTextStream &txtOut, const QString &txtIn, QWidget *measureWid) @@ -313,8 +298,8 @@ void wordWrap(const QString &label, QTextStream &txtOut, const QString &txtIn, Q line.chop(1); - if (labelWr) txtOut << label << line << endl; - else txtOut << indent << line << endl; + if (labelWr) txtOut << label << line << Qt::endl; + else txtOut << indent << line << Qt::endl; labelWr = false; } @@ -325,8 +310,8 @@ void wordWrap(const QString &label, QTextStream &txtOut, const QString &txtIn, Q // the case, the line is added to the return and then // cleared. - if (labelWr) txtOut << label << line << endl; - else txtOut << indent << line << endl; + if (labelWr) txtOut << label << line << Qt::endl; + else txtOut << indent << line << Qt::endl; line.clear(); diff --git a/src/common.h b/src/common.h index 8df54f8..263c389 100644 --- a/src/common.h +++ b/src/common.h @@ -71,12 +71,11 @@ #define DEFAULT_MAX_LINES 1000 #define RDBUFF 16777215 #define DEFAULT_PORT 35516 -#define TXT_CODEC "UTF-16LE" #define BOOKMARK_FOLDER "bookmarks" #define CONFIG_FILENAME "config_v3.json" #define APP_NAME "Cmdr" #define APP_TARGET "cmdr" -#define APP_VERSION "3.3" +#define APP_VERSION "3.4" enum TypeID : quint8 { @@ -168,11 +167,9 @@ void wordWrap(const QString &label, QTextStream &txtOut, const QString &t bool argExists(const QString &key, const QStringList &args); QByteArray wrInt(quint64 num, int numOfBits); QByteArray wrFrame(quint16 cmdId, const QByteArray &data, uchar dType); -QByteArray toTEXT(const QString &txt); -QByteArray fixedToTEXT(const QString &txt, int len); +QByteArray toFixedTEXT(const QString &txt, int len); QStringList parseArgs(const QByteArray &data, int maxArgs); QStringList parseArgs(const QString &line); -QString fromTEXT(const QByteArray &txt); QString appDataDir(); QString getParam(const QString &key, const QStringList &args); QString extractCmdName(const QByteArray &data); diff --git a/src/gen_file.cpp b/src/gen_file.cpp index eac032d..ac1b53e 100644 --- a/src/gen_file.cpp +++ b/src/gen_file.cpp @@ -269,7 +269,7 @@ QByteArray Genfile::autoFill(const QByteArray &data) args.append("-len"); args.append(len); - return toTEXT(args.join(' ')); + return args.join(' ').toUtf8(); } else { @@ -372,7 +372,7 @@ void Genfile::dataIn(const QByteArray &data) } else if (flags & CONFIRM_NEEDED) { - QString ans = fromTEXT(data); + auto ans = QString(data); if (QRegExp("y", Qt::CaseInsensitive).exactMatch(ans)) { diff --git a/src/session.cpp b/src/session.cpp index b14a4b4..7fc6bb0 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -83,7 +83,7 @@ void Session::sockerr(QAbstractSocket::SocketError err) } else { - if (sslErrors().isEmpty()) + 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 @@ -128,11 +128,12 @@ void Session::sslErrs(const QList &errors) void Session::isConnected() { - // client header format: [4bytes(tag)][134bytes(appName)][272bytes(padding)] + // client header format: [4bytes(tag)][32bytes(appName)][128bytes(modInst)][128bytes(padding)] // tag = 0x4D, 0x52, 0x43, 0x49 (MRCI) - // appName = UTF16LE string (padded with 0x00) - // padding = just a string of 0x00 (reserved for future expansion) + // appName = UTF8 string (padded with 0x00) + // modInst = UTF8 string (padded with 0x00) + // padding = 128 bytes of (0x00) cacheTxt(TEXT, "Connected.\n"); @@ -141,18 +142,10 @@ void Session::isConnected() QByteArray header; header.append(SERVER_HEADER_TAG); - header.append(fixedToTEXT(appName, 134)); - header.append(QByteArray(272, 0)); + header.append(toFixedTEXT(appName, 32)); + header.append(QByteArray(256, 0)); - if (header.size() == CLIENT_HEADER_LEN) - { - write(header); - } - else - { - cacheTxt(ERR, "\nerr: client bug! - header len not equal to " + QString::number(CLIENT_HEADER_LEN) + " bytes.\n"); - disconnectFromHost(); - } + write(header); } void Session::handShakeDone() @@ -162,13 +155,13 @@ void Session::handShakeDone() QString txt; QTextStream txtOut(&txt); - txtOut << "SSL Handshake sucessful." << endl - << "cipher details:" << endl - << " #cipher - " << cipher.name() << endl - << " #protocol - " << cipher.protocolString() << endl - << " #bit_depth - " << cipher.usedBits() << endl - << " #key_exchange - " << cipher.keyExchangeMethod() << endl - << " #authentication - " << cipher.authenticationMethod() << endl; + txtOut << "SSL Handshake sucessful." << Qt::endl + << "cipher details:" << Qt::endl + << " #cipher - " << cipher.name() << Qt::endl + << " #protocol - " << cipher.protocolString() << Qt::endl + << " #bit_depth - " << cipher.usedBits() << Qt::endl + << " #key_exchange - " << cipher.keyExchangeMethod() << Qt::endl + << " #authentication - " << cipher.authenticationMethod() << Qt::endl; cacheTxt(TEXT, txt); } @@ -276,21 +269,21 @@ void Session::procAsync(const QByteArray &data) { if (dType == TEXT) { - cacheTxt(dType, "\ncast_text: " + fromTEXT(data) + "\n"); + cacheTxt(dType, "\ncast_text: " + QString(data) + "\n"); } else if (dType == BIG_TEXT) { QString txt; QTextStream stream(&txt); - wordWrap("cast_text: ", stream, fromTEXT(data), Shared::mainWidget); + wordWrap("cast_text: ", stream, QString(data), Shared::mainWidget); cacheTxt(TEXT, "\n"); cacheTxt(TEXT, txt); } } else if (cmdId == ASYNC_RDY) { - cacheTxt(dType, fromTEXT(data)); + cacheTxt(dType, QString(data)); hook = 0; } @@ -386,7 +379,7 @@ void Session::dataFromHost(const QByteArray &data) { if ((dType == TEXT) || (dType == PRIV_TEXT) || (dType == BIG_TEXT) || (dType == ERR) || (dType == PROMPT_TEXT)) { - cacheTxt(dType, fromTEXT(data)); + cacheTxt(dType, QString(data)); } else if (dType == PROG) { @@ -466,7 +459,7 @@ void Session::dataIn() cacheTxt(TEXT, "Detected host version: " + verText() + "\n"); - if (*Shared::tcpRev == 1) + if (*Shared::tcpRev == 2) { *Shared::sessionId = servHeader.mid(9, 28); *Shared::connectedToHost = true; @@ -478,6 +471,7 @@ void Session::dataIn() if (reply == 2) { cacheTxt(TEXT, "Starting SSL handshake.\n"); + startClientEncryption(); } else diff --git a/src/session.h b/src/session.h index 93c7c4f..f2f8386 100644 --- a/src/session.h +++ b/src/session.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "cmd_objs/command.h" @@ -41,7 +42,6 @@ #include "cmd_objs/host_doc.h" #define SERVER_HEADER_TAG "MRCI" -#define CLIENT_HEADER_LEN 410 #define SERVER_HEADER_LEN 37 #define FRAME_HEADER_LEN 8 diff --git a/templates/linux_icon.desktop b/templates/linux_icon.desktop new file mode 100644 index 0000000..84770b6 --- /dev/null +++ b/templates/linux_icon.desktop @@ -0,0 +1,9 @@ +[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; diff --git a/templates/linux_run_script.sh b/templates/linux_run_script.sh new file mode 100644 index 0000000..801f13e --- /dev/null +++ b/templates/linux_run_script.sh @@ -0,0 +1,5 @@ +#!/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 \ No newline at end of file diff --git a/templates/linux_uninstall.sh b/templates/linux_uninstall.sh new file mode 100644 index 0000000..abb28fb --- /dev/null +++ b/templates/linux_uninstall.sh @@ -0,0 +1,21 @@ +#!/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" \ No newline at end of file diff --git a/templates/win_icon.vbs b/templates/win_icon.vbs new file mode 100644 index 0000000..823d751 --- /dev/null +++ b/templates/win_icon.vbs @@ -0,0 +1,6 @@ +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 \ No newline at end of file diff --git a/templates/windows_uninstall.bat b/templates/windows_uninstall.bat new file mode 100644 index 0000000..192a7dd --- /dev/null +++ b/templates/windows_uninstall.bat @@ -0,0 +1,6 @@ +$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" \ No newline at end of file