Added Windows Build/Install Support
linux_build.sh is now replaced by the build.py and install.py Python scripts. with this comes cross platform build/install support between Windows and Linux. as of v4.x.2.x of the MRCI protocol, the TEXT type id is now formatted in UTF8 instead of UTF16LE unicode. this change also affected the tcp rev and several async commands so client code had to be ajusted accordingly.
This commit is contained in:
parent
0c575ed8c9
commit
fb4d0bed87
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -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
|
||||
|
|
22
Cmdr.pro
22
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 \
|
||||
|
|
281
build.py
Normal file
281
build.py
Normal file
|
@ -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()
|
|
@ -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.
|
|
@ -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)]
|
||||
|
|
|
@ -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).
|
||||
|
|
BIN
icons/win_icon.ico
Normal file
BIN
icons/win_icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
372
install.py
Normal file
372
install.py
Normal file
|
@ -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)
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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<quint16>(rdInt(import.mid(0, 2)));
|
||||
cmdId = static_cast<quint16>(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<quint8>(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<quint32>(data.size());
|
||||
|
||||
for (; *offs < len; *offs += 2)
|
||||
auto len = static_cast<quint32>(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;
|
||||
}
|
||||
|
|
|
@ -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 <command>'" << 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 <command>'" << Qt::endl << Qt::endl;
|
||||
|
||||
cacheTxt(TEXT, txt);
|
||||
}
|
||||
|
@ -145,8 +145,8 @@ void ListCmds::ls(QHash<QString, Command *> *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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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<QSslError> &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
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <QSslCipher>
|
||||
#include <QSslCertificate>
|
||||
#include <QSocketNotifier>
|
||||
#include <QSslConfiguration>
|
||||
#include <QTimer>
|
||||
|
||||
#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
|
||||
|
||||
|
|
9
templates/linux_icon.desktop
Normal file
9
templates/linux_icon.desktop
Normal file
|
@ -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;
|
5
templates/linux_run_script.sh
Normal file
5
templates/linux_run_script.sh
Normal file
|
@ -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
|
21
templates/linux_uninstall.sh
Normal file
21
templates/linux_uninstall.sh
Normal file
|
@ -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"
|
6
templates/win_icon.vbs
Normal file
6
templates/win_icon.vbs
Normal file
|
@ -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
|
6
templates/windows_uninstall.bat
Normal file
6
templates/windows_uninstall.bat
Normal file
|
@ -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"
|
Loading…
Reference in New Issue
Block a user