From 72d57a0b106931cd9160b962f49039419b3075b6 Mon Sep 17 00:00:00 2001 From: Maurice ONeal Date: Fri, 8 Nov 2019 22:06:09 -0500 Subject: [PATCH] Major upgrade and module interface changes Made some major changes to the project to facilitate a lighter code base and the must flexible module interface possible. -the mutli-process architecture now operate at the command object level so each command now operate in it's own process instead of a single process handling multiple command objects. -each module is now an independent application that will now tell the session object all of the commands it can run via named pipe. during command execution, it will run the requested command object also running io with the session object via named pipe. with this change, it is now possible for modules to be developed in different versions or QT or entirely different languages. the only requirement is the need to support named pipes. shared memory segments is also a nice to have but not needed. -clients can now run multiple instances of the same command via changes to the protocol. mrci frames will now include a branch id along with the command id. the branch id can be used by clients to differentiate the io between instances of the same command. -the command states are longer controlled by a single object. it will now be up to the command object (internal/exterenal) to send an IDLE frame to the client to notify it that the command has finished. the session object will still track if the command is in idle state or not but not directly control it. -must async commands now use binary formatted data instead of TEXT as a way to reduce overhead. -all command objects can now send async commands. it is no longer limited to just internal commands, however; the data of these async commands are verified by session in some way to prevent host crashing due to malformed data. -changed up the database structure to rely more on user ids, channel ids and removed all foreign keys pointing to user names, channel names and sub-channel names. also removed the groups table altogether. instead, the host rank is now directly attached to the user data in the users table. -changed the query object to now support the INNER JOIN SQL clause. this change was needed to support the new database structure. -version negotiation is now one-way via tcp connection or module interface. the host will make it's own verion numner known to the client connected via tcp or the module connected via named pipe. it will now be entirely up to the client or module to decide if they support the host. another change in this regard is the removal of the import rev for the modules. compatibility for modules shall now use just the host verion. -removed ls_cmds and cmd_info. the NEW_CMD frame now carries all information about the command (cmd_id, cmd_name, summery, io and full_description) so it is now possible for the clients to display the command documentation instead of the host. Documentation for the internal commands were updated to reflect the changes but all other documentation will need to be updated in the near future. --- MRCI.pro | 19 +- cmd_docs.qrc | 11 +- docs/Type_IDs.md | 86 +- docs/intern_commands/add_acct.md | 2 +- docs/intern_commands/add_cert.md | 4 +- docs/intern_commands/add_ch.md | 2 +- docs/intern_commands/add_group.md | 11 - docs/intern_commands/add_mod.md | 10 +- docs/intern_commands/add_ranked_cmd.md | 6 +- docs/intern_commands/add_rdonly_flag.md | 2 +- docs/intern_commands/add_sub_ch.md | 2 +- docs/intern_commands/cast.md | 2 +- docs/intern_commands/close_host.md | 2 +- docs/intern_commands/cmd_info.md | 11 - docs/intern_commands/fs_cd.md | 2 +- docs/intern_commands/fs_copy.md | 2 +- docs/intern_commands/fs_delete.md | 2 +- docs/intern_commands/fs_download.md | 6 +- docs/intern_commands/fs_info.md | 2 +- docs/intern_commands/fs_move.md | 2 +- docs/intern_commands/fs_upload.md | 2 +- docs/intern_commands/lock_acct.md | 2 +- docs/intern_commands/ls_act_log.md | 2 +- docs/intern_commands/ls_auth_log.md | 6 +- docs/intern_commands/ls_ch_members.md | 4 +- docs/intern_commands/ls_cmds.md | 11 - docs/intern_commands/ls_dbg.md | 2 +- docs/intern_commands/ls_groups.md | 14 - docs/intern_commands/ls_mods.md | 8 +- docs/intern_commands/ls_ranked_cmds.md | 5 +- docs/intern_commands/ls_sub_chs.md | 2 +- docs/intern_commands/ls_users.md | 8 +- docs/intern_commands/open_sub_ch.md | 4 +- docs/intern_commands/p2p_close.md | 2 +- docs/intern_commands/p2p_open.md | 2 +- docs/intern_commands/p2p_request.md | 2 +- docs/intern_commands/pause.md | 11 - docs/intern_commands/preview_email.md | 2 +- docs/intern_commands/recover_acct.md | 4 +- docs/intern_commands/remove_ch_member.md | 2 +- docs/intern_commands/request_pw_reset.md | 2 +- docs/intern_commands/restart_host.md | 2 +- docs/intern_commands/resume.md | 11 - docs/intern_commands/rm_acct.md | 2 +- docs/intern_commands/rm_mod.md | 6 +- docs/intern_commands/rm_ranked_cmd.md | 6 +- docs/intern_commands/rm_rdonly_flag.md | 2 +- docs/intern_commands/rm_sub_ch.md | 2 +- docs/intern_commands/set_active_flag.md | 2 +- docs/intern_commands/set_disp_name.md | 4 +- docs/intern_commands/set_email.md | 2 +- docs/intern_commands/set_email_template.md | 6 +- docs/intern_commands/set_group.md | 11 - docs/intern_commands/set_group_rank.md | 11 - docs/intern_commands/set_member_level.md | 2 +- docs/intern_commands/set_pw.md | 4 +- docs/intern_commands/set_sub_ch_level.md | 2 +- docs/intern_commands/set_user_rank.md | 11 + docs/intern_commands/term.md | 11 - docs/intern_commands/to_peer.md | 2 +- docs/intern_commands/trans_group.md | 11 - src/async_funcs.cpp | 386 +++++ src/cmd_executor.cpp | 821 ---------- src/cmd_executor.h | 108 -- src/cmd_object.cpp | 219 +++ src/cmd_object.h | 99 ++ src/cmd_proc.cpp | 643 ++++++++ src/cmd_proc.h | 144 ++ src/commands/acct_recovery.cpp | 224 ++- src/commands/acct_recovery.h | 42 +- src/commands/admin.cpp | 223 +-- src/commands/admin.h | 14 +- src/commands/auth.cpp | 137 +- src/commands/auth.h | 12 +- src/commands/bans.cpp | 23 +- src/commands/bans.h | 13 +- src/commands/cast.cpp | 179 ++- src/commands/cast.h | 34 +- src/commands/certs.cpp | 57 +- src/commands/certs.h | 15 +- src/commands/channels.cpp | 471 +++--- src/commands/channels.h | 71 +- src/commands/cmd_ranks.cpp | 65 +- src/commands/cmd_ranks.h | 11 +- src/commands/cmd_state.cpp | 105 -- src/commands/cmd_state.h | 65 - src/commands/command.cpp | 70 - src/commands/command.h | 163 -- src/commands/fs.cpp | 301 ++-- src/commands/fs.h | 53 +- src/commands/groups.cpp | 286 ---- src/commands/groups.h | 109 -- src/commands/info.cpp | 278 ++-- src/commands/info.h | 44 +- src/commands/mods.cpp | 358 +---- src/commands/mods.h | 29 +- src/commands/p2p.cpp | 117 +- src/commands/p2p.h | 25 +- src/commands/table_viewer.cpp | 372 +++-- src/commands/table_viewer.h | 38 +- src/commands/users.cpp | 306 ++-- src/commands/users.h | 62 +- src/common.cpp | 1252 +++------------ src/common.h | 390 ++--- src/db.cpp | 275 +++- src/db.h | 103 +- src/db_setup.cpp | 101 +- src/int_loader.cpp | 347 ---- src/main.cpp | 74 +- src/mem_share.cpp | 444 ++++++ src/mem_share.h | 112 ++ src/module.cpp | 311 ++++ src/{int_loader.h => module.h} | 45 +- src/session.cpp | 1652 ++++++-------------- src/session.h | 152 +- src/tcp_server.cpp | 230 ++- src/tcp_server.h | 41 +- 117 files changed, 5643 insertions(+), 7071 deletions(-) delete mode 100644 docs/intern_commands/add_group.md delete mode 100644 docs/intern_commands/cmd_info.md delete mode 100644 docs/intern_commands/ls_cmds.md delete mode 100644 docs/intern_commands/ls_groups.md delete mode 100644 docs/intern_commands/pause.md delete mode 100644 docs/intern_commands/resume.md delete mode 100644 docs/intern_commands/set_group.md delete mode 100644 docs/intern_commands/set_group_rank.md create mode 100644 docs/intern_commands/set_user_rank.md delete mode 100644 docs/intern_commands/term.md delete mode 100644 docs/intern_commands/trans_group.md create mode 100644 src/async_funcs.cpp delete mode 100644 src/cmd_executor.cpp delete mode 100644 src/cmd_executor.h create mode 100644 src/cmd_object.cpp create mode 100644 src/cmd_object.h create mode 100644 src/cmd_proc.cpp create mode 100644 src/cmd_proc.h delete mode 100644 src/commands/cmd_state.cpp delete mode 100644 src/commands/cmd_state.h delete mode 100644 src/commands/command.cpp delete mode 100644 src/commands/command.h delete mode 100644 src/commands/groups.cpp delete mode 100644 src/commands/groups.h delete mode 100644 src/int_loader.cpp create mode 100644 src/mem_share.cpp create mode 100644 src/mem_share.h create mode 100644 src/module.cpp rename src/{int_loader.h => module.h} (54%) diff --git a/MRCI.pro b/MRCI.pro index 6c52ded..40b5f24 100644 --- a/MRCI.pro +++ b/MRCI.pro @@ -40,58 +40,57 @@ win32 { } SOURCES += src/main.cpp \ + src/async_funcs.cpp \ + src/cmd_object.cpp \ + src/cmd_proc.cpp \ src/commands/channels.cpp \ src/commands/cmd_ranks.cpp \ src/commands/p2p.cpp \ + src/mem_share.cpp \ + src/module.cpp \ src/session.cpp \ src/db.cpp \ src/make_cert.cpp \ - src/int_loader.cpp \ src/tcp_server.cpp \ src/unix_signal.cpp \ - src/cmd_executor.cpp \ src/common.cpp \ src/shell.cpp \ src/db_setup.cpp \ src/commands/users.cpp \ src/commands/mods.cpp \ src/commands/info.cpp \ - src/commands/groups.cpp \ src/commands/cast.cpp \ src/commands/bans.cpp \ src/commands/admin.cpp \ src/commands/auth.cpp \ - src/commands/command.cpp \ - src/commands/cmd_state.cpp \ src/commands/acct_recovery.cpp \ src/commands/table_viewer.cpp \ src/commands/fs.cpp \ src/commands/certs.cpp HEADERS += \ + src/cmd_object.h \ + src/cmd_proc.h \ src/commands/channels.h \ src/commands/cmd_ranks.h \ src/commands/p2p.h \ + src/mem_share.h \ + src/module.h \ src/session.h \ src/db.h \ src/make_cert.h \ - src/int_loader.h \ src/tcp_server.h \ src/unix_signal.h \ - src/cmd_executor.h \ src/common.h \ src/shell.h \ src/db_setup.h \ src/commands/users.h \ src/commands/mods.h \ src/commands/info.h \ - src/commands/groups.h \ src/commands/cast.h \ src/commands/bans.h \ src/commands/admin.h \ src/commands/auth.h \ - src/commands/command.h \ - src/commands/cmd_state.h \ src/commands/acct_recovery.h \ src/commands/table_viewer.h \ src/commands/fs.h \ diff --git a/cmd_docs.qrc b/cmd_docs.qrc index b18e228..85ddeda 100644 --- a/cmd_docs.qrc +++ b/cmd_docs.qrc @@ -5,7 +5,6 @@ docs/intern_commands/add_ban.md docs/intern_commands/add_cert.md docs/intern_commands/add_ch.md - docs/intern_commands/add_group.md docs/intern_commands/add_mod.md docs/intern_commands/add_ranked_cmd.md docs/intern_commands/add_rdonly_flag.md @@ -15,7 +14,6 @@ docs/intern_commands/cert_info.md docs/intern_commands/close_host.md docs/intern_commands/close_sub_ch.md - docs/intern_commands/cmd_info.md docs/intern_commands/decline_ch.md docs/intern_commands/find_ch.md docs/intern_commands/force_set_email.md @@ -40,9 +38,7 @@ docs/intern_commands/ls_certs.md docs/intern_commands/ls_ch_members.md docs/intern_commands/ls_chs.md - docs/intern_commands/ls_cmds.md docs/intern_commands/ls_dbg.md - docs/intern_commands/ls_groups.md docs/intern_commands/ls_mods.md docs/intern_commands/ls_open_chs.md docs/intern_commands/ls_p2p.md @@ -55,7 +51,6 @@ docs/intern_commands/p2p_close.md docs/intern_commands/p2p_open.md docs/intern_commands/p2p_request.md - docs/intern_commands/pause.md docs/intern_commands/ping_peers.md docs/intern_commands/preview_email.md docs/intern_commands/recover_acct.md @@ -66,7 +61,6 @@ docs/intern_commands/request_new_user_name.md docs/intern_commands/request_pw_reset.md docs/intern_commands/restart_host.md - docs/intern_commands/resume.md docs/intern_commands/rm_acct.md docs/intern_commands/rm_ban.md docs/intern_commands/rm_cert.md @@ -79,16 +73,13 @@ docs/intern_commands/set_disp_name.md docs/intern_commands/set_email_template.md docs/intern_commands/set_email.md - docs/intern_commands/set_group_rank.md - docs/intern_commands/set_group.md docs/intern_commands/set_member_level.md docs/intern_commands/set_pw.md docs/intern_commands/set_sub_ch_level.md docs/intern_commands/set_user_name.md - docs/intern_commands/term.md docs/intern_commands/to_peer.md - docs/intern_commands/trans_group.md docs/intern_commands/verify_email.md docs/intern_commands/ch_owner_override.md + docs/intern_commands/set_user_rank.md diff --git a/docs/Type_IDs.md b/docs/Type_IDs.md index 548598c..a897800 100644 --- a/docs/Type_IDs.md +++ b/docs/Type_IDs.md @@ -3,26 +3,34 @@ All mrci frames transferred throughout this application are usually passed into various functions using a ```uchar``` numeric value to indicate the type of data being passed with the ```QByteArray``` that are also usually passed into the functions. The type id enum values are as follows: ``` -enum TypeID +enum TypeID : quint8 { - GEN_FILE = 30, - TEXT = 31, - ERR = 32, - PRIV_TEXT = 33, - IDLE = 34, - HOST_CERT = 35, - FILE_INFO = 36, - PEER_INFO = 37, - MY_INFO = 38, - PEER_STAT = 39, - P2P_REQUEST = 40, - P2P_CLOSE = 41, - P2P_OPEN = 42, - BYTES = 43, - SESSION_ID = 44, - NEW_CMD = 45, - CMD_ID = 46, - BIG_TEXT = 47 + GEN_FILE = 1, + TEXT = 2, + ERR = 3, + PRIV_TEXT = 4, + IDLE = 5, + HOST_CERT = 6, + FILE_INFO = 7, + PEER_INFO = 8, + MY_INFO = 9, + PEER_STAT = 10, + P2P_REQUEST = 11, + P2P_CLOSE = 12, + P2P_OPEN = 13, + BYTES = 14, + SESSION_ID = 15, + NEW_CMD = 16, + CMD_ID = 17, + BIG_TEXT = 18, + TERM_CMD = 19, + HOST_VER = 20, + PRIV_IPC = 21, + PUB_IPC = 22, + PUB_IPC_WITH_FEEDBACK = 23, + PING_PEERS = 24, + CH_MEMBER_INFO = 25, + CH_ID = 26 }; ``` @@ -131,8 +139,8 @@ This contains all of the information found in ```PEER_INFO``` for the local sess ``` format: 1. bytes[300-427] 128bytes - email (TEXT - padded with empty spaces) - 2. bytes[428-451] 24bytes - group name (TEXT - padded with empty spaces) - 3. bytes[452] 1byte - is email confirmed? (0x00 false, 0x01 true) + 2. bytes[428-431] 4bytes - host rank (32bit unsigned int) + 3. bytes[432] 1byte - is email confirmed? (0x00 false, 0x01 true) ``` ```NEW_CMD``` @@ -142,17 +150,19 @@ 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 - bool (0x01 or 0x00) (handles gen file) - 3. bytes[3-130] 128bytes - command name (TEXT - padded with empty spaces) - 4. bytes[131-258] 128bytes - library name (TEXT - padded with empty spaces) + 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) notes: 1. the handles gen file flag is a single byte 0x01 to indicate true and 0x00 to indicate false. clients need to be aware of which command handles the GEN_FILE mini protocol because it requires user input at both ends (host and client). - 2. the library name is what ever is returned by the command object's - ExternCommand::libText() function. this can contain the module name - and/or extra informaion the client can use to identify the command. + 2. the library name can contain the module name and/or extra informaion + the client can use to identify the library the command is a part of. ``` ```CMD_ID``` @@ -201,6 +211,30 @@ format: ```28bytes - session id (224bit sha3 hash)``` ```BYTES``` This contains arbitrary binary data of any format that is not specialized for any internal objects in the host. +```CH_MEMBER_INFO``` +This contains public information about a channel member. + +``` + format: + 1. bytes[0-7] 8bytes - channel id (64bit unsigned int) + 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) + + notes: + 1. a 16bit null terminated TEXT formatted string ended with 2 bytes of + (0x00) to indicate the end of the string data. + 2. the member's privilege level can be any of the values discribed in + section [5.3](Host_Features.md). + 3. is invite? indicates if this user has received an invite to join + that channel by has not accepted yet. if, accepted the user will + become a full member of the channel at the level indicated by this + data type. +``` + ### 4.3 GEN_FILE Example ### Setup: diff --git a/docs/intern_commands/add_acct.md b/docs/intern_commands/add_acct.md index da3a659..24a33c0 100644 --- a/docs/intern_commands/add_acct.md +++ b/docs/intern_commands/add_acct.md @@ -8,4 +8,4 @@ create a new host user account. ### Description ### -this creates a new user account with the user name given in -name and an email address used for account recovery in -email. the command will fail if the user name already exists. you can pass the optional -disp to set the display name for the new user account. the display name can be used by clients to present the user account to other clients without showing the true user name or make it easier for users to identify each other since the display name is not restricted by uniqueness. the display name can be anything; it's only restricted to 32 chars or less. \ No newline at end of file +this creates a new user account with the user name given in -name and an email address used for account recovery in -email. the command will fail if the user name or email address already exists. you can pass the optional -disp to set the display name for the new user account. the display name can be used by clients to present the user account to other clients without showing the true user name or make it easier for users to identify each other since the display name is not restricted by uniqueness. the display name can be anything; it's only restricted to 32 chars or less but it cannot contain new lines or lines breaks. \ No newline at end of file diff --git a/docs/intern_commands/add_cert.md b/docs/intern_commands/add_cert.md index 66f8864..af2d887 100644 --- a/docs/intern_commands/add_cert.md +++ b/docs/intern_commands/add_cert.md @@ -1,6 +1,6 @@ ### Summary ### -install a new SSL/TLS cert into the host from a local cert and private key file. +install a new SSL/TLS cert into the host. ### IO ### @@ -8,4 +8,4 @@ install a new SSL/TLS cert into the host from a local cert and private key file. ### Description ### -add an SSL/TLS certificate to a common name given in -name. the common name is used by clients to request certain certificate installed in the host. certificates usually come in seperate cert file and private key file pairs. -cert is the file path to the cert file and -priv is the path to the private key. the files must be formatted in Pem or Der (extension doesn't matter). once installed, the file pairs no longer need to exists for the host to use them. you can the pass the optional -force option to replace a common name if it already exists without asking a confirmation question. \ No newline at end of file +add an SSL/TLS certificate to a common name given in -name. the common name is used by clients to request a certain certificate installed in the host. certificates usually come in seperate cert file and private key file pairs. -cert is the file path to the cert file and -priv is the path to the private key. the files must be formatted in Pem or Der (extension doesn't matter). once installed, the file pairs no longer need to exists for the host to use them. you can the pass the optional -force option to replace a common name if it already exists without asking a confirmation question. \ No newline at end of file diff --git a/docs/intern_commands/add_ch.md b/docs/intern_commands/add_ch.md index 52a68c9..e392965 100644 --- a/docs/intern_commands/add_ch.md +++ b/docs/intern_commands/add_ch.md @@ -8,4 +8,4 @@ create a new channel. ### Description ### -create a new channel with a unique name given in -ch_name. this command will automatically add the currently logged in user as a member and set it as the channel owner. being a channel owner prevents you from being able to delete your account at a later time. you will need to name a new owner from the channel member list or delete the entire channel to allow account deletion. the host will automatically assign a unique channel id for the new channel. channels are used for broadcast peer to peer data transfers within the host and the base channel created by this command is used to manage who has access to such data. \ No newline at end of file +create a new channel with a unique name given in -ch_name. this command will automatically add the currently logged in user as a member and set it as the channel owner. being a channel owner prevents you from being able to delete your account at a later time. you will need to name a new owner from the channel member list or delete the entire channel to allow account deletion. the host will automatically assign a unique channel id for the new channel. channels are used to broadcast peer to peer data transfers within the host and is used to manage who has access to such data. \ No newline at end of file diff --git a/docs/intern_commands/add_group.md b/docs/intern_commands/add_group.md deleted file mode 100644 index cc6bfe3..0000000 --- a/docs/intern_commands/add_group.md +++ /dev/null @@ -1,11 +0,0 @@ -### Summary ### - -create a new host group. - -### IO ### - -```[-name (text)]/[text]``` - -### Description ### - -this will create a new host group with the unique group name given in -name. all new groups are initialized at rank 2. this can be reconfigured at any time using various group editing commands. host groups are used to manage the access level of all users. each user registered in the host must be assigned a group and each group must be assigned a host rank. the rank is numeric integer that indicates the value of the group's rank. The lower it's numeric value, the higher the level of access the users in that group have in the host with 0 being invalid. \ No newline at end of file diff --git a/docs/intern_commands/add_mod.md b/docs/intern_commands/add_mod.md index 0c04d42..d1b0f3b 100644 --- a/docs/intern_commands/add_mod.md +++ b/docs/intern_commands/add_mod.md @@ -1,15 +1,11 @@ ### Summary ### -upload a new module to install into the host. +add a new module to the host. ### IO ### -```[-client_file (path) -name (text) -len (int)]/[text]``` +```[-mod_path (path)]/[text]``` ### Description ### -upload a new module to the host using an archive file (.zip, .tar, .7z, etc...) or a loadable library file (.so, .dll, .a, etc...) using the GEN_FILE data type id. the module name is given in -name while GEN_FILE specific arguments like -len determine the size of the file being sent and -client_file determine the file location on the client's file system. note: the host will use -client_file argument to read the file's suffix and determine if it is an archive or library file. - -once successfully unloaded; if it is an archive file, all of it's contents are extracted to the module installation directory. the archive file must contain a loadable library file called 'main' with any of the host platform compatible library suffixes like (.so, .dll, .a, etc...). if a library file is uploaded, it is simply copied over to the module installation directory as 'main.' - -if successfully installed, all sessions are notified to load the new module. even after a successful install, the module can still fail to load. check the host debug log for failure details if that is the case. \ No newline at end of file +add a new module to the host in the form of a path to an executable file given in -mod_path. the path can be absolute, relative to the host working directory or can contain environmental variables. a module that gets successfully added to the database via this command does not guarantee it's commands will successfuly load. any failures during module loading will be logged in the host debug log. \ No newline at end of file diff --git a/docs/intern_commands/add_ranked_cmd.md b/docs/intern_commands/add_ranked_cmd.md index 10800a1..4d90a67 100644 --- a/docs/intern_commands/add_ranked_cmd.md +++ b/docs/intern_commands/add_ranked_cmd.md @@ -1,11 +1,11 @@ ### Summary ### -assign a rank to a command object name. +assign a rank to a module's command name. ### IO ### -```[-command (text) -rank (int)]/[text]``` +```[-mod (text) -command (text) -rank (int)]/[text]``` ### Description ### -this will assign the command rank in -rank to the command name given in -command. assigning ranks to commands give groups that have that rank or higher access to running the command. any commands that don't have an assigned rank is assumed a rank of 1. the lower the numeric value of the rank, the higher the rank (0 is invalid). some commands can claim immunity from host ranking which basically means the command is allowed to load/run regardless of the user's host rank. for internal commands, any commands that edit the user's own information like it's name, display name, password, etc including the login command and various session state commands have immunity. \ No newline at end of file +this will assign the command rank in -rank to the command name given in -command for the module executable given in -mod. assigning ranks to commands give users that have that rank or higher access to running the command. any commands that don't have an assigned rank is assumed a rank of 1. the lower the numeric value of the rank, the higher the rank (0 is invalid). some commands can claim immunity from host ranking which basically means the command is allowed to load/run regardless of the user's host rank. for internal commands, any commands that edit the user's own information like it's name, display name, password, etc including the login command have immunity. the path to the mod executable can be relative or absolute but this command doesn't check if it actually exists. for ranked command enforcement to work properly, the mod to command name relationship must match with what is actually there. \ No newline at end of file diff --git a/docs/intern_commands/add_rdonly_flag.md b/docs/intern_commands/add_rdonly_flag.md index 7a88760..d05491d 100644 --- a/docs/intern_commands/add_rdonly_flag.md +++ b/docs/intern_commands/add_rdonly_flag.md @@ -8,4 +8,4 @@ add a read only flag to a certain sub-channel and privilege level. ### Description ### -this adds a read only flag to a sub-channel for users at a certain privilage level given in -level. valid privilage levels range 1-5 representing owner-level(1), admin-level(2), officer-level(3), regular-level(4) and public-level(5). the presents of a read only flag bassically tells the host that users connected to this sub-channel at this level can only receive data from this sub-channel and cannot cast data to the sub-channel. this is useful if you want to setup a channel that allow certain user(s) to broadcast data while everybody else can only listen for the data. the channel name given in -ch_name must already exists but the sub-channel id given in -sub_id doesn't need to exists (valid range 0-255) but what ever sub-channel name that takes the sub-id specified here gets the read only flag. only the channel owner-level(1) and admin-level(2) can add read only flags to sub-channels. also note that this command will cause all sessions that currently have the sub-channel open to close it. it will be up to the clients to re-open the sub-channel. \ No newline at end of file +this adds a read only flag to a sub-channel for users at a certain privilage level given in -level. valid privilage levels range 1-5 representing owner-level(1), admin-level(2), officer-level(3), regular-level(4) and public-level(5). the presents of a read only flag bassically tells the host that users connected to this sub-channel at this level can only receive data from this sub-channel and cannot cast data to the sub-channel. this is useful if you want to setup a channel that allow certain user(s) to broadcast data while everybody else can only listen for the data. the channel name given in -ch_name must already exists but the sub-channel id given in -sub_id doesn't need to exists (valid range 0-255) but what ever sub-channel name that takes the sub-id specified here gets the read only flag. only the channel owner-level(1) and admin-level(2) can add read only flags to sub-channels. also note that this command will cause all sessions that currently have the sub-channel open to close it. it will be up to the clients to re-open the sub-channel if they still have access. \ No newline at end of file diff --git a/docs/intern_commands/add_sub_ch.md b/docs/intern_commands/add_sub_ch.md index c568afd..e214687 100644 --- a/docs/intern_commands/add_sub_ch.md +++ b/docs/intern_commands/add_sub_ch.md @@ -8,4 +8,4 @@ create a new sub-channel within a channel. ### Description ### -create a new sub-channel given in -sub_name for the channel given in -ch_name. only the channel owner-level(1) and admin-level(2) is allowed to do this. sub-channels are used to determine which clients can send/receive broadcast data. when a client broadcast data to a open sub-channel, the clients that want to receive that data will also need to have the matching sub-channel open. \ No newline at end of file +create a new sub-channel given in -sub_name for the channel given in -ch_name. only the channel owner-level(1) and admin-level(2) are allowed to do this. sub-channels are used to determine which clients can send/receive broadcast data. when a client broadcast data to a open sub-channel, the clients that want to receive that data will also need to have the matching sub-channel open. \ No newline at end of file diff --git a/docs/intern_commands/cast.md b/docs/intern_commands/cast.md index 69e9ef6..39cc7c9 100644 --- a/docs/intern_commands/cast.md +++ b/docs/intern_commands/cast.md @@ -1,6 +1,6 @@ ### Summary ### -broadcast text/data to all sessions listening to any matching sub-channels. +broadcast data to all sessions listening to any matching sub-channels. ### IO ### diff --git a/docs/intern_commands/close_host.md b/docs/intern_commands/close_host.md index 2f6842a..5468651 100644 --- a/docs/intern_commands/close_host.md +++ b/docs/intern_commands/close_host.md @@ -8,4 +8,4 @@ close the host instance. ### Description ### -this ends all sessions and closes the host main instance. be very careful with this command since it will shutdown the mrci host entirely so all clients will not be able to connect until it is started again. \ No newline at end of file +this ends all sessions and closes the host main instance. be very careful with this command since it will shutdown the mrci host application entirely so all clients will not be able to connect until it is started again. \ No newline at end of file diff --git a/docs/intern_commands/cmd_info.md b/docs/intern_commands/cmd_info.md deleted file mode 100644 index 3f868f1..0000000 --- a/docs/intern_commands/cmd_info.md +++ /dev/null @@ -1,11 +0,0 @@ -### Summary ### - -display detailed information about a command. - -### IO ### - -```[-cmd_name (text)] or [-cmd_id (int)]/[text]``` - -### Description ### - -display more detailed information about the command name given in -cmd_name or the command id given in -cmd_id. \ No newline at end of file diff --git a/docs/intern_commands/fs_cd.md b/docs/intern_commands/fs_cd.md index 7737c8f..2bd3c0b 100644 --- a/docs/intern_commands/fs_cd.md +++ b/docs/intern_commands/fs_cd.md @@ -8,4 +8,4 @@ display or change the current directory for the current session. ### Description ### -change the current directory of your current session to the directory specified in -path or display it if -path is not specified. note: the current directory is not shared among any of the peer sessions. \ No newline at end of file +change the current directory of your current session to the directory specified in -path or display it if -path is not specified. note: the current directory is not shared among any of the peer sessions and changes to it does not take effect on currently running commands until fully terminated (not IDLE but fully terminated via KILL_CMD). \ No newline at end of file diff --git a/docs/intern_commands/fs_copy.md b/docs/intern_commands/fs_copy.md index c399bbc..b9ba597 100644 --- a/docs/intern_commands/fs_copy.md +++ b/docs/intern_commands/fs_copy.md @@ -1,6 +1,6 @@ ### Summary ### -copy a file system object (file,directory,symlink) from one location to another. +copy a file or directory in the host file system. ### IO ### diff --git a/docs/intern_commands/fs_delete.md b/docs/intern_commands/fs_delete.md index 16a0201..64bbc77 100644 --- a/docs/intern_commands/fs_delete.md +++ b/docs/intern_commands/fs_delete.md @@ -1,6 +1,6 @@ ### Summary ### -attempt to delete a file system object (file,directory,symlink) in the host. +delete a file or directory in the host file system. ### IO ### diff --git a/docs/intern_commands/fs_download.md b/docs/intern_commands/fs_download.md index ff13a53..bbae3ff 100644 --- a/docs/intern_commands/fs_download.md +++ b/docs/intern_commands/fs_download.md @@ -10,8 +10,8 @@ download a single file from the host. this command sends GEN_FILE data of the file given in -remote_file. depending on the client, you might need to enter the destination file in -client_file. --offset is the position in the file to start reading and it defaults to 0 if not given. --len is the amount of data to read from the file and the host will auto fill it to the file size if not given. the host also auto fill to the file size if it larger than the actual file size. +-offset is the position in the file to start reading. it defaults to 0 if not given. +-len is the amount of data to read from the file. the host will auto fill it to the file size if not given. the host also auto fill to the file size if it larger than the actual file size. -single_step enables GEN_FILE's single step mode if the client/host desires it. --force bypasses any overwrite confirmation question if the client does such a thing. +-force bypasses any overwrite confirmation questions if the client does such a thing. the host does nothing with this. it's entirely up to the client to implement this option. -truncate tells the client if it should truncate the destination file. the host does nothing with this. it's entirely up to the client to support it. \ No newline at end of file diff --git a/docs/intern_commands/fs_info.md b/docs/intern_commands/fs_info.md index bf67314..faf109f 100644 --- a/docs/intern_commands/fs_info.md +++ b/docs/intern_commands/fs_info.md @@ -1,6 +1,6 @@ ### Summary ### -get detailed information about a file system object (file,directory,symlink) in the host. +get detailed information about a file in the host file system. ### IO ### diff --git a/docs/intern_commands/fs_move.md b/docs/intern_commands/fs_move.md index 10fb57b..5e22a16 100644 --- a/docs/intern_commands/fs_move.md +++ b/docs/intern_commands/fs_move.md @@ -1,6 +1,6 @@ ### Summary ### -move/rename a file system object (file,directory,symlink) from one location to another. +move/rename a file or directory in the host file system. ### IO ### diff --git a/docs/intern_commands/fs_upload.md b/docs/intern_commands/fs_upload.md index 9b12e76..52f01b8 100644 --- a/docs/intern_commands/fs_upload.md +++ b/docs/intern_commands/fs_upload.md @@ -10,7 +10,7 @@ upload a single file to the host. attempt to upload the file given in -client_file to the destination file path in the host given in -remote_file. depending on the client, you might not need to enter -len. if supported, the client will auto fill -len with what ever value is needed. --offset is the position in the file to start writing and it defaults to 0 if not given. +-offset is the position in the file to start writing. it defaults to 0 if not given. -single_step enables GEN_FILE's single step mode if the client/host desires it. -force bypasses the overwrite confirmation question if the destination file already exists. -truncate tells the host if it should truncate the destination file. \ No newline at end of file diff --git a/docs/intern_commands/lock_acct.md b/docs/intern_commands/lock_acct.md index f8af8d9..eb60ce2 100644 --- a/docs/intern_commands/lock_acct.md +++ b/docs/intern_commands/lock_acct.md @@ -1,6 +1,6 @@ ### Summary ### -lock user account. +lock a user account. ### IO ### diff --git a/docs/intern_commands/ls_act_log.md b/docs/intern_commands/ls_act_log.md index 142fc5a..3e9d6c7 100644 --- a/docs/intern_commands/ls_act_log.md +++ b/docs/intern_commands/ls_act_log.md @@ -12,8 +12,8 @@ by default, all entries in the table are displayed in 50 entries per page. you c -time_stamp -ip_address +-client_app -session_id --client_version -log_entry you can also pass -delete that will cause the command to delete the entries instead of displaying them. note: passing no search terms with this option will delete all entries in the table. \ No newline at end of file diff --git a/docs/intern_commands/ls_auth_log.md b/docs/intern_commands/ls_auth_log.md index 08b19d4..905fe84 100644 --- a/docs/intern_commands/ls_auth_log.md +++ b/docs/intern_commands/ls_auth_log.md @@ -1,10 +1,10 @@ ### Summary ### -display or manage the host authorization activity log. +display the host authorization activity log. ### IO ### -```[{search_terms} {-delete}]/[text]``` +```[{search_terms}]/[text]``` ### Description ### @@ -18,8 +18,6 @@ by default, all entries in the table are displayed in 50 entries per page. you c -count_to_threshold -accepted -you can also pass -delete that will cause the command to delete the entries instead of displaying them. note: passing no search terms with this option will delete all entries in the table. - the host use entries in this table to enforce maximum failed login thresholds using a combination of the values found in the -count_to_threshold, -user_name, -count_to_threshold and -accepted columns. so for example, if the host counts a maximum of 50 entries for a certain -user_name with -count_to_threshold = 1 and -accepted = 0 and the host maximum amount of failed attempts is set to 50 then the host will then auto lock the user account to protect it. the -accepted column is a 1 or 0 to indicate if the user successfully logged in or not and the -count_to_threshold column is also 1 or 0 to determine if this particular entry should be counted toward the threshold or not. \ No newline at end of file diff --git a/docs/intern_commands/ls_ch_members.md b/docs/intern_commands/ls_ch_members.md index 887e7ee..d586c6d 100644 --- a/docs/intern_commands/ls_ch_members.md +++ b/docs/intern_commands/ls_ch_members.md @@ -4,8 +4,8 @@ list all members in a channel. ### IO ### -```[-ch_name (text) {-find (text)}]/[text]``` +```[-ch_name (text) {-user_name (text)} {-disp_name (text)}]/[text]``` ### Description ### -list all members currently in the channel given in -ch_name. you must be a member of the channel (any level) for this to work. this command will display 50 entries per page but you can pass the optional -find argument with a text search term to find members via the user name. \ No newline at end of file +list all members currently in the channel given in -ch_name. you must be a member of the channel (any level) for this to work. this command will display 50 entries per page but you can pass the optional -user_name argument with a text search term to find members via the user name and/or -disp_name to find members via the display name. \ No newline at end of file diff --git a/docs/intern_commands/ls_cmds.md b/docs/intern_commands/ls_cmds.md deleted file mode 100644 index 21acee7..0000000 --- a/docs/intern_commands/ls_cmds.md +++ /dev/null @@ -1,11 +0,0 @@ -### Summary ### - -list all available commands for your current session. - -### IO ### - -```[{-find (text)}]/[text]``` - -### Description ### - -display all available commands in your current session in a table format. you can pass an optional -find text to narrow down the size of the list or find specific commands via the command name. \ No newline at end of file diff --git a/docs/intern_commands/ls_dbg.md b/docs/intern_commands/ls_dbg.md index ab69b4c..d221807 100644 --- a/docs/intern_commands/ls_dbg.md +++ b/docs/intern_commands/ls_dbg.md @@ -1,6 +1,6 @@ ### Summary ### -display debug messages from the host instance and all session instances. +display all debug log messages. ### IO ### diff --git a/docs/intern_commands/ls_groups.md b/docs/intern_commands/ls_groups.md deleted file mode 100644 index 9f6f85c..0000000 --- a/docs/intern_commands/ls_groups.md +++ /dev/null @@ -1,14 +0,0 @@ -### Summary ### - -list all groups currently registered in the host. - -### IO ### - -```[{search_terms}]/[text]``` - -### Description ### - -by default, all entries in the table are displayed in 50 entries per page. you can pass the column names as -column_name (text) to refine your search to specific entries. this command can handle the following columns: - --group_name --host_rank \ No newline at end of file diff --git a/docs/intern_commands/ls_mods.md b/docs/intern_commands/ls_mods.md index f61e9b4..df57084 100644 --- a/docs/intern_commands/ls_mods.md +++ b/docs/intern_commands/ls_mods.md @@ -1,6 +1,6 @@ ### Summary ### -list all available modules currently installed in the host. +list all available modules currently configured in the host. ### IO ### @@ -8,8 +8,4 @@ list all available modules currently installed in the host. ### Description ### -display a list of all modules currently installed in the host. any mods marked with a '1' on the locked column will prevent new sessions from attempting to load it. the presents of a lock can mean 3 things: - -1. the module is queued for deletion. -2. installation is in process and the module is not yet ready for loading. -3. a session has crashed while attempting to load the module. \ No newline at end of file +display a list of all modules currently configured to run in the host. modules are independent applications so having them listed here doesn't guarantee the commands installed in them will actually be avaiable for running. any module that fails to load correctly should log debug messages. \ No newline at end of file diff --git a/docs/intern_commands/ls_ranked_cmds.md b/docs/intern_commands/ls_ranked_cmds.md index 13542a7..4856c2b 100644 --- a/docs/intern_commands/ls_ranked_cmds.md +++ b/docs/intern_commands/ls_ranked_cmds.md @@ -1,6 +1,6 @@ ### Summary ### -list all command names with assigned host ranks. +list all module commands with assigned host ranks. ### IO ### @@ -10,5 +10,6 @@ list all command names with assigned host ranks. by default, all entries in the table are displayed in 50 entries per page. you can pass the column names as -column_name (text) to refine your search to specific entries. this command can handle the following columns: --command_obj_name +-module_executable +-command_name -host_rank diff --git a/docs/intern_commands/ls_sub_chs.md b/docs/intern_commands/ls_sub_chs.md index 75e0e85..76c903e 100644 --- a/docs/intern_commands/ls_sub_chs.md +++ b/docs/intern_commands/ls_sub_chs.md @@ -1,6 +1,6 @@ ### Summary ### -list all sub-channels within a channel you currently a member of. +list all sub-channels within a channel. ### IO ### diff --git a/docs/intern_commands/ls_users.md b/docs/intern_commands/ls_users.md index 468cec5..096e8ee 100644 --- a/docs/intern_commands/ls_users.md +++ b/docs/intern_commands/ls_users.md @@ -10,7 +10,7 @@ list all users currently registered in the host database. by default, all entries in the table are displayed in 50 entries per page. you can pass the column names as -column_name (text) to refine your search to specific entries. this command can handle the following columns: --DateAdded --UserName --GroupName --UserID +-time_stamp +-user_name +-host_rank +-user_id diff --git a/docs/intern_commands/open_sub_ch.md b/docs/intern_commands/open_sub_ch.md index 5ee2290..0fcae45 100644 --- a/docs/intern_commands/open_sub_ch.md +++ b/docs/intern_commands/open_sub_ch.md @@ -1,6 +1,6 @@ ### Summary ### -open a sub-channel to send/receive broadcasted data to/from other peers. +open a sub-channel to send/receive broadcasted data to/from peers. ### IO ### @@ -8,4 +8,4 @@ open a sub-channel to send/receive broadcasted data to/from other peers. ### Description ### -use this command to open the channel and sub-channel name given in -ch for the main channel name and -sub for the sub-channel name. only peers with matching channel-sub combinations can send/receive data with each other when using the cast command. whether peers send/receive auto/pinged PEER_INFO or PEER_STAT frames depends if the active update flag is set on the channel itself or the host. \ No newline at end of file +use this command to open the channel and sub-channel name given in -ch for the main channel name and -sub for the sub-channel name. only peers with matching channel-sub combinations can send/receive data with each other when using the cast command. whether peers can send/receive PEER_INFO or PEER_STAT frames depends if the active update flag is set on the sub-channel itself or the host global setting. \ No newline at end of file diff --git a/docs/intern_commands/p2p_close.md b/docs/intern_commands/p2p_close.md index 5e95ea9..71188a6 100644 --- a/docs/intern_commands/p2p_close.md +++ b/docs/intern_commands/p2p_close.md @@ -1,6 +1,6 @@ ### Summary ### -close the p2p connection with the client given in this command or decline a p2p request. +close the p2p connection with the peer given in this command or decline a p2p request. ### IO ### diff --git a/docs/intern_commands/p2p_open.md b/docs/intern_commands/p2p_open.md index cf0aaac..d36ab39 100644 --- a/docs/intern_commands/p2p_open.md +++ b/docs/intern_commands/p2p_open.md @@ -1,6 +1,6 @@ ### Summary ### -accept the p2p request you may have received from another client connected to the host. +accept the p2p request you may have received from another peer connected to the host. ### IO ### diff --git a/docs/intern_commands/p2p_request.md b/docs/intern_commands/p2p_request.md index 9c26adb..1d55d7e 100644 --- a/docs/intern_commands/p2p_request.md +++ b/docs/intern_commands/p2p_request.md @@ -1,6 +1,6 @@ ### Summary ### -send out a p2p request to the client session id given in this command. +send out a p2p request to the peer session id given in this command. ### IO ### diff --git a/docs/intern_commands/pause.md b/docs/intern_commands/pause.md deleted file mode 100644 index b707175..0000000 --- a/docs/intern_commands/pause.md +++ /dev/null @@ -1,11 +0,0 @@ -### Summary ### - -pause the current task that the command is running. - -### IO ### - -```[{CMD_ID}]/[text]``` - -### Description ### - -this pauses the command given by a CMD_ID frame sent into this command. an error is returned if the requested command is not currently in the looping state. if the frame is empty, this command pauses all commands currently in the looping state. \ No newline at end of file diff --git a/docs/intern_commands/preview_email.md b/docs/intern_commands/preview_email.md index b6b0c91..8a7115f 100644 --- a/docs/intern_commands/preview_email.md +++ b/docs/intern_commands/preview_email.md @@ -8,4 +8,4 @@ preview the confirmation or password reset emails with dummy values. ### Description ### -preview the reset password email with the -reset_email argument or preview the email confirmation with -confirm_email. this prints the subject first and then the message body with the keywords substituted the dummy values/text. \ No newline at end of file +preview the reset password email with the -reset_email argument or preview the email confirmation with -confirm_email. this prints the subject first and then the message body with the keywords substituted the dummy values/text. this command will output the preview as configured so if the messages do contain html, it will output the html as is. it will be up to the client to parse the html or not. \ No newline at end of file diff --git a/docs/intern_commands/recover_acct.md b/docs/intern_commands/recover_acct.md index 6fbabe7..9c65a5e 100644 --- a/docs/intern_commands/recover_acct.md +++ b/docs/intern_commands/recover_acct.md @@ -1,6 +1,6 @@ ### Summary ### -reset a user account password. +login to a user account using a temporary password. ### IO ### @@ -8,4 +8,4 @@ reset a user account password. ### Description ### -reset the password for the user account given in -email or -user. during execution, this command will ask for the temporary password that was sent to that email address when request_pw_reset was called. \ No newline at end of file +reset the password for the user account given in -email or -user. during execution, this command will ask for the temporary password that was sent to that email address when the password reset was requested. if successful, you will then be required to enter a new password. \ No newline at end of file diff --git a/docs/intern_commands/remove_ch_member.md b/docs/intern_commands/remove_ch_member.md index 38041b7..8e9f347 100644 --- a/docs/intern_commands/remove_ch_member.md +++ b/docs/intern_commands/remove_ch_member.md @@ -8,4 +8,4 @@ remove a user as a member of a channel you currently a member of or cancel an in ### Description ### -remove a user given in -user from the channel member list for the channel given in -ch_name. normally, only the channel owner-level(1), admin-level(2) of officer(3) can do this but it is unrestricted if removing your self as a member except the channel owner. another restriction is if trying to remove members with higher or equal member privileges than your self so officers can't remove admins, other officers or the owner and admins can't remove other admins or the owner. the owner can't be removed from the channel at anytime. this command can also be used to cancel an invite for a user. \ No newline at end of file +remove a user given in -user from the channel member list for the channel given in -ch_name. normally, only the channel owner-level(1), admin-level(2) of officer(3) can do this but it is unrestricted if removing your self as a member except the channel owner. another restriction is if trying to remove members with higher or equal member privileges than your self so officers can't remove admins, other officers or the owner and admins can't remove other admins or the owner. the owner can't be removed from the channel in any case. this command can also be used to cancel an invite for a user. \ No newline at end of file diff --git a/docs/intern_commands/request_pw_reset.md b/docs/intern_commands/request_pw_reset.md index 3fd89d7..cf9cdcc 100644 --- a/docs/intern_commands/request_pw_reset.md +++ b/docs/intern_commands/request_pw_reset.md @@ -8,4 +8,4 @@ request a password reset for a user account. ### Description ### -this will send a temporary password for the user account associated with the email adress given in -email or the username given in -user. this password can then be used with recover_account to reset the password. \ No newline at end of file +this will send a temporary password for the user account associated with the email adress given in -email or the user name given in -user. this password can then be used to login via the recovery command to reset the password. \ No newline at end of file diff --git a/docs/intern_commands/restart_host.md b/docs/intern_commands/restart_host.md index d2cbe27..c915eb3 100644 --- a/docs/intern_commands/restart_host.md +++ b/docs/intern_commands/restart_host.md @@ -8,4 +8,4 @@ re-start the host instance. ### Description ### -this will restart the host instance, along with a database reload. be very careful with this command. it ends all current sessions and since it also reloads the database, host behaviour might also change depending on what is in the new database if the path was changed. \ No newline at end of file +this will restart the host instance. it will stop listening for new clients, close all current sessions, reload the database and then start listening for clients again. the host behaviour might also change depending on what is new in the database or if the path has changed. \ No newline at end of file diff --git a/docs/intern_commands/resume.md b/docs/intern_commands/resume.md deleted file mode 100644 index a9c81b1..0000000 --- a/docs/intern_commands/resume.md +++ /dev/null @@ -1,11 +0,0 @@ -### Summary ### - -resumes the current task that the command is running. - -### IO ### - -```[{CMD_ID}]/[text]``` - -### Description ### - -this resumes the looping task for the command given by a CMD_ID frame sent into this command. a failure is returned if the command is not currently in a paused state. if the frame is empty, all currently paused commands are resumed. \ No newline at end of file diff --git a/docs/intern_commands/rm_acct.md b/docs/intern_commands/rm_acct.md index 89b0286..a979161 100644 --- a/docs/intern_commands/rm_acct.md +++ b/docs/intern_commands/rm_acct.md @@ -8,4 +8,4 @@ delete a user account from the host database. ### Description ### -delete the user account given in -name. this will also automatically kill all sessions currently using this user account. be very careful with this command since it's changes cannot be undone. you can use the -force option to bypass the confirmation question. \ No newline at end of file +delete the user account given in -name. this will also automatically kill all sessions currently using this user account. be very careful with this command since it's changes cannot be undone. you can use the -force option to bypass the confirmation question. this command will fail if the user account is the owner of a channel. you must assign a new owner for that channel or delete the channel to unlock account deletion. \ No newline at end of file diff --git a/docs/intern_commands/rm_mod.md b/docs/intern_commands/rm_mod.md index d5cfe97..a2c9278 100644 --- a/docs/intern_commands/rm_mod.md +++ b/docs/intern_commands/rm_mod.md @@ -1,11 +1,11 @@ ### Summary ### -uninstall a module from the host. +remove a module from the host. ### IO ### -```[-name (text)]/[text]``` +```[-mod_path (text)]/[text]``` ### Description ### -this tells all sessions in the host to unload the module given in -name and all associated commands. it will also permanently delete all files associated with the module from the host. file deletion is not done instantly; instead, a specific amount of time is allotted to allow all sessions to unload the mod/clean up before it's files are deleted. also note that all associated commands are forced to terminate when the module is unloaded. \ No newline at end of file +this tells all sessions in the host to unload the module given in -mod_path and all associated commands. all associated commands are forced to terminate when the module is unloaded. this command will not uninstall the module application, all it does is remove the path to the module application from the host database so the module's commands will no longer be executable by host users. \ No newline at end of file diff --git a/docs/intern_commands/rm_ranked_cmd.md b/docs/intern_commands/rm_ranked_cmd.md index bf94213..631112f 100644 --- a/docs/intern_commands/rm_ranked_cmd.md +++ b/docs/intern_commands/rm_ranked_cmd.md @@ -1,11 +1,11 @@ ### Summary ### -remove a rank from a command object name. +remove a rank from a module command name. ### IO ### -```[-command (text)]/[text]``` +```[-command (text) -mod (text)]/[text]``` ### Description ### -this will remove the command rank from the command object given in -command. any command object without an assigned perm id is assumed a rank of 1 but some exceptions apply for commands that need to be allowed for all users regardless of rank. \ No newline at end of file +this will remove the command rank from the command name given in -command for the module given in -mod. any command name without an assigned rank is assumed a rank of 1 except for commands that claim immunity. \ No newline at end of file diff --git a/docs/intern_commands/rm_rdonly_flag.md b/docs/intern_commands/rm_rdonly_flag.md index 0984db1..ae24a90 100644 --- a/docs/intern_commands/rm_rdonly_flag.md +++ b/docs/intern_commands/rm_rdonly_flag.md @@ -8,4 +8,4 @@ remove a read only flag from a certain sub-channel privilege level combination. ### Description ### -remove a read only flag the sub-channel given in -sub_id for connected users at the level given in -level. the channel given in -ch_name must already exists. with, the read only flag gone users connected to the sub-channel at the specified level would be able to cast data to the sub-channel once again. also note that this command will cause all sessions that currently have the sub-channel open to close the sub-channel. it will be up to the clients to re-open the sub-channel. \ No newline at end of file +remove a read only flag the sub-channel given in -sub_id for connected users at the level given in -level. the channel given in -ch_name must already exists. with, the read only flag gone; users connected to the sub-channel at the specified level would be able to cast data to the sub-channel once again. also note that this command will cause all sessions that currently have the sub-channel open to close the sub-channel. it will be up to the clients to re-open the sub-channel if the user still has access. \ No newline at end of file diff --git a/docs/intern_commands/rm_sub_ch.md b/docs/intern_commands/rm_sub_ch.md index 6cec3d2..9315860 100644 --- a/docs/intern_commands/rm_sub_ch.md +++ b/docs/intern_commands/rm_sub_ch.md @@ -8,4 +8,4 @@ remove a sub-channel within a channel. ### Description ### -remove a sub-channel given in -sub_name for the channel given in -ch_name. only the channel owner-level(1) and admin-level(2) is allowed to do this. only the channel owner-level(1) and admin-level(2) is allowed to do this. \ No newline at end of file +remove a sub-channel given in -sub_name for the channel given in -ch_name. only the channel owner-level(1) and admin-level(2) is allowed to do this. all sessions that currently have this sub-channel open will be forced to close it. \ No newline at end of file diff --git a/docs/intern_commands/set_active_flag.md b/docs/intern_commands/set_active_flag.md index 911190f..a20f3c9 100644 --- a/docs/intern_commands/set_active_flag.md +++ b/docs/intern_commands/set_active_flag.md @@ -8,4 +8,4 @@ set or unset the active update flag of a sub-channel. ### Description ### -this sets the active update flag on the channel given in -ch_name true or false based on the value given in -state. the active update flag allow the sessions to send PEER_INFO or PEER_STAT frames to the clients when a peer connected to the channel changes information like it's user name, group name, display name, disconnect etc...this flag is ignored if the host have the global active update flag set. if that's the case, all channels will active update. the channel owner-level(1) and admin-level(2) is allowed to do this. \ No newline at end of file +this sets the active update flag on the channel given in -ch_name true or false based on the value given in -state. the active update flag allow the sessions to send PEER_INFO or PEER_STAT frames to the clients when a peer connected to the sub-channel changes information like it's user name, sub-channels, display name, disconnect etc...this flag is ignored if the host have the global active update flag set. if that's the case, all channels will active update. the channel owner-level(1) and admin-level(2) is allowed update this flag. \ No newline at end of file diff --git a/docs/intern_commands/set_disp_name.md b/docs/intern_commands/set_disp_name.md index d5f5d3c..c88133b 100644 --- a/docs/intern_commands/set_disp_name.md +++ b/docs/intern_commands/set_disp_name.md @@ -4,8 +4,8 @@ change your account display name. ### IO ### -```[-name (text)]/[text]``` +```[-new_name (text)]/[text]``` ### Description ### -this changes the display name on your own account to the name given in -name. the display name is used by some clients to present your account to other clients instead of showing your real user name. it is not restricted by uniqueness and can be any text that is less than or equal to 32 chars. \ No newline at end of file +this changes the display name on your own account to the name given in -new_name. the display name is used by some clients to present your account to other clients instead of showing your real user name. it is not restricted by uniqueness and can be any text that is less than or equal to 32 chars long. it can even contain spaces if desired but a display name of just spaces will be considered empty because this command will trim the text. the only chars that are considered invalid is new lines or line breaks of any kind. \ No newline at end of file diff --git a/docs/intern_commands/set_email.md b/docs/intern_commands/set_email.md index e0305ff..3e6aba2 100644 --- a/docs/intern_commands/set_email.md +++ b/docs/intern_commands/set_email.md @@ -1,6 +1,6 @@ ### Summary ### -change the user account email address. +set the user account email address. ### IO ### diff --git a/docs/intern_commands/set_email_template.md b/docs/intern_commands/set_email_template.md index 0d1528c..855a9d0 100644 --- a/docs/intern_commands/set_email_template.md +++ b/docs/intern_commands/set_email_template.md @@ -8,7 +8,9 @@ set the email template used by the host to send emails for user account resets a ### Description ### -this updates the email template used by the host to send emails to the users that request password resets and or email confirmations. the presents of -reset_template updates the password reset email or the presents of -confirm_template updates the confirmation email. -subject is exactly as the name implies, it tells the host what subject to use when sending the email. +this updates the email template used by the host to send emails to the users that request password resets and or email confirmations. the presents of -reset_template updates the password reset email or the presents of -confirm_template updates the confirmation email. + +-subject is exactly as the name implies, it tells the host what subject to use when sending the email. -body sets the email message body directly from the command line or -client_file loads the body from a text file. if uploading a text file, use the -len parameter to enter the file size in bytes if your client does not auto fill that. @@ -19,4 +21,4 @@ the message body must contain the following keywords to be acceptable: %temp_pw% (if -reset_template) %confirmation_code% (if -confirm_template) -the host will substitute these keywords with actual values/text when contructing the email. note: if sending a text file, the host assumes it is encoded in UTF-16LE. pass an empty -subject and/or a empty -body to reset the values to host defaults. if the body or subject contains single quotes ' ' escape them with a \. \ No newline at end of file +the host will substitute these keywords with actual values/text when contructing the email. note: if sending a text file, the host assumes it is encoded in UTF-16LE. pass an empty -subject and/or a empty -body to reset the values to host defaults. \ No newline at end of file diff --git a/docs/intern_commands/set_group.md b/docs/intern_commands/set_group.md deleted file mode 100644 index de2080d..0000000 --- a/docs/intern_commands/set_group.md +++ /dev/null @@ -1,11 +0,0 @@ -### Summary ### - -change a user account's group. - -### IO ### - -```[-user (text) -group (text)]/[text]``` - -### Description ### - -this changes the group of the user account given in -user to the target group given in -group. \ No newline at end of file diff --git a/docs/intern_commands/set_group_rank.md b/docs/intern_commands/set_group_rank.md deleted file mode 100644 index f4f074b..0000000 --- a/docs/intern_commands/set_group_rank.md +++ /dev/null @@ -1,11 +0,0 @@ -### Summary ### - -set the host rank of a group. - -### IO ### - -```[-name (text) -rank (int)]/[text]``` - -### Description ### - -set the host rank for the group name given in -name to the rank given in -rank. the host rank is used throughout this application to determine how much access to the host commands each user attached to the group has. the lower the numeric value of the host rank, the higher the level of access to the host the group has (1 being the highest level of access). you cannot change the group of a group that has a higher rank than your own group. \ No newline at end of file diff --git a/docs/intern_commands/set_member_level.md b/docs/intern_commands/set_member_level.md index 5e41b40..caa64f5 100644 --- a/docs/intern_commands/set_member_level.md +++ b/docs/intern_commands/set_member_level.md @@ -1,6 +1,6 @@ ### Summary ### -set the user privilege levels of a channel member. (lower the value, the higher the privilege) +set the user privilege levels of a channel member. ### IO ### diff --git a/docs/intern_commands/set_pw.md b/docs/intern_commands/set_pw.md index a801ae2..87679d9 100644 --- a/docs/intern_commands/set_pw.md +++ b/docs/intern_commands/set_pw.md @@ -1,6 +1,6 @@ ### Summary ### -change your account password. +update your account password. ### IO ### @@ -8,4 +8,4 @@ change your account password. ### Description ### -this changes the password on your own account. this command will instantly hook the input and await a new password to be entered. \ No newline at end of file +this changes the password on your own account. during execution, this command will ask for a new password to be entered. \ No newline at end of file diff --git a/docs/intern_commands/set_sub_ch_level.md b/docs/intern_commands/set_sub_ch_level.md index 0314002..262e49b 100644 --- a/docs/intern_commands/set_sub_ch_level.md +++ b/docs/intern_commands/set_sub_ch_level.md @@ -8,4 +8,4 @@ set the lowest privilege level that members need to be in order to open a certai ### Description ### -this command makes it possible so set minimum privilege levels to open the sub-channel given in -sub_name to the level given in -level for the channel given in -ch_name. a valid level is an integer between 1-5 representing owner-level(1), admin-level(2), officer-level(3), regular-level(4) and public-level(5). for example, you could set a sub-channel's minimum level to 4 to make it so only channel regular members and above can open/close the sub-channel or you can set it to 5 to make it so anybody can open/close the sub-channel, affectively making it a public sub-channel. only the channel owner or admin(s) are allowed to do this. also note that this command will cause all sessions that currently have the sub-channel open to close the sub-channel. it will be up to the client to re-open the sub-channel if it's user account still have access to it. \ No newline at end of file +this command makes it possible so set minimum privilege levels to open the sub-channel given in -sub_name to the level given in -level for the channel given in -ch_name. a valid level is an integer between 1-5 representing owner-level(1), admin-level(2), officer-level(3), regular-level(4) and public-level(5). for example, you could set a sub-channel's minimum level to 4 to make it so only channel regular members and above can open/close the sub-channel or you can set it to 5 to make it so anybody can open/close the sub-channel, affectively making it a public sub-channel. only the channel owner or admin(s) are allowed to update this. also note that this command will cause all sessions that currently have the sub-channel open to close the sub-channel. it will be up to the client to re-open the sub-channel if it's user account still have access to it. \ No newline at end of file diff --git a/docs/intern_commands/set_user_rank.md b/docs/intern_commands/set_user_rank.md new file mode 100644 index 0000000..c281193 --- /dev/null +++ b/docs/intern_commands/set_user_rank.md @@ -0,0 +1,11 @@ +### Summary ### + +change a user account's host rank. + +### IO ### + +```[-user (text) -rank (int)]/[text]``` + +### Description ### + +this changes the host rank of the target user account name given in -user to the rank given in -rank. the host rank itself is an integer value that determine what commands each user can or cannot run depending on how the ranked commands are configured. it is also used to determine what direct changes a user can do to another user's account. the lower the numerical value the rank is the higher level of privilege the user have on the host (zero is invalid so 1 is the highest level of privilege any user can have). \ No newline at end of file diff --git a/docs/intern_commands/term.md b/docs/intern_commands/term.md deleted file mode 100644 index 9eba129..0000000 --- a/docs/intern_commands/term.md +++ /dev/null @@ -1,11 +0,0 @@ -### Summary ### - -terminate the current task that the command is running. - -### IO ### - -```[{CMD_ID}]/[text]``` - -### Description ### - -this terminates the command given by a CMD_ID frame sent into this command. if the frame is empty, this command terminates all commands. this command doesn't care if the target command object itself is in looping and/or more input states. \ No newline at end of file diff --git a/docs/intern_commands/to_peer.md b/docs/intern_commands/to_peer.md index f0ecbad..5198533 100644 --- a/docs/intern_commands/to_peer.md +++ b/docs/intern_commands/to_peer.md @@ -1,6 +1,6 @@ ### Summary ### -send/receive any data directly with a client connected to the host that has accepted your p2p request or the peer's p2p request. +send/receive any data directly with a client connected to the host. ### IO ### diff --git a/docs/intern_commands/trans_group.md b/docs/intern_commands/trans_group.md deleted file mode 100644 index 406b266..0000000 --- a/docs/intern_commands/trans_group.md +++ /dev/null @@ -1,11 +0,0 @@ -### Summary ### - -transfer all user accounts from one group to another. - -### IO ### - -```[-src (text) -dst (text)]/[text]``` - -### Description ### - -transfer all user accounts currently attached to the group given in -src to the group given in -dst. you must have a higher rank than both groups for this to work. \ No newline at end of file diff --git a/src/async_funcs.cpp b/src/async_funcs.cpp new file mode 100644 index 0000000..17b2607 --- /dev/null +++ b/src/async_funcs.cpp @@ -0,0 +1,386 @@ +#include "session.h" + +// This file is part of MRCI. + +// MRCI is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// MRCI is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with MRCI under the LICENSE.md file. If not, see +// . + +void Session::acctDeleted(const QByteArray &data) +{ + if (flags & LOGGED_IN) + { + // format: [32bytes(user_id)] + + if (memcmp(userId, data.data(), BLKSIZE_USER_ID) == 0) + { + logout("", true); + asyncToClient(ASYNC_SYS_MSG, toTEXT("\nsystem: your session was forced to logout because your account was deleted.\n"), TEXT); + asyncToClient(ASYNC_USER_DELETED, data, TEXT); + } + } +} + +void Session::acctEdited(const QByteArray &data) +{ + if (flags & LOGGED_IN) + { + // format: [32bytes(user_id)] + + if (memcmp(userId, data.data(), BLKSIZE_USER_ID) == 0) + { + sendLocalInfo(); + } + } +} + +void Session::acctRenamed(const QByteArray &data) +{ + if (flags & LOGGED_IN) + { + // format: [32bytes(user_id)][48bytes(new_user_name)] + + if (memcmp(userId, data.data(), BLKSIZE_USER_ID) == 0) + { + memcpy(userName, data.data() + BLKSIZE_USER_ID, BLKSIZE_USER_NAME); + castPeerInfo(PEER_INFO); + sendLocalInfo(); + } + } +} + +void Session::acctDispChanged(const QByteArray &data) +{ + if (flags & LOGGED_IN) + { + // format: [32bytes(user_id)][64bytes(new_disp_name)] + + if (memcmp(userId, data.data(), BLKSIZE_USER_ID) == 0) + { + memcpy(displayName, data.data() + BLKSIZE_USER_ID, BLKSIZE_DISP_NAME); + castPeerInfo(PEER_INFO); + sendLocalInfo(); + } + } +} + +void Session::castCatch(const QByteArray &data) +{ + // format: [54bytes(chIds)][1byte(typeId)][rest-of-bytes(payload)] + + if (matchChs(openSubChs, data.data())) + { + int payloadOffs = (MAX_OPEN_SUB_CHANNELS * BLKSIZE_SUB_CHANNEL) + 1; + quint8 typeId = static_cast(data[payloadOffs - 1]); + quint32 len = static_cast(data.size() - payloadOffs); + + const char *payload = data.data() + payloadOffs; + + asyncToClient(ASYNC_CAST, rdFromBlock(payload, len), typeId); + } +} + +void Session::directDataFromPeer(const QByteArray &data) +{ + // format: [28bytes(sessionId)][1byte(typeId)][rest-of-bytes(payload)] + + if (memcmp(sessionId, data.data(), BLKSIZE_SESSION_ID) == 0) + { + int payloadOffs = BLKSIZE_SESSION_ID + 1; + quint8 typeId = static_cast(data[payloadOffs - 1]); + quint32 len = static_cast(data.size() - payloadOffs); + + const char *payload = data.data() + payloadOffs; + + asyncToClient(ASYNC_TO_PEER, rdFromBlock(payload, len), typeId); + } +} + +void Session::p2p(const QByteArray &data) +{ + // format: [28bytes(dst_sessionId)][28bytes(src_sessionId)][1byte(typeId)][rest-of-bytes(payload)] + + if (memcmp(sessionId, data.data(), BLKSIZE_SESSION_ID) == 0) + { + int payloadOffs = (BLKSIZE_SESSION_ID * 2) + 1; + + const char *src = data.data() + BLKSIZE_SESSION_ID; + const char *payload = data.data() + payloadOffs; + + quint32 len = static_cast(data.size() - payloadOffs); + quint8 typeId = static_cast(data[payloadOffs - 1]); + + if (typeId == P2P_REQUEST) + { + if (posOfBlock(src, p2pAccepted, MAX_P2P_LINKS, BLKSIZE_SESSION_ID) == -1) + { + if (addBlockToBlockset(src, p2pPending, MAX_P2P_LINKS, BLKSIZE_SESSION_ID)) + { + asyncToClient(ASYNC_P2P, rdFromBlock(payload, len), P2P_REQUEST); + } + } + } + else if (typeId == P2P_OPEN) + { + if (rmBlockFromBlockset(src, p2pPending, MAX_P2P_LINKS, BLKSIZE_SESSION_ID)) + { + if (addBlockToBlockset(src, p2pAccepted, MAX_P2P_LINKS, BLKSIZE_SESSION_ID)) + { + asyncToClient(ASYNC_P2P, rdFromBlock(payload, len), P2P_OPEN); + } + } + } + else if (typeId == P2P_CLOSE) + { + if (rmBlockFromBlockset(src, p2pPending, MAX_P2P_LINKS, BLKSIZE_SESSION_ID)) + { + asyncToClient(ASYNC_P2P, rdFromBlock(payload, len), P2P_CLOSE); + } + else if (rmBlockFromBlockset(src, p2pAccepted, MAX_P2P_LINKS, BLKSIZE_SESSION_ID)) + { + asyncToClient(ASYNC_P2P, rdFromBlock(payload, len), P2P_CLOSE); + } + } + else if (posOfBlock(src, p2pAccepted, MAX_P2P_LINKS, BLKSIZE_SESSION_ID) != -1) + { + asyncToClient(ASYNC_P2P, rdFromBlock(src, BLKSIZE_SESSION_ID) + rdFromBlock(payload, len), typeId); + } + } +} + +void Session::closeP2P(const QByteArray &data) +{ + // format: [28bytes(src_sessionId)] + + if (rmBlockFromBlockset(data.data(), p2pAccepted, MAX_P2P_LINKS, BLKSIZE_SESSION_ID) || + rmBlockFromBlockset(data.data(), p2pPending, MAX_P2P_LINKS, BLKSIZE_SESSION_ID)) + { + asyncToClient(ASYNC_P2P, data, P2P_CLOSE); + } +} + +void Session::limitedCastCatch(const QByteArray &data) +{ + // format: [54bytes(chIds)][1byte(typeId)][rest-of-bytes(payload)] + + if (rd8BitFromBlock(activeUpdate) && matchChs(openSubChs, data.data())) + { + int payloadOffs = (MAX_OPEN_SUB_CHANNELS * BLKSIZE_SUB_CHANNEL) + 1; + quint32 len = static_cast(data.size() - payloadOffs); + quint8 typeId = static_cast(data[payloadOffs - 1]); + + const char *payload = data.data() + payloadOffs; + + if (typeId == PING_PEERS) + { + // PING_PEERS is formatted exactly like PEER_INFO. it only tells this + // async command to also send PEER_INFO of this session to the session + // that requested the ping using ASYNC_TO_PEER. + + QByteArray peerId = rdFromBlock(payload, BLKSIZE_SESSION_ID); + QByteArray typeId = wrInt(PEER_INFO, 8); + QByteArray info = createPeerInfoFrame(); + + emit asyncToPeers(ASYNC_TO_PEER, peerId + typeId + info); + + asyncToClient(ASYNC_LIMITED_CAST, rdFromBlock(payload, len), PEER_INFO); + } + else + { + asyncToClient(ASYNC_LIMITED_CAST, rdFromBlock(payload, len), typeId); + } + } +} + +void Session::updateRankViaUser(const QByteArray &data) +{ + if (flags & LOGGED_IN) + { + // format: [32bytes(userId)][4bytes(newRank)] + + if (memcmp(data.data(), userId, BLKSIZE_USER_ID) == 0) + { + wr32BitToBlock(rd32BitFromBlock(data.data() + BLKSIZE_USER_ID), hostRank); + sendLocalInfo(); + loadCmds(); + } + } +} + +void Session::userAddedToChannel(quint16 cmdId, const QByteArray &data) +{ + // format: [8bytes(chId)][32bytes(userId)] + + if (memcmp(data.data() + BLKSIZE_CHANNEL_ID, userId, BLKSIZE_USER_ID) == 0) + { + if ((cmdId == ASYNC_NEW_CH_MEMBER) || (cmdId == ASYNC_INVITE_ACCEPTED)) + { + addBlockToBlockset(data.data(), chList, MAX_CHANNELS_PER_USER, BLKSIZE_CHANNEL_ID); + } + + asyncToClient(cmdId, data, CH_MEMBER_INFO); + } + else if (posOfBlock(data.data(), chList, MAX_CHANNELS_PER_USER, BLKSIZE_CHANNEL_ID) != -1) + { + asyncToClient(cmdId, data, CH_MEMBER_INFO); + } +} + +void Session::userRemovedFromChannel(const QByteArray &data) +{ + // format: [8bytes(chId)][32bytes(user_id)] + + if (memcmp(data.data() + BLKSIZE_CHANNEL_ID, userId, BLKSIZE_USER_ID) == 0) + { + closeByChId(rdFromBlock(data.data(), BLKSIZE_CHANNEL_ID), true); + rmBlockFromBlockset(data.data(), chList, MAX_CHANNELS_PER_USER, BLKSIZE_CHANNEL_ID); + asyncToClient(ASYNC_RM_CH_MEMBER, data, BYTES); + } + else if (posOfBlock(data.data(), chList, MAX_CHANNELS_PER_USER, BLKSIZE_CHANNEL_ID) != -1) + { + asyncToClient(ASYNC_RM_CH_MEMBER, data, BYTES); + } +} + +void Session::channelDeleted(const QByteArray &data) +{ + // format: [8bytes(chId)] + + if (rmBlockFromBlockset(data.data(), chList, MAX_CHANNELS_PER_USER, BLKSIZE_CHANNEL_ID)) + { + closeByChId(data, false); + asyncToClient(ASYNC_DEL_CH, data, CH_ID); + } + else if (rmLikeBlkFromBlkset(data, openSubChs, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL)) + { + rmLikeBlkFromBlkset(data, openWritableSubChs, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL); + asyncToClient(ASYNC_DEL_CH, data, CH_ID); + } +} + +void Session::channelMemberLevelUpdated(const QByteArray &data) +{ + // format: [8bytes(chId)][32bytes(user_id)] + + if (memcmp(data.data() + BLKSIZE_CHANNEL_ID, userId, BLKSIZE_USER_ID) == 0) + { + closeByChId(rdFromBlock(data.data(), BLKSIZE_CHANNEL_ID), true); + asyncToClient(ASYNC_MEM_LEVEL_CHANGED, data, BYTES); + } + else if (posOfBlock(data.data(), chList, MAX_CHANNELS_PER_USER, BLKSIZE_CHANNEL_ID) != -1) + { + asyncToClient(ASYNC_MEM_LEVEL_CHANGED, data, BYTES); + } +} + +void Session::channelRenamed(const QByteArray &data) +{ + // format: [8bytes(chId)] + + if (posOfBlock(data.data(), chList, MAX_CHANNELS_PER_USER, BLKSIZE_CHANNEL_ID) != -1) + { + asyncToClient(ASYNC_RENAME_CH, data, BYTES); + } +} + +void Session::channelActiveFlagUpdated(const QByteArray &data) +{ + // format: [9bytes(72bit_sub_id)] + + if (posOfBlock(data.data(), openSubChs, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL) != -1) + { + containsActiveCh(openSubChs, activeUpdate); + } +} + +void Session::subChannelAdded(quint16 cmdId, const QByteArray &data) +{ + // format: [8bytes(64bit_ch_id)][1byte(8bit_sub_ch_id)] + + if (posOfBlock(data.data(), chList, MAX_CHANNELS_PER_USER, BLKSIZE_CHANNEL_ID) != -1) + { + asyncToClient(cmdId, data, BYTES); + } +} + +void Session::subChannelUpdated(quint16 cmdId, const QByteArray &data) +{ + // format: [9bytes(72bit_sub_id)] + + if (rmBlockFromBlockset(data.data(), openSubChs, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL)) + { + rmBlockFromBlockset(data.data(), openWritableSubChs, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL); + asyncToClient(cmdId, data, BYTES); + } + else if (posOfBlock(data.data(), chList, MAX_CHANNELS_PER_USER, BLKSIZE_CHANNEL_ID) != -1) + { + asyncToClient(cmdId, data, BYTES); + } +} + +void Session::addModule(const QByteArray &data) +{ + QString modApp = fromTEXT(data); + + if (!modCmdNames.contains(modApp)) + { + startModProc(modApp); + } +} + +void Session::rmModule(const QByteArray &data) +{ + QString modApp = fromTEXT(data); + + if (modCmdNames.contains(modApp) && (modApp != QCoreApplication::applicationFilePath())) + { + for (auto&& cmdName : modCmdNames[modApp]) + { + quint16 cmdId16 = cmdRealNames.key(cmdName); + + emit killCmd16(cmdId16); + + cmdRealNames.remove(cmdId16); + cmdUniqueNames.remove(cmdId16); + cmdAppById.remove(cmdId16); + cmdIds.removeOne(cmdId16); + } + + modCmdNames.remove(modApp); + } +} + +void Session::closeSubChannel(const QByteArray &data) +{ + QByteArray oldSubChs = QByteArray(openSubChs, MAX_OPEN_SUB_CHANNELS * BLKSIZE_SUB_CHANNEL); + + if (rmBlockFromBlockset(data.data(), openSubChs, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL)) + { + rmBlockFromBlockset(data.data(), openWritableSubChs, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL); + + if (rd8BitFromBlock(activeUpdate)) + { + castPeerStat(oldSubChs, false); + containsActiveCh(openSubChs, activeUpdate); + } + } +} + +void Session::openSubChannel(const QByteArray &data) +{ + if (addBlockToBlockset(data.data(), openSubChs, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL)) + { + containsActiveCh(openSubChs, activeUpdate); + rd8BitFromBlock(activeUpdate); + } +} diff --git a/src/cmd_executor.cpp b/src/cmd_executor.cpp deleted file mode 100644 index 9028ba0..0000000 --- a/src/cmd_executor.cpp +++ /dev/null @@ -1,821 +0,0 @@ -#include "cmd_executor.h" - -// This file is part of MRCI. - -// MRCI is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// MRCI is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with MRCI under the LICENSE.md file. If not, see -// . - -CmdExecutor::CmdExecutor(RWSharedObjs *rwShare, SharedObjs *rdOnlyShare, QSharedMemory *debugInfo, QObject *parent) : QObject(parent) -{ - rwShare->commands = &commands; - rwShare->activeLoopCmds = &activeLoopCmds; - rwShare->pausedCmds = &pausedCmds; - rwShare->moreInputCmds = &moreInputCmds; - rwShare->cmdNames = &cmdNames; - - rdOnlyShare->activeLoopCmds = &activeLoopCmds; - rdOnlyShare->pausedCmds = &pausedCmds; - rdOnlyShare->moreInputCmds = &moreInputCmds; - rdOnlyShare->cmdNames = &cmdNames; - - loopIndex = 0; - exeDebugInfo = debugInfo; - rdSharedObjs = rdOnlyShare; - rwSharedObjs = rwShare; - internalCmds = nullptr; - - connect(this, &CmdExecutor::loop, this, &CmdExecutor::exeCmd); -} - -void CmdExecutor::buildCmdLoaders() -{ - internalCmds = new InternalCommandLoader(rwSharedObjs, this); - - cmdLoaders.insert(INTERN_MOD_NAME, internalCmds); - - Query db(this); - - db.setType(Query::PULL, TABLE_MODULES); - db.addColumn(COLUMN_MOD_NAME); - db.addCondition(COLUMN_LOCKED, false); - db.exec(); - - for (int i = 0; i < db.rows(); ++i) - { - loadModFile(db.getData(COLUMN_MOD_NAME, i).toString()); - } -} - -void CmdExecutor::wrCrashDebugInfo(const QString &msg) -{ - if (exeDebugInfo->isAttached()) - { - exeDebugInfo->lock(); - - QByteArray data = toTEXT(msg).leftJustified(EXE_DEBUG_INFO_SIZE, static_cast(0), true); - - memcpy(exeDebugInfo->data(), data.data(), EXE_DEBUG_INFO_SIZE); - - exeDebugInfo->unlock(); - } -} - -void CmdExecutor::connectExternCmd(ExternCommand *cmd, quint16 cmdId, const QString &cmdName) -{ - wrCrashDebugInfo(" exe func: connectExternCmd()\n cmd id: " + QString::number(cmdId) + "\n cmd name: " + cmdName); - - auto *cmdOutput = new CommandOutput(cmd); - - connect(cmdOutput, &CommandOutput::dataOut, this, &CmdExecutor::externDataToIPC); - - connectCommon(cmd, cmdOutput, cmdId, cmdName); -} - -void CmdExecutor::connectInternCmd(InternCommand *cmd, quint16 cmdId, const QString &cmdName) -{ - wrCrashDebugInfo(" exe func: connectInternCmd()\n cmd id: " + QString::number(cmdId) + "\n cmd name: " + cmdName); - - auto *cmdOutput = new CommandOutput(cmd); - - connect(cmdOutput, &CommandOutput::dataOut, this, &CmdExecutor::internDataToIPC); - - connect(cmd, &InternCommand::backendDataOut, this, &CmdExecutor::backendFromCmd); - connect(cmd, &InternCommand::authOk, this, &CmdExecutor::authOk); - connect(cmd, &InternCommand::termAllCommands, this, &CmdExecutor::termAllCommands); - connect(cmd, &InternCommand::termCommandId, this, &CmdExecutor::termCommandId); - connect(cmd, &InternCommand::reloadCommands, this, &CmdExecutor::buildCommands); - - connectCommon(cmd, cmdOutput, cmdId, cmdName); -} - -void CmdExecutor::connectCommon(ExternCommand *cmd, CommandOutput *cmdOutput, quint16 cmdId, const QString &cmdName) -{ - wrCrashDebugInfo(" exe func: connectCommon()\n cmd id: " + QString::number(cmdId) + "\n cmd name: " + cmdName); - - connect(cmdOutput, &CommandOutput::closeChById, this, &CmdExecutor::closeChById); - connect(cmdOutput, &CommandOutput::openChById, this, &CmdExecutor::openChById); - connect(cmdOutput, &CommandOutput::closeChByName, this, &CmdExecutor::closeChByName); - connect(cmdOutput, &CommandOutput::openChByName, this, &CmdExecutor::openChByName); - connect(cmdOutput, &CommandOutput::cmdFinished, this, &CmdExecutor::commandFinished); - connect(cmdOutput, &CommandOutput::enableLoop, this, &CmdExecutor::enableLoop); - connect(cmdOutput, &CommandOutput::enableMoreInput, this, &CmdExecutor::enableMoreInput); - - connect(cmd, &ExternCommand::closeChById, cmdOutput, &CommandOutput::closeChIdFromCmdObj); - connect(cmd, &ExternCommand::openChById, cmdOutput, &CommandOutput::openChIdFromCmdObj); - connect(cmd, &ExternCommand::closeChByName, cmdOutput, &CommandOutput::closeChNameFromCmdObj); - connect(cmd, &ExternCommand::openChByName, cmdOutput, &CommandOutput::openChNameFromCmdObj); - connect(cmd, &ExternCommand::cmdFinished, cmdOutput, &CommandOutput::finished); - connect(cmd, &ExternCommand::enableLoop, cmdOutput, &CommandOutput::enableLoopFromCmdObj); - connect(cmd, &ExternCommand::enableMoreInput, cmdOutput, &CommandOutput::enableMoreInputFromCmdObj); - connect(cmd, &ExternCommand::dataToClient, cmdOutput, &CommandOutput::dataFromCmdObj); - - connect(cmd, &ExternCommand::castToPeers, this, &CmdExecutor::castToPeers); - connect(cmd, &ExternCommand::toPeer, this, &CmdExecutor::toPeer); - connect(cmd, &ExternCommand::closeSession, this, &CmdExecutor::endSession); - connect(cmd, &ExternCommand::logout, this, &CmdExecutor::logout); - - cmd->cmdId = cmdId; - cmd->inMoreInputMode = false; - cmd->inLoopMode = false; - cmd->errSent = false; - - cmd->setObjectName(cmdName); - cmdOutput->setCmdId(cmdId); -} - -void CmdExecutor::enableLoop(quint16 cmdId, bool state) -{ - if (state) - { - uniqueAdd(cmdId, activeLoopCmds); - } - else - { - activeLoopCmds.removeAll(cmdId); - } -} - -void CmdExecutor::enableMoreInput(quint16 cmdId, bool state) -{ - if (state) - { - uniqueAdd(cmdId, moreInputCmds); - } - else - { - moreInputCmds.removeAll(cmdId); - } -} - -void CmdExecutor::preExe(ExternCommand *cmdObj, quint16 cmdId) -{ - cmdObj->cmdId = cmdId; - cmdObj->errSent = false; -} - -void CmdExecutor::preExe(const QList &cmdObjs, quint16 cmdId) -{ - for (auto cmdObj : cmdObjs) - { - preExe(cmdObj, cmdId); - } -} - -void CmdExecutor::exeCmd(quint16 cmdId, const QByteArray &data, uchar typeId) -{ - wrCrashDebugInfo(" exe func: exeCmd()\n cmd id: " + QString::number(cmdId) + "\n type id: " + QString::number(typeId)); - - if (!commands.contains(cmdId)) - { - emit dataToSession(cmdId, toTEXT("err: The requested command id: '" + QString::number(cmdId) + "' does not exists.\n"), ERR); - emit dataToSession(cmdId, QByteArray(), IDLE); - } - else if (!pausedCmds.contains(cmdId)) - { - ExternCommand *cmdObj = commands[cmdId]; - - preExe(cmdObj, cmdId); - preExe(cmdObj->internCommands.values(), cmdId); - - cmdObj->procBin(rdSharedObjs, data, typeId); - - if (!activeLoopCmds.contains(cmdId) && !moreInputCmds.contains(cmdId)) - { - emit cmdObj->cmdFinished(); - } - - nextLoopCmd(); - } -} - -void CmdExecutor::nextLoopCmd() -{ - if (!activeLoopCmds.isEmpty()) - { - if (loopIndex == activeLoopCmds.size()) - { - loopIndex = 0; - } - - wrCrashDebugInfo(" exe func: nextLoopCmd()\n loop index: " + QString::number(loopIndex)); - - emit loop(activeLoopCmds[loopIndex++], QByteArray(), TEXT); - } -} - -void CmdExecutor::termCommandObj(quint16 cmdId, ExternCommand *cmd, bool del) -{ - wrCrashDebugInfo(" exe func: termCommandObj()\n cmd id: " + QString::number(cmdId)); - - if (moreInputCmds.contains(cmdId) || activeLoopCmds.contains(cmdId)) - { - cmd->term(); - - cmd->inLoopMode = false; - cmd->inMoreInputMode = false; - - for (auto internObj : cmd->internCommands.values()) - { - if (internObj->inLoopMode || internObj->inMoreInputMode) - { - internObj->term(); - - internObj->inLoopMode = false; - internObj->inMoreInputMode = false; - } - } - - emit dataToSession(cmdId, QByteArray(), IDLE); - } - - moreInputCmds.removeAll(cmdId); - activeLoopCmds.removeAll(cmdId); - pausedCmds.removeAll(cmdId); - - if (del) - { - wrCrashDebugInfo(" exe func: termCommandObj()\n cmd id: " + QString::number(cmdId) + "\n note: deleting intern commands"); - - for (auto internObj : cmd->internCommands.values()) - { - internObj->aboutToDelete(); - internObj->deleteLater(); - } - - wrCrashDebugInfo(" exe func: termCommandObj()\n cmd id: " + QString::number(cmdId) + "\n note: calling the command object's aboutToDelete()"); - - cmd->aboutToDelete(); - cmd->deleteLater(); - - commands.remove(cmdId); - cmdNames.remove(cmdId); - - for (auto&& list : cmdIdsByModName.values()) - { - if (list.contains(cmdId)) - { - list.removeAll(cmdId); - - break; - } - } - - emit dataToSession(ASYNC_RM_CMD, wrInt(cmdId, 16), CMD_ID); - } -} - -void CmdExecutor::termCommandId(quint16 cmdId) -{ - if (commands.contains(cmdId)) - { - termCommandObj(cmdId, commands[cmdId]); - } -} - -void CmdExecutor::termCommandsInList(const QList &cmds, bool del) -{ - for (auto&& cmdId : cmds) - { - termCommandObj(cmdId, commands[cmdId], del); - } -} - -void CmdExecutor::termAllCommands() -{ - termCommandsInList(moreInputCmds, false); - termCommandsInList(activeLoopCmds, false); - termCommandsInList(pausedCmds, false); -} - -void CmdExecutor::close() -{ - termCommandsInList(commands.keys(), true); - - for (auto cmdLoader : cmdLoaders.values()) - { - cmdLoader->aboutToDelete(); - cmdLoader->deleteLater(); - } - - for (auto plugin : plugins.values()) - { - plugin->unload(); - plugin->deleteLater(); - } - - cleanupDbConnection(); - - emit okToDelete(); -} - -void CmdExecutor::commandFinished(quint16 cmdId) -{ - emit dataToSession(cmdId, QByteArray(), IDLE); - - moreInputCmds.removeAll(cmdId); - activeLoopCmds.removeAll(cmdId); - pausedCmds.removeAll(cmdId); -} - -QString CmdExecutor::makeCmdUnique(const QString &name) -{ - QString strNum; - QStringList names = cmdNames.values(); - - for (int j = 1; names.contains(QString(name + strNum).toLower()); ++j) - { - strNum = "_" + QString::number(j); - } - - return QString(name + strNum).toLower(); -} - -void CmdExecutor::procInternRequest(ExternCommand *cmd, quint16 cmdId, const QString &cmdName) -{ - wrCrashDebugInfo(" exe func: procInternRequest()\n cmd id: " + QString::number(cmdId)); - - QStringList internCmdNames = internalCmds->cmdList(); - - for (auto&& reqCmdName : cmd->internRequest()) - { - if (internCmdNames.contains(reqCmdName, Qt::CaseInsensitive)) - { - InternCommand *cmdObj = internalCmds->cmdObj(reqCmdName); - - if (cmdObj != nullptr) - { - connectInternCmd(cmdObj, cmdId, cmdName); - - cmdObj->setParent(cmd); - cmd->internCommands.insert(reqCmdName, cmdObj); - } - } - } -} - -quint16 CmdExecutor::getModIdOffs(const QString &name) -{ - if (name == INTERN_MOD_NAME) - { - return MAX_CMDS_PER_MOD; - } - else - { - Query db(this); - - db.setType(Query::PULL, TABLE_MODULES); - db.addColumn(COLUMN_CMD_ID_OFFS); - db.addCondition(COLUMN_MOD_NAME, name); - db.exec(); - - return static_cast(db.getData(COLUMN_CMD_ID_OFFS).toUInt()); - } -} - -QString CmdExecutor::getModFile(const QString &modName) -{ - Query db(this); - - db.setType(Query::PULL, TABLE_MODULES); - db.addColumn(COLUMN_MOD_MAIN); - db.addCondition(COLUMN_MOD_NAME, modName); - db.exec(); - - return db.getData(COLUMN_MOD_MAIN).toString(); -} - -void CmdExecutor::loadModFile(const QString &modName) -{ - bool modOk = false; - QStringList ver = QCoreApplication::applicationVersion().split('.'); - QString mainFilePath = getModFile(modName); - QString path = QFileInfo(mainFilePath).path(); - auto *lib = new QLibrary(mainFilePath, this); - - lib->load(); - - ModImportFunc importFunc = reinterpret_cast(lib->resolve(MOD_IMPORT_FUNC)); - - if (!importFunc) - { - qDebug() << "CmdExecutor::loadModFile() err: failed to load mod lib file: " << mainFilePath << " reason: " << lib->errorString(); - } - else - { - wrCrashDebugInfo(" exe func: loadModFile()\n path: " + mainFilePath + " \nmod name: " + modName + " \nnote: calling the module's import function."); - - CommandLoader *cmdLoader = importFunc(); - - wrCrashDebugInfo(" exe func: loadModFile()\n path: " + mainFilePath + " \nmod name: " + modName + " \nnote: running import rev negotiations."); - - if (!cmdLoader) - { - qDebug() << "CmdExecutor::loadModFile() err: failed to load mod lib file: " << mainFilePath << " reason: the CommandLoader object returned by the import function is null."; - } - else if (cmdLoader->rev() < IMPORT_REV) - { - qDebug() << "CmdExecutor::loadModFile() err: failed to load mod lib file: " << mainFilePath << " reason: module import rev " << cmdLoader->rev() << " not compatible with host rev " << IMPORT_REV << "."; - } - else if (!cmdLoader->hostRevOk(IMPORT_REV, ver[0].toUShort(), ver[1].toUShort(), ver[2].toUShort())) - { - qDebug() << "CmdExecutor::loadModFile() err: failed to load mod lib file: " << mainFilePath << " the module rejected the host rev/version. reason: " << cmdLoader->lastError() << "."; - } - else - { - modOk = true; - - wrCrashDebugInfo(" exe func: loadModFile()\n path: " + mainFilePath + " \nmod name: " + modName + " \nnote: calling the module's modPath() function."); - - cmdLoader->modPath(path); - - cmdLoaders.insert(modName, cmdLoader); - plugins.insert(modName, lib); - } - } - - if (!modOk) - { - lib->unload(); - lib->deleteLater(); - } -} - -void CmdExecutor::unloadModFile(const QString &modName) -{ - if (cmdIdsByModName.contains(modName)) - { - termCommandsInList(cmdIdsByModName[modName], true); - - wrCrashDebugInfo(" exe func: unloadModFile()\n mod name: " + modName + "\n note: calling the modules's aboutToDelete()."); - - cmdLoaders[modName]->aboutToDelete(); - cmdLoaders[modName]->deleteLater(); - - wrCrashDebugInfo(" exe func: unloadModFile()\n mod name: " + modName + "\n note: calling the modules's library unload function."); - - plugins[modName]->unload(); - plugins[modName]->deleteLater(); - - cmdIdsByModName.remove(modName); - cmdLoaders.remove(modName); - plugins.remove(modName); - } -} - -void CmdExecutor::addCommandToList(quint16 cmdId, const QString &cmdName, const QString &modName, ExternCommand *cmdObj) -{ - procInternRequest(cmdObj, cmdId, cmdName); - - commands.insert(cmdId, cmdObj); - cmdNames.insert(cmdId, cmdName); - - if (cmdIdsByModName.contains(modName)) - { - cmdIdsByModName[modName].append(cmdId); - } - else - { - QList list; - - list.append(cmdId); - cmdIdsByModName.insert(modName, list); - } - - emit dataToSession(ASYNC_ADD_CMD, toNEW_CMD(cmdId, cmdName, cmdObj), NEW_CMD); -} - -bool CmdExecutor::allowCmdLoad(const QString &cmdName, const QString &modName, const QStringList &exemptList) -{ - bool ret = false; - - if (exemptList.contains(cmdName, Qt::CaseInsensitive)) - { - ret = true; - } - else - { - Query db(this); - - db.setType(Query::PULL, TABLE_CMD_RANKS); - db.addColumn(COLUMN_HOST_RANK); - db.addCondition(COLUMN_COMMAND, cmdName); - db.addCondition(COLUMN_MOD_NAME, modName); - db.exec(); - - if (db.rows()) - { - uint cmdRank = db.getData(COLUMN_HOST_RANK).toUInt(); - - if (cmdRank >= *rdSharedObjs->hostRank) - { - ret = true; - } - } - else if (*rdSharedObjs->hostRank == 1) - { - ret = true; - } - } - - return ret; -} - -void CmdExecutor::loadInternCmd(CommandLoader *loader, const QString &cmdName, const QString &uniqueName, quint16 id) -{ - auto *cmdObj = reinterpret_cast(loader->cmdObj(cmdName)); - - if (cmdObj != nullptr) - { - connectInternCmd(cmdObj, id, uniqueName); - addCommandToList(id, uniqueName, INTERN_MOD_NAME, cmdObj); - } -} - -void CmdExecutor::loadExternCmd(CommandLoader *loader, const QString &modName, const QString &cmdName, const QString &uniqueName, quint16 id) -{ - ExternCommand *cmdObj = loader->cmdObj(cmdName); - - if (cmdObj != nullptr) - { - connectExternCmd(cmdObj, id, uniqueName); - addCommandToList(id, uniqueName, modName, cmdObj); - } -} - -void CmdExecutor::loadCmd(CommandLoader *loader, quint16 cmdId, const QString &modName, const QString &cmdName, const QString &uniqueCmdName) -{ - wrCrashDebugInfo(" exe func: loadCmd()\n cmd id: " + QString::number(cmdId) + "\n cmd name: " + cmdName); - - if (modName == INTERN_MOD_NAME) - { - loadInternCmd(loader, cmdName, uniqueCmdName, cmdId); - } - else - { - loadExternCmd(loader, modName, cmdName, uniqueCmdName, cmdId); - } -} - -void CmdExecutor::loadCmds(CommandLoader *loader, quint16 idOffs, const QString &modName) -{ - wrCrashDebugInfo(" exe func: loadCmds()\n mod name: " + modName + "\n id offs: " + QString::number(idOffs)); - - QStringList list = loader->cmdList(); - QStringList pub = loader->pubCmdList(); - QStringList exempt = loader->rankExemptList(); - - list.sort(Qt::CaseInsensitive); - - for (quint16 id = 0; (id < list.size()) && (id < MAX_CMDS_PER_MOD); ++id) - { - QString unique = makeCmdUnique(list[id]); - quint16 cmdId = idOffs + id; - - if (validCommandName(unique)) - { - if (commands.contains(cmdId)) - { - if (!allowCmdLoad(list[id], modName, exempt)) - { - termCommandObj(cmdId, commands[cmdId], true); - } - } - else - { - if (rdSharedObjs->userName->isEmpty()) - { - if (pub.contains(list[id], Qt::CaseInsensitive)) - { - loadCmd(loader, cmdId, modName, list[id], unique); - } - } - else if (allowCmdLoad(list[id], modName, exempt)) - { - loadCmd(loader, cmdId, modName, list[id], unique); - } - } - } - else - { - qDebug() << "CmdExecutor::getCmdNames() err: command object name '" << unique << "' is not valid."; - } - } -} - -void CmdExecutor::buildCommands() -{ - for (auto&& loaderName : cmdLoaders.keys()) - { - loadCmds(cmdLoaders[loaderName], getModIdOffs(loaderName), loaderName); - } -} - -bool CmdExecutor::externBlockedTypeId(uchar typeId) -{ - // the internal host objects will handle sending the following TypeIDs to the clients - // and peers. any attempt to do so via ExternCommand object will be blocked since these - // data types can cause some behaviour issues if sent at an unexpected time. - - return (typeId == PRIV_IPC) || (typeId == PUB_IPC) || (typeId == PING_PEERS) || - (typeId == PEER_STAT) || (typeId == MY_INFO) || (typeId == PEER_INFO) || - (typeId == HOST_CERT) || (typeId == IDLE) || (typeId == NEW_CMD); -} - -bool CmdExecutor::p2pBlockedTypeId(uchar typeId) -{ - // this is used to block P2P specific typeIDs. only toPeers() or internDataToIPC() - // should be allowed to send these frame types, all others use this to block them. - - return (typeId == P2P_REQUEST) || (typeId == P2P_OPEN) || (typeId == P2P_CLOSE); -} - -void CmdExecutor::externDataToIPC(quint16 cmdId, const QByteArray &data, uchar typeId) -{ - if (!externBlockedTypeId(typeId) && !p2pBlockedTypeId(typeId)) - { - emit dataToSession(cmdId, data, typeId); - } -} - -void CmdExecutor::internDataToIPC(quint16 cmdId, const QByteArray &data, uchar typeId) -{ - if ((typeId != IDLE) && (typeId != NEW_CMD)) - { - emit dataToSession(cmdId, data, typeId); - } -} - -void CmdExecutor::castToPeers(const QByteArray &data, uchar typeId) -{ - if (!externBlockedTypeId(typeId) && !p2pBlockedTypeId(typeId)) - { - QByteArray castHeader = *rdSharedObjs->wrAbleChIds + wrInt(typeId, 8); - - emit dataToSession(ASYNC_CAST, castHeader + data, PUB_IPC); - } -} - -void CmdExecutor::toPeer(const QByteArray &dst, const QByteArray &data, uchar typeId) -{ - if (!externBlockedTypeId(typeId) && (dst.size() == 28)) - { - QByteArray p2pHeader = dst + *rdSharedObjs->sessionId + wrInt(typeId, 8); - - if (typeId == P2P_REQUEST) - { - if (!rdSharedObjs->p2pPending->contains(dst)) - { - rwSharedObjs->p2pPending->append(dst); - - emit dataToSession(ASYNC_P2P, p2pHeader + toPEER_INFO(rdSharedObjs), PUB_IPC); - } - } - else - { - if ((typeId == P2P_CLOSE) || (typeId == P2P_OPEN)) - { - if (rdSharedObjs->p2pPending->contains(dst) || rdSharedObjs->p2pAccepted->contains(dst)) - { - if (typeId == P2P_CLOSE) - { - rwSharedObjs->p2pPending->removeAll(dst); - rwSharedObjs->p2pAccepted->removeAll(dst); - - emit dataToSession(ASYNC_P2P, p2pHeader + dst, PUB_IPC); - } - else if (!rdSharedObjs->p2pAccepted->contains(dst)) - { - rwSharedObjs->p2pPending->removeAll(dst); - rwSharedObjs->p2pAccepted->append(dst); - - emit dataToSession(ASYNC_P2P, p2pHeader + dst, PUB_IPC); - } - } - } - else - { - emit dataToSession(ASYNC_P2P, p2pHeader + data, PUB_IPC); - } - } - } -} - -void CmdExecutor::backendFromCmd(quint16 cmdId, const QByteArray &data, uchar typeId) -{ - if ((typeId == PUB_IPC) || (typeId == PRIV_IPC) || (typeId == PUB_IPC_WITH_FEEDBACK)) - { - emit dataToSession(cmdId, data, typeId); - } -} - -void CmdExecutor::openOrCloseChByName(quint16 cmdId, const QString &ch, const QString &sub, bool open) -{ - if (!validChName(ch)) - { - emit dataToSession(cmdId, toTEXT("err: '" + ch + "' is not a valid channel name.\n"), ERR); - } - else if (!validChName(sub)) - { - emit dataToSession(cmdId, toTEXT("err: '" + sub + "' is not a valid sub channel name.\n"), ERR); - } - else if (!channelSubExists(ch, sub)) - { - emit dataToSession(cmdId, toTEXT("err: Sub-channel: '" + sub + "' does not exists.\n"), ERR); - } - else - { - Query db(this); - - db.setType(Query::PULL, TABLE_SUB_CHANNELS); - db.addColumn(COLUMN_CHANNEL_ID); - db.addColumn(COLUMN_SUB_CH_ID); - db.addCondition(COLUMN_SUB_CH_NAME, sub); - db.addCondition(COLUMN_CHANNEL_NAME, ch); - db.exec(); - - if (open) - { - openChById(cmdId, db.getData(COLUMN_CHANNEL_ID).toULongLong(), static_cast(db.getData(COLUMN_SUB_CH_ID).toUInt())); - } - else - { - closeChById(cmdId, db.getData(COLUMN_CHANNEL_ID).toULongLong(), static_cast(db.getData(COLUMN_SUB_CH_ID).toUInt())); - } - } -} - -void CmdExecutor::openChByName(quint16 cmdId, const QString &ch, const QString &sub) -{ - openOrCloseChByName(cmdId, ch, sub, true); -} - -void CmdExecutor::closeChByName(quint16 cmdId, const QString &ch, const QString &sub) -{ - openOrCloseChByName(cmdId, ch, sub, false); -} - -void CmdExecutor::openChById(quint16 cmdId, quint64 chId, uchar subId) -{ - QByteArray id = wrInt(chId, 64) + wrInt(subId, 8); - - if (chId == 0) - { - emit dataToSession(cmdId, toTEXT("err: '0' is not a valid channel id. it must an unsigned integer between 1-18446744073709551615.\n"), ERR); - } - else if (countChs(*rdSharedObjs->chIds) == 6) - { - emit dataToSession(cmdId, toTEXT("err: The maximum amount of open sub-channels reached (6).\n"), ERR); - } - else if (containsChId(id, *rdSharedObjs->chIds)) - { - emit dataToSession(cmdId, toTEXT("err: The requested sub-channel is already open.\n"), ERR); - } - else if (!channelSubExists(chId, subId)) - { - emit dataToSession(cmdId, toTEXT("err: The requested sub-channel does not exists.\n"), ERR); - } - else if (channelAccessLevel(rdSharedObjs, chId) > lowestAcessLevel(chId, subId)) - { - emit dataToSession(cmdId, toTEXT("err: Access denied.\n"), ERR); - } - else - { - wrOpenCh(rwSharedObjs, id); - } -} - -void CmdExecutor::closeChById(quint16 cmdId, quint64 chId, uchar subId) -{ - QByteArray id = wrInt(chId, 64) + wrInt(subId, 8); - - if (chId == 0) - { - emit dataToSession(cmdId, toTEXT("err: '0' is not a valid channel id. it must an integer between 1-18446744073709551615.\n"), ERR); - } - else if (!containsChId(id, *rdSharedObjs->chIds)) - { - emit dataToSession(cmdId, toTEXT("err: The requested sub-channel is not open.\n"), ERR); - } - else - { - QByteArray peerStat; - - wrCloseCh(rwSharedObjs, id, peerStat); - - if (!peerStat.isEmpty()) - { - emit dataToSession(ASYNC_LIMITED_CAST, peerStat, PUB_IPC); - } - } -} diff --git a/src/cmd_executor.h b/src/cmd_executor.h deleted file mode 100644 index 9390509..0000000 --- a/src/cmd_executor.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef CMD_EXECUTOR_H -#define CMD_EXECUTOR_H - -// This file is part of MRCI. - -// MRCI is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// MRCI is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with MRCI under the LICENSE.md file. If not, see -// . - -#include "common.h" -#include "int_loader.h" - -typedef CommandLoader *(*ModImportFunc)(); - -class CmdExecutor : public QObject -{ - Q_OBJECT - -private: - - SharedObjs *rdSharedObjs; - RWSharedObjs *rwSharedObjs; - InternalCommandLoader *internalCmds; - QSharedMemory *exeDebugInfo; - QHash cmdLoaders; - QHash plugins; - QHash > cmdIdsByModName; - QList moreInputCmds; - QList activeLoopCmds; - QList pausedCmds; - QHash cmdNames; - QHash commands; - int loopIndex; - - void nextLoopCmd(); - void preExe(ExternCommand *cmdObj, quint16 cmdId); - void preExe(const QList &cmdObjs, quint16 cmdId); - void procInternRequest(ExternCommand *cmd, quint16 cmdId, const QString &cmdName); - void connectCommon(ExternCommand *cmd, CommandOutput *cmdOutput, quint16 cmdId, const QString &cmdName); - void connectInternCmd(InternCommand *cmd, quint16 cmdId, const QString &cmdName); - void connectExternCmd(ExternCommand *cmd, quint16 cmdId, const QString &cmdName); - void openOrCloseChByName(quint16 cmdId, const QString &ch, const QString &sub, bool open); - void loadCmd(CommandLoader *loader, quint16 cmdId, const QString &modName, const QString &cmdName, const QString &uniqueCmdName); - void loadCmds(CommandLoader *loader, quint16 idOffs, const QString &modName); - void loadInternCmd(CommandLoader *loader, const QString &cmdName, const QString &uniqueName, quint16 id); - void loadExternCmd(CommandLoader *loader, const QString &modName, const QString &cmdName, const QString &uniqueName, quint16 id); - void addCommandToList(quint16 cmdId, const QString &cmdName, const QString &modName, ExternCommand *cmdObj); - bool externBlockedTypeId(uchar typeId); - bool p2pBlockedTypeId(uchar typeId); - bool allowCmdLoad(const QString &cmdName, const QString &modName, const QStringList &exemptList); - QString makeCmdUnique(const QString &name); - QString getModFile(const QString &modName); - quint16 getModIdOffs(const QString &name); - -private slots: - - void termAllCommands(); - void enableMoreInput(quint16 cmdId, bool state); - void enableLoop(quint16 cmdId, bool state); - void openChById(quint16 cmdId, quint64 chId, uchar subId); - void openChByName(quint16 cmdId, const QString &ch, const QString &sub); - void closeChById(quint16 cmdId, quint64 chId, uchar subId); - void closeChByName(quint16 cmdId, const QString &ch, const QString &sub); - void termCommandId(quint16 cmdId); - void termCommandObj(quint16 cmdId, ExternCommand *cmd, bool del = false); - void termCommandsInList(const QList &cmds, bool del = false); - void commandFinished(quint16 cmdId); - void externDataToIPC(quint16 cmdId, const QByteArray &data, uchar typeId); - void internDataToIPC(quint16 cmdId, const QByteArray &data, uchar typeId); - void castToPeers(const QByteArray &data, uchar typeId); - void toPeer(const QByteArray &dst, const QByteArray &data, uchar typeId); - -public slots: - - void close(); - void buildCommands(); - void buildCmdLoaders(); - void wrCrashDebugInfo(const QString &msg); - void unloadModFile(const QString &modName); - void loadModFile(const QString &modName); - void exeCmd(quint16 cmdId, const QByteArray &data, uchar typeId); - void backendFromCmd(quint16 cmdId, const QByteArray &data, uchar typeId); - -signals: - - void endSession(); - void logout(); - void authOk(); - void okToDelete(); - void dataToSession(quint16 cmdId, const QByteArray &data, uchar typeId); - void loop(quint16 cmdId, const QByteArray &data, uchar typeId); - -public: - - explicit CmdExecutor(RWSharedObjs *rwShare, SharedObjs *rdOnlyShare, QSharedMemory *debugInfo, QObject *parent = nullptr); -}; - -#endif // CMD_EXECUTOR_H diff --git a/src/cmd_object.cpp b/src/cmd_object.cpp new file mode 100644 index 0000000..dce3871 --- /dev/null +++ b/src/cmd_object.cpp @@ -0,0 +1,219 @@ +#include "cmd_object.h" + +// This file is part of MRCI. + +// MRCI is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// MRCI is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with MRCI under the LICENSE.md file. If not, see +// . + +IPCWorker::IPCWorker(const QString &pipe, QObject *parent) : QObject(parent) +{ + pipeName = pipe; + idleTimer = new IdleTimer(this); + ipcSocket = new QLocalSocket(this); + flags = 0; + + connect(ipcSocket, &QLocalSocket::readyRead, this, &IPCWorker::rdFromIPC); + connect(ipcSocket, &QLocalSocket::disconnected, this, &IPCWorker::ipcClosed); + connect(ipcSocket, &QLocalSocket::connected, this, &IPCWorker::ipcOpened); + connect(idleTimer, &IdleTimer::timeout, this, &IPCWorker::termProc); + + idleTimer->attach(ipcSocket, 60000); //1min idle timeout +} + +void IPCWorker::rdFromIPC() +{ + if (flags & FRAME_RDY) + { + if (ipcSocket->bytesAvailable() >= ipcDataSize) + { + emit dataOut(ipcSocket->read(ipcDataSize), ipcTypeId); + + flags ^= FRAME_RDY; + + rdFromIPC(); + } + } + else if (ipcSocket->bytesAvailable() >= (FRAME_HEADER_SIZE - 4)) + { + QByteArray header = ipcSocket->read(FRAME_HEADER_SIZE - 4); + + ipcTypeId = static_cast(header[0]); + ipcDataSize = static_cast(rdInt(header.mid(1, 3))); + flags |= FRAME_RDY; + + rdFromIPC(); + } +} + +void IPCWorker::dataIn(const QByteArray &data, quint8 typeId) +{ + // format: [typeId][payload_len][payload] + + ipcSocket->write(wrInt(typeId, 8) + wrInt(data.size(), MAX_FRAME_BITS) + data); +} + +void IPCWorker::connectIPC() +{ + ipcSocket->connectToServer(pipeName); +} + +CmdObject::CmdObject(QObject *parent) : MemShare(parent) +{ + flags = 0; + + QStringList args = QCoreApplication::instance()->arguments(); + QString pipe = getParam("-pipe_name", args); + QString sMemKey = getParam("-mem_ses", args); + QString hMemKey = getParam("-mem_host", args); + + if (attachSharedMem(sMemKey, hMemKey)) + { + ipcWorker = new IPCWorker(pipe, nullptr); + keepAliveTimer = new QTimer(this); + + auto *thr = new QThread(nullptr); + + serializeThread(thr); + setupDataBlocks(); + + connect(thr, &QThread::started, ipcWorker, &IPCWorker::connectIPC); + + connect(this, &CmdObject::destroyed, thr, &QThread::deleteLater); + connect(this, &CmdObject::procOut, ipcWorker, &IPCWorker::dataIn); + + connect(keepAliveTimer, &QTimer::timeout, this, &CmdObject::keepAlive); + + connect(ipcWorker, &IPCWorker::dataOut, this, &CmdObject::preProc); + connect(ipcWorker, &IPCWorker::termProc, this, &CmdObject::kill); + connect(ipcWorker, &IPCWorker::ipcClosed, this, &CmdObject::term); + connect(ipcWorker, &IPCWorker::ipcOpened, this, &CmdObject::onIPCConnected); + + keepAliveTimer->setSingleShot(false); + keepAliveTimer->setInterval(30000); //30sec keep alive + ipcWorker->moveToThread(thr); + thr->start(); + } + else + { + kill(); + } +} + +void CmdObject::term() +{ + if (flags & (MORE_INPUT | HALT_STATE | LOOPING)) + { + flags = 0; + + postProc(); + } +} + +void CmdObject::kill() +{ + term(); + + QCoreApplication::instance()->exit(); +} + +void CmdObject::preProc(const QByteArray &data, quint8 typeId) +{ + if (typeId == TERM_CMD) + { + term(); + } + else if (typeId == KILL_CMD) + { + kill(); + } + else if (typeId == HALT_CMD) + { + if (flags & LOOPING) + { + flags |= HALT_STATE; + flags &= ~LOOPING; + } + } + else if (typeId == RESUME_CMD) + { + if (flags & HALT_STATE) + { + flags |= LOOPING; + flags &= ~HALT_STATE; + } + } + else + { + sharedMem->lock(); + + procIn(data, typeId); + + sharedMem->unlock(); + + postProc(); + } +} + +void CmdObject::postProc() +{ + if (flags & LOOPING) + { + preProc(QByteArray(), TEXT); + } + else if (flags & (MORE_INPUT | HALT_STATE)) + { + keepAliveTimer->start(); + } + else + { + keepAliveTimer->stop(); + + emit procOut(QByteArray(), IDLE); + } +} + +void CmdObject::mainTxt(const QString &txt) +{ + emit procOut(toTEXT(txt), TEXT); +} + +void CmdObject::errTxt(const QString &txt) +{ + emit procOut(toTEXT(txt), ERR); +} + +void CmdObject::privTxt(const QString &txt) +{ + emit procOut(toTEXT(txt), PRIV_TEXT); +} + +void CmdObject::bigTxt(const QString &txt) +{ + emit procOut(toTEXT(txt), BIG_TEXT); +} + +void CmdObject::async(quint16 asyncId, quint8 asyncType, const QByteArray &data) +{ + emit procOut(wrInt(asyncId, 16) + data, asyncType); +} + +void CmdObject::keepAlive() +{ + async(ASYNC_KEEP_ALIVE, PRIV_IPC); +} + +QString CmdObject::libName() +{ + return QCoreApplication::applicationName() + " v" + QCoreApplication::applicationVersion() + " " + QString::number(QSysInfo::WordSize) + "Bit"; +} diff --git a/src/cmd_object.h b/src/cmd_object.h new file mode 100644 index 0000000..fe7459e --- /dev/null +++ b/src/cmd_object.h @@ -0,0 +1,99 @@ +#ifndef CMDOBJECT_H +#define CMDOBJECT_H + +// This file is part of MRCI. + +// MRCI is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// MRCI is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with MRCI under the LICENSE.md file. If not, see +// . + +#include "common.h" + +class IPCWorker : public QObject +{ + Q_OBJECT + +private slots: + + void rdFromIPC(); + +private: + + IdleTimer *idleTimer; + QLocalSocket *ipcSocket; + quint32 flags; + quint8 ipcTypeId; + quint32 ipcDataSize; + QString pipeName; + +public slots: + + void dataIn(const QByteArray &data, quint8 typeId); + +public: + + explicit IPCWorker(const QString &pipe, QObject *parent = nullptr); + +public slots: + + void connectIPC(); + +signals: + + void dataOut(const QByteArray &data, quint8 typeId); + void ipcClosed(); + void ipcOpened(); + void termProc(); +}; + +//---------------------- + +class CmdObject : public MemShare +{ + Q_OBJECT + +protected: + + QTimer *keepAliveTimer; + IPCWorker *ipcWorker; + quint32 flags; + + void mainTxt(const QString &txt); + void errTxt(const QString &txt); + void privTxt(const QString &txt); + void bigTxt(const QString &txt); + void async(quint16 asyncId, quint8 asyncType, const QByteArray &data = QByteArray()); + void postProc(); + QString libName(); + + virtual void procIn(const QByteArray &, quint8) {} + +protected slots: + + void preProc(const QByteArray &data, quint8 typeId); + void keepAlive(); + void term(); + void kill(); + + virtual void onIPCConnected() {} + +public: + + explicit CmdObject(QObject *parent = nullptr); + +signals: + + void procOut(const QByteArray &data, quint8 typeId); +}; + +#endif // CMDOBJECT_H diff --git a/src/cmd_proc.cpp b/src/cmd_proc.cpp new file mode 100644 index 0000000..f856d13 --- /dev/null +++ b/src/cmd_proc.cpp @@ -0,0 +1,643 @@ +#include "cmd_proc.h" + +// This file is part of MRCI. + +// MRCI is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// MRCI is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with MRCI under the LICENSE.md file. If not, see +// . + +ModProcess::ModProcess(const QString &app, const QString &memSes, const QString &memHos, const QString &pipe, QObject *parent) : QProcess(parent) +{ + flags = 0; + ipcTypeId = 0; + ipcDataSize = 0; + hostRank = 0; + modCmdNames = nullptr; + cmdUniqueNames = nullptr; + cmdRealNames = nullptr; + cmdAppById = nullptr; + cmdIds = nullptr; + ipcSocket = nullptr; + ipcServ = new QLocalServer(this); + idleTimer = new IdleTimer(this); + sesMemKey = memSes; + hostMemKey = memHos; + pipeName = pipe; + + ipcServ->setMaxPendingConnections(1); + + connect(this, &QProcess::readyReadStandardError, this, &ModProcess::rdFromStdErr); + connect(this, &QProcess::readyReadStandardOutput, this, &ModProcess::rdFromStdOut); + connect(this, &QProcess::errorOccurred, this, &ModProcess::err); + connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(onFinished(int,QProcess::ExitStatus))); + + connect(ipcServ, &QLocalServer::newConnection, this, &ModProcess::newIPCLink); + connect(idleTimer, &IdleTimer::timeout, this, &ModProcess::kill); + + setProgram(app); +} + +void ModProcess::rdFromStdErr() +{ + emit dataToClient(toCmdId32(ASYNC_SYS_MSG, 0), toTEXT(readAllStandardError()), ERR); +} + +void ModProcess::rdFromStdOut() +{ + emit dataToClient(toCmdId32(ASYNC_SYS_MSG, 0), toTEXT(readAllStandardOutput()), TEXT); +} + +quint16 ModProcess::genCmdId() +{ + quint16 ret = 256; + + if (flags & SESSION_PARAMS_SET) + { + while(cmdIds->contains(ret)) ret++; + } + + return ret; +} + +QString ModProcess::makeCmdUnique(const QString &name) +{ + QString strNum; + QStringList names = cmdUniqueNames->values(); + + for (int i = 1; names.contains(name + strNum); ++i) + { + strNum = "_" + QString::number(i); + } + + return QString(name + strNum).toLower(); +} + +bool ModProcess::isCmdLoaded(const QString &name) +{ + bool ret = false; + + if (modCmdNames->contains(program())) + { + if (modCmdNames->value(program()).contains(name)) + { + ret = true; + } + } + + return ret; +} + +bool ModProcess::allowCmdLoad(const QString &cmdName) +{ + bool ret = false; + + if (flags & (LOADING_PUB_CMDS | LOADING_EXEMPT_CMDS)) + { + ret = true; + } + else if (!cmdRanks.contains(cmdName)) + { + ret = (hostRank == 1); + } + else + { + ret = (cmdRanks[cmdName] >= hostRank); + } + + return ret; +} + +void ModProcess::onDataFromProc(quint8 typeId, const QByteArray &data) +{ + if ((typeId == NEW_CMD) && (flags & SESSION_PARAMS_SET)) + { + QString cmdName = fromTEXT(data.mid(3, 128)).trimmed().toLower(); + + if (isCmdLoaded(cmdName)) + { + if (!allowCmdLoad(cmdName)) + { + quint16 cmdId = cmdRealNames->key(cmdName); + + cmdIds->removeOne(cmdId); + cmdRealNames->remove(cmdId); + cmdUniqueNames->remove(cmdId); + cmdAppById->remove(cmdId); + + if (modCmdNames->contains(program())) + { + modCmdNames->operator[](program()).removeOne(cmdName); + } + + emit cmdUnloaded(cmdId); + emit dataToClient(toCmdId32(ASYNC_RM_CMD, 0), wrInt(cmdId, 16), CMD_ID); + } + } + else if (allowCmdLoad(cmdName)) + { + quint16 cmdId = genCmdId(); + QByteArray cmdIdBa = wrInt(cmdId, 16); + QString unique = makeCmdUnique(cmdName); + + cmdIds->append(cmdId); + cmdRealNames->insert(cmdId, cmdName); + cmdUniqueNames->insert(cmdId, unique); + cmdAppById->insert(cmdId, program()); + + if (modCmdNames->contains(program())) + { + modCmdNames->operator[](program()).append(cmdName); + } + else + { + QStringList list = QStringList() << cmdName; + + modCmdNames->insert(program(), list); + } + + QByteArray frame = cmdIdBa + data.mid(2, 1) + fixedToTEXT(unique, 128) + data.mid(131); + + emit dataToClient(toCmdId32(ASYNC_ADD_CMD, 0), frame, NEW_CMD); + } + } +} + +void ModProcess::rdFromIPC() +{ + if (flags & FRAME_RDY) + { + if (ipcSocket->bytesAvailable() >= ipcDataSize) + { + onDataFromProc(ipcTypeId, ipcSocket->read(ipcDataSize)); + + flags ^= FRAME_RDY; + + rdFromIPC(); + } + } + else if (ipcSocket->bytesAvailable() >= (FRAME_HEADER_SIZE - 4)) + { + QByteArray header = ipcSocket->read(FRAME_HEADER_SIZE - 4); + + ipcTypeId = static_cast(header[0]); + ipcDataSize = static_cast(rdInt(header.mid(1, 3))); + flags |= FRAME_RDY; + + rdFromIPC(); + } +} + +void ModProcess::ipcDisconnected() +{ + if (ipcSocket != nullptr) + { + ipcSocket->deleteLater(); + } + + ipcSocket = nullptr; +} + +void ModProcess::newIPCLink() +{ + if (ipcSocket != nullptr) + { + ipcServ->nextPendingConnection()->deleteLater(); + } + else + { + ipcSocket = ipcServ->nextPendingConnection(); + + connect(ipcSocket, &QLocalSocket::readyRead, this, &ModProcess::rdFromIPC); + connect(ipcSocket, &QLocalSocket::disconnected, this, &ModProcess::ipcDisconnected); + + idleTimer->attach(ipcSocket, 120000); //2min idle timeout + + onReady(); + } +} + +void ModProcess::setSessionParams(QHash *uniqueNames, + QHash *realNames, + QHash *appById, + QHash *namesForMod, + QList *ids, + quint32 rnk) +{ + flags |= SESSION_PARAMS_SET; + modCmdNames = namesForMod; + cmdUniqueNames = uniqueNames; + cmdRealNames = realNames; + cmdAppById = appById; + cmdIds = ids; + hostRank = rnk; + + Query db(this); + + db.setType(Query::PULL, TABLE_CMD_RANKS); + db.addColumn(COLUMN_HOST_RANK); + db.addColumn(COLUMN_COMMAND); + db.addCondition(COLUMN_MOD_MAIN, program()); + db.exec(); + + for (int i = 0; i < db.rows(); ++i) + { + cmdRanks.insert(db.getData(COLUMN_COMMAND, i).toString(), db.getData(COLUMN_HOST_RANK, i).toUInt()); + } +} + +void ModProcess::onFailToStart() +{ + emit dataToClient(toCmdId32(ASYNC_SYS_MSG, 0), toTEXT("\nerr: A module failed to start so some commands may not have loaded. detailed error information was logged for admin review.\n"), ERR); +} + +void ModProcess::err(QProcess::ProcessError error) +{ + if (error == QProcess::FailedToStart) + { + qDebug() << "err: Module process: " << program() << " failed to start. reason: " << errorString(); + + emit finished(1, QProcess::CrashExit); + + onFailToStart(); + } +} + +bool ModProcess::openPipe() +{ + bool ret = ipcServ->listen(pipeName); + + fullPipe = ipcServ->fullServerName(); + + if (!ipcServ->isListening()) + { + QFile::remove(fullPipe); + + ret = ipcServ->listen(pipeName); + } + + return ret; +} + +bool ModProcess::startProc(const QStringList &args) +{ + bool ret = false; + + if (openPipe()) + { + fullPipe = ipcServ->fullServerName(); + + setArguments(QStringList() << "-pipe_name" << fullPipe << "-mem_ses" << sesMemKey << "-mem_host" << hostMemKey << args); + start(); + } + else + { + setErrorString("Unable to open pipe: " + fullPipe + " " + ipcServ->errorString()); + + emit errorOccurred(QProcess::FailedToStart); + } + + return ret; +} + +bool ModProcess::loadPublicCmds() +{ + flags |= LOADING_PUB_CMDS; + + return startProc(QStringList() << "-public_cmds"); +} + +bool ModProcess::loadUserCmds() +{ + flags |= LOADING_USER_CMDS; + + return startProc(QStringList() << "-user_cmds"); +} + +bool ModProcess::loadExemptCmds() +{ + flags |= LOADING_EXEMPT_CMDS; + + return startProc(QStringList() << "-exempt_cmds"); +} + +void ModProcess::cleanupPipe() +{ + ipcServ->close(); + + if (QFile::exists(fullPipe)) + { + QFile::remove(fullPipe); + } + + ipcDisconnected(); +} + +void ModProcess::onReady() +{ + QStringList hostVer = QCoreApplication::applicationVersion().split('.'); + QByteArray verFrame; + + verFrame.append(wrInt(hostVer[0].toULongLong(), 16)); + verFrame.append(wrInt(hostVer[1].toULongLong(), 16)); + verFrame.append(wrInt(hostVer[2].toULongLong(), 16)); + + wrIpcFrame(HOST_VER, verFrame); +} + +void ModProcess::onFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + Q_UNUSED(exitCode) + Q_UNUSED(exitStatus) + + cleanupPipe(); + deleteLater(); +} + +void ModProcess::wrIpcFrame(quint8 typeId, const QByteArray &data) +{ + if (ipcSocket != nullptr) + { + ipcSocket->write(wrInt(typeId, 8) + wrInt(data.size(), MAX_FRAME_BITS) + data); + } +} + +CmdProcess::CmdProcess(quint32 id, const QString &cmd, const QString &modApp, const QString &memSes, const QString &memHos, const QString &pipe, QObject *parent) : ModProcess(modApp, memSes, memHos, pipe, parent) +{ + cmdId = id; + cmdName = cmd; + cmdIdle = false; +} + +void CmdProcess::setSessionParams(QSharedMemory *mem, char *sesId, char *wrableSubChs) +{ + sesMem = mem; + sessionId = sesId; + openWritableSubChs = wrableSubChs; +} + +void CmdProcess::killCmd() +{ + wrIpcFrame(KILL_CMD, QByteArray()); + + QTimer::singleShot(3000, this, SLOT(kill())); +} + +void CmdProcess::killCmd16(quint16 id16) +{ + if (toCmdId16(cmdId) == id16) + { + killCmd(); + } +} + +void CmdProcess::killCmd32(quint32 id32) +{ + if (cmdId == id32) + { + killCmd(); + } +} + +void CmdProcess::onReady() +{ + emit cmdProcReady(cmdId, this); +} + +void CmdProcess::onFailToStart() +{ + emit dataToClient(cmdId, toTEXT("err: The command failed to start. error details were logged for admin review.\n"), ERR); + emit dataToClient(cmdId, QByteArray(), IDLE); +} + +void CmdProcess::onFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + if (!cmdIdle) + { + emit dataToClient(cmdId, toTEXT("err: The command has stopped unexpectedly or it has failed to send an IDLE frame before exiting.\n"), ERR); + emit dataToClient(cmdId, QByteArray(), IDLE); + } + + emit cmdProcFinished(cmdId); + + ModProcess::onFinished(exitCode, exitStatus); +} + +void CmdProcess::rdFromStdErr() +{ + emit dataToClient(cmdId, toTEXT(readAllStandardError()), ERR); +} + +void CmdProcess::rdFromStdOut() +{ + emit dataToClient(cmdId, toTEXT(readAllStandardOutput()), TEXT); +} + +void CmdProcess::dataFromSession(quint32 id, const QByteArray &data, quint8 dType) +{ + if (id == cmdId) + { + cmdIdle = false; + + wrIpcFrame(dType, data); + } +} + +bool CmdProcess::validAsync(quint16 async, const QByteArray &data, QTextStream &errMsg) +{ + bool ret = true; + + if ((async == ASYNC_USER_DELETED) || (async == ASYNC_RW_MY_INFO) || (async == ASYNC_USER_LOGIN)) + { + if (data.size() != BLKSIZE_USER_ID) + { + ret = false; errMsg << "the 256bit user id is not " << BLKSIZE_USER_ID << " bytes long."; + } + } + else if (async == ASYNC_USER_RENAMED) + { + if (data.size() != (BLKSIZE_USER_ID + BLKSIZE_USER_NAME)) + { + ret = false; errMsg << "expected data containing the user id and name to be " << (BLKSIZE_USER_ID + BLKSIZE_USER_NAME) << " bytes long."; + } + } + else if (async == ASYNC_DISP_RENAMED) + { + if (data.size() != (BLKSIZE_USER_ID + BLKSIZE_DISP_NAME)) + { + ret = false; errMsg << "expected data containing the user id and display name to be " << (BLKSIZE_USER_ID + BLKSIZE_DISP_NAME) << " bytes long."; + } + } + else if (async == ASYNC_MAXSES) + { + if (data.size() != BLKSIZE_HOST_LOAD) + { + ret = false; errMsg << "the 32bit max session int is not " << BLKSIZE_HOST_LOAD << " bytes long."; + } + } + else if (async == ASYNC_USER_RANK_CHANGED) + { + if (data.size() != (BLKSIZE_USER_ID + BLKSIZE_HOST_RANK)) + { + ret = false; errMsg << "expected data containing the user id and host rank to be " << (BLKSIZE_USER_ID + BLKSIZE_HOST_RANK) << " bytes long."; + } + } + else if ((async == ASYNC_CAST) || (async == ASYNC_LIMITED_CAST)) + { + int payloadOffs = (MAX_OPEN_SUB_CHANNELS * BLKSIZE_SUB_CHANNEL) + 1; + + sesMem->lock(); + + if (data.size() < payloadOffs) + { + ret = false; errMsg << "cast header is not at least " << payloadOffs << " bytes long."; + } + else if (memcmp(data.data(), openWritableSubChs, static_cast(payloadOffs - 1)) != 0) + { + ret = false; errMsg << "the open sub-channels (writable) does not match the actual open sub-channels."; + } + else if (rd8BitFromBlock(data.data() + (payloadOffs - 1)) == PING_PEERS) + { + // casting PING_PEERS to active update sessions are blocked. + // command processes should use ASYNC_PING_PEERS instead. + + ret = false; errMsg << "attempted to cast PING_PEERS which is forbidden for module commands."; + } + + sesMem->unlock(); + } + else if (async == ASYNC_TO_PEER) + { + ret = false; errMsg << "ASYNC_TO_PEER is forbidden for module commands."; + } + else if (async == ASYNC_P2P) + { + int payloadOffs = ((BLKSIZE_SESSION_ID * 2) + 1); + + if (data.size() < payloadOffs) + { + ret = false; errMsg << "p2p header is not at least " << payloadOffs << " bytes long."; + } + else if (memcmp(data.data() + BLKSIZE_SESSION_ID, sessionId, BLKSIZE_SESSION_ID) != 0) + { + // make sure P2P async commands source session id is the actual the local + // session. fraudulent P2P async's are blocked. + + ret = false; errMsg << "the source session id does not match the actual session id."; + } + } + else if (async == ASYNC_CLOSE_P2P) + { + if (data.size() < BLKSIZE_SESSION_ID) + { + ret = false; errMsg << "p2p header is not at least " << BLKSIZE_SESSION_ID << " bytes long."; + } + else if (memcmp(data.data(), sessionId, BLKSIZE_SESSION_ID) != 0) + { + // make sure P2P async commands source session id is the actual the local + // session. fraudulent P2P async's are blocked. + + ret = false; errMsg << "the source session id does not match the actual session id."; + } + } + else if ((async == ASYNC_OPEN_SUBCH) || (async == ASYNC_CLOSE_SUBCH)) + { + if (data.size() != BLKSIZE_SUB_CHANNEL) + { + ret = false; errMsg << "the 72bit sub-channel id is not " << BLKSIZE_SUB_CHANNEL << " bytes long."; + } + } + else if ((async == ASYNC_RM_SUB_CH) || (async == ASYNC_SUB_CH_LEVEL_CHG) || + (async == ASYNC_RM_RDONLY) || (async == ASYNC_ADD_RDONLY) || + (async == ASYNC_CH_ACT_FLAG) || (async == ASYNC_NEW_SUB_CH) || + (async == ASYNC_RENAME_SUB_CH)) + { + if (data.size() < BLKSIZE_SUB_CHANNEL) + { + ret = false; errMsg << "a 72bit sub-channel id header is not present."; + } + } + else if ((cmdId == ASYNC_NEW_CH_MEMBER) || (cmdId == ASYNC_INVITED_TO_CH) || + (cmdId == ASYNC_INVITE_ACCEPTED) || (cmdId == ASYNC_RM_CH_MEMBER) || + (cmdId == ASYNC_MEM_LEVEL_CHANGED)) + { + if (data.size() < (BLKSIZE_USER_ID + BLKSIZE_CHANNEL_ID)) + { + ret = false; errMsg << "the channel member info header is not at least " << (BLKSIZE_USER_ID + BLKSIZE_CHANNEL_ID) << "bytes long."; + } + } + else if ((cmdId == ASYNC_RENAME_CH) || (cmdId == ASYNC_DEL_CH)) + { + if (data.size() < BLKSIZE_CHANNEL_ID) + { + ret = false; errMsg << "a 64bit channel id header was not found."; + } + } + + return ret; +} + +void CmdProcess::onDataFromProc(quint8 typeId, const QByteArray &data) +{ + if ((typeId == PRIV_IPC) || (typeId == PUB_IPC) || (typeId == PUB_IPC_WITH_FEEDBACK)) + { + if (data.size() >= 2) + { + quint16 async = rd16BitFromBlock(data.data()); + + // ASYNC_KEEP_ALIVE is blocked but not considered an error. it has already done + // it's job by getting transffered so it doesn't need to go any further. + + if (async != ASYNC_KEEP_ALIVE) + { + QByteArray payload = rdFromBlock(data.data() + 2, static_cast(data.size() - 2)); + + QString errMsg; + QTextStream errTxt(&errMsg); + + if (validAsync(async, payload, errTxt)) + { + if (typeId == PRIV_IPC) + { + emit privIPC(async, payload); + } + else if (typeId == PUB_IPC) + { + emit pubIPC(async, payload); + } + else + { + emit pubIPCWithFeedBack(async, payload); + } + } + else + { + qDebug() << "async id: " << async << " from command id: " << toCmdId16(cmdId) << " blocked. reason: " << errMsg; + } + } + } + } + else + { + if (typeId == IDLE) + { + cmdIdle = true; + } + + emit dataToClient(cmdId, data, typeId); + } +} + +bool CmdProcess::startCmdProc() +{ + return startProc(QStringList() << "-run_cmd" << cmdName); +} diff --git a/src/cmd_proc.h b/src/cmd_proc.h new file mode 100644 index 0000000..8fb5597 --- /dev/null +++ b/src/cmd_proc.h @@ -0,0 +1,144 @@ +#ifndef CMD_PROC_H +#define CMD_PROC_H + +// This file is part of MRCI. + +// MRCI is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// MRCI is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with MRCI under the LICENSE.md file. If not, see +// . + +#include "common.h" + +class ModProcess : public QProcess +{ + Q_OBJECT + +private: + + QHash cmdRanks; + QHash *modCmdNames; + QHash *cmdUniqueNames; + QHash *cmdRealNames; + QHash *cmdAppById; + QList *cmdIds; + IdleTimer *idleTimer; + + quint16 genCmdId(); + QString makeCmdUnique(const QString &name); + bool allowCmdLoad(const QString &cmdName); + +protected: + + QString pipeName; + QString fullPipe; + QString sesMemKey; + QString hostMemKey; + quint8 ipcTypeId; + quint32 ipcDataSize; + quint32 hostRank; + quint32 flags; + QLocalServer *ipcServ; + QLocalSocket *ipcSocket; + + virtual void onReady(); + virtual void onFailToStart(); + virtual void onDataFromProc(quint8 typeId, const QByteArray &data); + + void cleanupPipe(); + void wrIpcFrame(quint8 typeId, const QByteArray &data); + bool startProc(const QStringList &args); + bool isCmdLoaded(const QString &name); + bool openPipe(); + +protected slots: + + virtual void onFinished(int exitCode, QProcess::ExitStatus exitStatus); + virtual void rdFromStdErr(); + virtual void rdFromStdOut(); + + void rdFromIPC(); + void newIPCLink(); + void ipcDisconnected(); + void err(QProcess::ProcessError error); + +public: + + explicit ModProcess(const QString &app, const QString &memSes, const QString &memHos, const QString &pipe, QObject *parent = nullptr); + + void setSessionParams(QHash *uniqueNames, + QHash *realNames, + QHash *appById, + QHash *namesForMod, + QList *ids, + quint32 rnk); + + bool loadPublicCmds(); + bool loadUserCmds(); + bool loadExemptCmds(); + +signals: + + void cmdUnloaded(quint16 cmdId); + void dataToClient(quint32 cmdId, const QByteArray &data, quint8 typeId); +}; + +//---------------------------- + +class CmdProcess : public ModProcess +{ + Q_OBJECT + +private: + + quint32 cmdId; + QString cmdName; + bool cmdIdle; + QSharedMemory *sesMem; + char *sessionId; + char *openWritableSubChs; + + void onReady(); + void onFailToStart(); + void onFinished(int exitCode, QProcess::ExitStatus exitStatus); + void onDataFromProc(quint8 typeId, const QByteArray &data); + bool validAsync(quint16 async, const QByteArray &data, QTextStream &errMsg); + +private slots: + + void killCmd(); + void rdFromStdErr(); + void rdFromStdOut(); + +public slots: + + void killCmd16(quint16 id16); + void killCmd32(quint32 id32); + +public: + + explicit CmdProcess(quint32 id, const QString &cmd, const QString &modApp, const QString &memSes, const QString &memHos, const QString &pipe, QObject *parent = nullptr); + + void dataFromSession(quint32 id, const QByteArray &data, quint8 dType); + void setSessionParams(QSharedMemory *mem, char *sesId, char *wrableSubChs); + bool startCmdProc(); + +signals: + + void cmdProcFinished(quint32 id); + void cmdProcReady(quint32 id, CmdProcess *obj); + void pubIPC(quint16 cmdId, const QByteArray &data); + void privIPC(quint16 cmdId, const QByteArray &data); + void pubIPCWithFeedBack(quint16 cmdId, const QByteArray &data); +}; + +#endif // CMD_PROC_H diff --git a/src/commands/acct_recovery.cpp b/src/commands/acct_recovery.cpp index 2e9ea67..ba8911d 100644 --- a/src/commands/acct_recovery.cpp +++ b/src/commands/acct_recovery.cpp @@ -16,12 +16,12 @@ // along with MRCI under the LICENSE.md file. If not, see // . -RecoverAcct::RecoverAcct(QObject *parent) : InternCommand(parent) {inputOk = false;} -ResetPwRequest::ResetPwRequest(QObject *parent) : InternCommand(parent) {} -VerifyEmail::VerifyEmail(QObject *parent) : InternCommand(parent) {} -IsEmailVerified::IsEmailVerified(QObject *parent) : InternCommand(parent) {} -SetEmailTemplate::SetEmailTemplate(QObject *parent) : InternCommand(parent) {} -PreviewEmail::PreviewEmail(QObject *parent) : InternCommand(parent) {} +RecoverAcct::RecoverAcct(QObject *parent) : CmdObject(parent) {} +ResetPwRequest::ResetPwRequest(QObject *parent) : CmdObject(parent) {} +VerifyEmail::VerifyEmail(QObject *parent) : CmdObject(parent) {} +IsEmailVerified::IsEmailVerified(QObject *parent) : CmdObject(parent) {} +SetEmailTemplate::SetEmailTemplate(QObject *parent) : CmdObject(parent) {} +PreviewEmail::PreviewEmail(QObject *parent) : CmdObject(parent) {} QString RecoverAcct::cmdName() {return "recover_acct";} QString ResetPwRequest::cmdName() {return "request_pw_reset";} @@ -30,38 +30,54 @@ QString IsEmailVerified::cmdName() {return "is_email_verified";} QString SetEmailTemplate::cmdName() {return "set_email_template";} QString PreviewEmail::cmdName() {return "preview_email";} -void RecoverAcct::term() +void delRecoverPw(const QByteArray &uId) { - emit enableMoreInput(false); - - inputOk = false; -} - -void RecoverAcct::delRecoverPw() -{ - Query db(this); + Query db; db.setType(Query::DEL, TABLE_PW_RECOVERY); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); db.setType(Query::UPDATE, TABLE_AUTH_LOG); db.addColumn(COLUMN_COUNT, false); db.addCondition(COLUMN_COUNT, true); db.addCondition(COLUMN_RECOVER_ATTEMPT, true); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); - - term(); } -void RecoverAcct::addToThreshold(const SharedObjs *sharedObjs) +bool expired(const QByteArray &uId) +{ + bool ret = true; + + Query db; + + db.setType(Query::PULL, TABLE_PW_RECOVERY); + db.addColumn(COLUMN_TIME); + db.addCondition(COLUMN_USER_ID, uId); + db.exec(); + + QDateTime expiry = db.getData(COLUMN_TIME).toDateTime().addSecs(3600); // pw datetime + 1hour; + + if (expiry > QDateTime::currentDateTime().toUTC()) + { + ret = false; + } + else + { + delRecoverPw(uId); + } + + return ret; +} + +void RecoverAcct::addToThreshold() { Query db(this); db.setType(Query::PUSH, TABLE_AUTH_LOG); - db.addColumn(COLUMN_USERNAME, uName); - db.addColumn(COLUMN_IPADDR, *sharedObjs->sessionAddr); + db.addColumn(COLUMN_USER_ID, uId); + db.addColumn(COLUMN_IPADDR, rdStringFromBlock(clientIp, BLKSIZE_CLIENT_IP)); db.addColumn(COLUMN_AUTH_ATTEMPT, false); db.addColumn(COLUMN_RECOVER_ATTEMPT, true); db.addColumn(COLUMN_COUNT, true); @@ -72,20 +88,21 @@ void RecoverAcct::addToThreshold(const SharedObjs *sharedObjs) db.addColumn(COLUMN_LOCK_LIMIT); db.exec(); - uint maxAttempts = db.getData(COLUMN_LOCK_LIMIT).toUInt(); + quint32 maxAttempts = db.getData(COLUMN_LOCK_LIMIT).toUInt(); db.setType(Query::PULL, TABLE_AUTH_LOG); db.addColumn(COLUMN_IPADDR); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.addCondition(COLUMN_RECOVER_ATTEMPT, true); db.addCondition(COLUMN_COUNT, true); db.addCondition(COLUMN_ACCEPTED, false); db.exec(); - if (static_cast(db.rows()) > maxAttempts) + if (static_cast(db.rows()) > maxAttempts) { - delRecoverPw(); - term(); + delRecoverPw(uId); + + flags &= ~MORE_INPUT; } else { @@ -94,9 +111,9 @@ void RecoverAcct::addToThreshold(const SharedObjs *sharedObjs) } } -void RecoverAcct::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void RecoverAcct::procIn(const QByteArray &binIn, quint8 dType) { - if (moreInputEnabled() && (dType == TEXT)) + if ((flags & MORE_INPUT) && (dType == TEXT)) { QString pw = fromTEXT(binIn); @@ -109,24 +126,25 @@ void RecoverAcct::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } else { - updatePassword(uName, pw, TABLE_USERS); - delRecoverPw(); - term(); + updatePassword(uId, pw, TABLE_USERS); + delRecoverPw(uId); + + flags &= ~MORE_INPUT; } } else { if (pw.isEmpty()) { - term(); + flags &= ~MORE_INPUT; } else if (!validPassword(pw)) { - addToThreshold(sharedObjs); + addToThreshold(); } - else if (!auth(uName, pw, TABLE_PW_RECOVERY)) + else if (!auth(uId, pw, TABLE_PW_RECOVERY)) { - addToThreshold(sharedObjs); + addToThreshold(); } else { @@ -142,65 +160,71 @@ void RecoverAcct::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, QString email = getParam("-email", args); QString name = getParam("-user", args); - if (!email.isEmpty() && validEmailAddr(email)) name = getUserNameForEmail(email); + if (!email.isEmpty() && validEmailAddr(email)) + { + name = getUserNameForEmail(email); + } if (name.isEmpty() || !validUserName(name)) { errTxt("err: The -user or -email argument is empty, not found or invalid.\n"); } - else if (!userExists(name)) + else if (!userExists(name, &uId)) { errTxt("err: No such user.\n"); } - else if (!recoverPWExists(name)) + else if (!recoverPWExists(uId)) { errTxt("err: This account does not have a recovery password.\n"); } + else if (expired(uId)) + { + errTxt("err: The recovery password has expired.\n"); + } else { privTxt("Enter the temporary password (leave blank to cancel): "); - uName = name; - - emit enableMoreInput(true); + inputOk = false; + flags |= MORE_INPUT; } } } -void ResetPwRequest::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ResetPwRequest::procIn(const QByteArray &binIn, uchar dType) { - Q_UNUSED(sharedObjs); - if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); QString email = getParam("-email", args); QString name = getParam("-user", args); + QByteArray uId; - if (!email.isEmpty() && validEmailAddr(email)) name = getUserNameForEmail(email); + if (!email.isEmpty() && validEmailAddr(email)) + { + name = getUserNameForEmail(email); + } if (name.isEmpty() || !validUserName(name)) { errTxt("err: The -user or -email argument is empty, not found or invalid.\n"); } - else if (!userExists(name)) + else if (!userExists(name, &uId, &email)) { errTxt("err: No such user.\n"); } else { - email = getEmailForUser(name); - QString pw = genPw(); QString date = QDateTime::currentDateTimeUtc().toString("YYYY-MM-DD HH:MM:SS"); - if (recoverPWExists(name)) + if (recoverPWExists(uId)) { - updatePassword(name, pw, TABLE_PW_RECOVERY); + updatePassword(uId, pw, TABLE_PW_RECOVERY); } else { - createTempPw(name, email, pw); + createTempPw(uId, pw); } Query db(this); @@ -227,40 +251,33 @@ void ResetPwRequest::procBin(const SharedObjs *sharedObjs, const QByteArray &bin QProcess::startDetached(expandEnvVariables(app), parseArgs(toTEXT(cmdLine), -1)); - mainTxt("A temporary password was sent to the email address associated with the account.\n"); + mainTxt("A temporary password was sent to the email address associated with the account. this password will expire in 1hour.\n"); } } } -void VerifyEmail::term() +void VerifyEmail::procIn(const QByteArray &binIn, quint8 dType) { - emit enableMoreInput(false); - - code.clear(); -} - -void VerifyEmail::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - if (moreInputEnabled() && (dType == TEXT)) + if ((flags & MORE_INPUT) && (dType == TEXT)) { QString txt = fromTEXT(binIn); - if (txt == code) + if (txt.isEmpty()) + { + flags &= ~MORE_INPUT; + } + else if (txt == code) { Query db(this); db.setType(Query::UPDATE, TABLE_USERS); db.addColumn(COLUMN_EMAIL_VERIFIED, true); - db.addCondition(COLUMN_USERNAME, *sharedObjs->userName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); - emit backendDataOut(ASYNC_RW_MY_INFO, toTEXT(*sharedObjs->userName), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_RW_MY_INFO, PUB_IPC_WITH_FEEDBACK, uId); - term(); - } - else if (txt.isEmpty()) - { - term(); + flags &= ~MORE_INPUT; } else { @@ -270,7 +287,9 @@ void VerifyEmail::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } else if (dType == TEXT) { - QString email = getEmailForUser(*sharedObjs->userName); + uId = rdFromBlock(userId, BLKSIZE_USER_ID); + + QString email = getEmailForUser(uId); if (email.isEmpty()) { @@ -278,9 +297,8 @@ void VerifyEmail::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } else { - QString date = QDateTime::currentDateTimeUtc().toString("yyyy-MM-dd HH:mm:ss"); - - code = QString::number(QRandomGenerator::global()->bounded(100000, 999999)); + flags |= MORE_INPUT; + code = QString::number(QRandomGenerator::global()->bounded(100000, 999999)); Query db(this); @@ -291,13 +309,15 @@ void VerifyEmail::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, db.addColumn(COLUMN_MAIL_SEND); db.exec(); + QString uName = rdStringFromBlock(userName, BLKSIZE_USER_NAME); + QString date = QDateTime::currentDateTimeUtc().toString("yyyy-MM-dd HH:mm:ss"); QString subject = db.getData(COLUMN_CONFIRM_SUBJECT).toString(); QString body = db.getData(COLUMN_CONFIRM_MSG).toString(); QString app = db.getData(COLUMN_MAILERBIN).toString(); QString cmdLine = db.getData(COLUMN_MAIL_SEND).toString(); body.replace(DATE_SUB, date); - body.replace(USERNAME_SUB, *sharedObjs->userName); + body.replace(USERNAME_SUB, uName); body.replace(CONFIRM_CODE_SUB, code); cmdLine.replace(TARGET_EMAIL_SUB, email); @@ -307,51 +327,32 @@ void VerifyEmail::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, QProcess::startDetached(expandEnvVariables(app), parseArgs(toTEXT(cmdLine), -1)); privTxt("A confirmation code was sent to your email address: " + email + "\n\n" + "Please enter that code now or leave blank to cancel: "); - - emit enableMoreInput(true); } } } -void IsEmailVerified::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void IsEmailVerified::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(binIn); + Q_UNUSED(binIn) if (dType == TEXT) { + QByteArray uId = rdFromBlock(userId, BLKSIZE_USER_ID); + Query db(this); db.setType(Query::PULL, TABLE_USERS); db.addColumn(COLUMN_EMAIL_VERIFIED); - db.addCondition(COLUMN_USERNAME, *sharedObjs->userName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); mainTxt(boolStr(db.getData(COLUMN_EMAIL_VERIFIED).toBool()) + "\n"); } } -bool SetEmailTemplate::handlesGenfile() +void SetEmailTemplate::procIn(const QByteArray &binIn, quint8 dType) { - return true; -} - -void SetEmailTemplate::term() -{ - emit enableMoreInput(false); - - textFromFile = false; - dataSent = 0; - - subject.clear(); - bodyText.clear(); - len.clear(); -} - -void SetEmailTemplate::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - Q_UNUSED(sharedObjs); - - if (moreInputEnabled() && (dType == GEN_FILE)) + if ((flags & MORE_INPUT) && (dType == GEN_FILE)) { bodyText.append(fromTEXT(binIn)); @@ -361,8 +362,6 @@ void SetEmailTemplate::procBin(const SharedObjs *sharedObjs, const QByteArray &b if (dataSent >= len.toInt()) { - emit enableMoreInput(false); - mainTxt("\nUpload complete.\n"); proc(); } @@ -371,6 +370,7 @@ void SetEmailTemplate::procBin(const SharedObjs *sharedObjs, const QByteArray &b { QStringList args = parseArgs(binIn, 9); + dataSent = 0; textFromFile = argExists("-client_file", args); subject = getParam("-subject", args); bodyText = getParam("-body", args); @@ -392,22 +392,18 @@ void SetEmailTemplate::procBin(const SharedObjs *sharedObjs, const QByteArray &b if (eType == NONE) { errTxt("err: Which template do you want to change? -reset_template or -confirm_template not found.\n"); - term(); } else if (textFromFile && !isInt(len)) { errTxt("err: '" + len + "' given in -len is not a valid integer.\n"); - term(); } else if (textFromFile && (len.toInt() <= 0)) { errTxt("err: The text file size cannot be 0 or less than 0.\n"); - term(); } else if (textFromFile && (len.toInt() > 20000)) { errTxt("err: The text file size is too large. it cannot exceed 20,000 bytes or 10,000 chars.\n"); - term(); } else { @@ -429,9 +425,10 @@ void SetEmailTemplate::procBin(const SharedObjs *sharedObjs, const QByteArray &b bodyText.clear(); - emit enableMoreInput(true); - emit dataToClient(toTEXT("-to_host"), GEN_FILE); - emit dataToClient(QByteArray(), GEN_FILE); + flags |= MORE_INPUT; + + emit procOut(toTEXT("-to_host"), GEN_FILE); + emit procOut(QByteArray(), GEN_FILE); } else { @@ -518,10 +515,10 @@ void SetEmailTemplate::proc() if (execQuery) db.exec(); } - term(); + flags &= ~MORE_INPUT; } -void PreviewEmail::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void PreviewEmail::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { @@ -565,11 +562,12 @@ void PreviewEmail::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn db.addColumn(subjectColumn); db.exec(); + QString uName = rdStringFromBlock(userName, BLKSIZE_USER_NAME); QString subject = db.getData(subjectColumn).toString(); QString body = db.getData(bodyColumn).toString(); body.replace(DATE_SUB, date); - body.replace(USERNAME_SUB, *sharedObjs->userName); + body.replace(USERNAME_SUB, uName); body.replace(codeSub, code); QString txt; diff --git a/src/commands/acct_recovery.h b/src/commands/acct_recovery.h index 2aa0194..fa3008f 100644 --- a/src/commands/acct_recovery.h +++ b/src/commands/acct_recovery.h @@ -18,6 +18,7 @@ // . #include "../common.h" +#include "../cmd_object.h" enum TemplateType { @@ -26,31 +27,32 @@ enum TemplateType NONE }; -class RecoverAcct : public InternCommand +bool expired(const QByteArray &uId); +void delRecoverPw(const QByteArray &uId); + +class RecoverAcct : public CmdObject { Q_OBJECT private: - QString uName; - bool inputOk; + QByteArray uId; + bool inputOk; - void delRecoverPw(); - void addToThreshold(const SharedObjs *sharedObjs); + void addToThreshold(); public: static QString cmdName(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit RecoverAcct(QObject *parent = nullptr); }; //--------------- -class ResetPwRequest : public InternCommand +class ResetPwRequest : public CmdObject { Q_OBJECT @@ -58,34 +60,34 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, uchar dType); explicit ResetPwRequest(QObject *parent = nullptr); }; //---------------- -class VerifyEmail : public InternCommand +class VerifyEmail : public CmdObject { Q_OBJECT private: - QString code; + QString code; + QByteArray uId; public: static QString cmdName(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit VerifyEmail(QObject *parent = nullptr); }; //---------------- -class IsEmailVerified : public InternCommand +class IsEmailVerified : public CmdObject { Q_OBJECT @@ -93,14 +95,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit IsEmailVerified(QObject *parent = nullptr); }; //------------------ -class SetEmailTemplate : public InternCommand +class SetEmailTemplate : public CmdObject { Q_OBJECT @@ -119,16 +121,14 @@ public: static QString cmdName(); - bool handlesGenfile(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit SetEmailTemplate(QObject *parent = nullptr); }; //----------------- -class PreviewEmail : public InternCommand +class PreviewEmail : public CmdObject { Q_OBJECT @@ -136,7 +136,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit PreviewEmail(QObject *parent = nullptr); }; diff --git a/src/commands/admin.cpp b/src/commands/admin.cpp index 58a55e6..e653fde 100644 --- a/src/commands/admin.cpp +++ b/src/commands/admin.cpp @@ -16,32 +16,31 @@ // along with MRCI under the LICENSE.md file. If not, see // . -CloseHost::CloseHost(QObject *parent) : InternCommand(parent) {} -RestartHost::RestartHost(QObject *parent) : InternCommand(parent) {} -ServSettings::ServSettings(QObject *parent) : InternCommand(parent) {} +CloseHost::CloseHost(QObject *parent) : CmdObject(parent) {} +RestartHost::RestartHost(QObject *parent) : CmdObject(parent) {} +ServSettings::ServSettings(QObject *parent) : CmdObject(parent) {} QString CloseHost::cmdName() {return "close_host";} QString RestartHost::cmdName() {return "restart_host";} QString ServSettings::cmdName() {return "host_config";} -void CloseHost::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void CloseHost::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs); - if (dType == TEXT) { - if (moreInputEnabled()) + if (flags & MORE_INPUT) { QString input = fromTEXT(binIn); if (input == "CLOSE") { - emit enableMoreInput(false); - emit backendDataOut(ASYNC_EXIT, QByteArray(), PRIV_IPC); + flags &= ~MORE_INPUT; + + async(ASYNC_EXIT, PRIV_IPC); } else if (input.isEmpty()) { - emit enableMoreInput(false); + flags &= ~MORE_INPUT; } else { @@ -51,31 +50,30 @@ void CloseHost::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, u } else { - emit enableMoreInput(true); + flags |= MORE_INPUT; mainTxt("You are about to shutdown the host instance, type: 'CLOSE' to proceed or leave blank to cancel: "); } } } -void RestartHost::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void RestartHost::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs); - if (dType == TEXT) { - if (moreInputEnabled()) + if (flags & MORE_INPUT) { QString input = fromTEXT(binIn); if (input == "RESTART") { - emit enableMoreInput(false); - emit backendDataOut(ASYNC_RESTART, QByteArray(), PRIV_IPC); + flags &= ~MORE_INPUT; + + async(ASYNC_RESTART, PRIV_IPC); } else if (input.isEmpty()) { - emit enableMoreInput(false); + flags &= ~MORE_INPUT; } else if (!input.isEmpty()) { @@ -85,21 +83,13 @@ void RestartHost::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } else { - emit enableMoreInput(true); + flags |= MORE_INPUT; mainTxt("You are about to re-start the host instance, type: 'RESTART' to proceed or leave blank to cancel: "); } } } -void ServSettings::term() -{ - emit enableMoreInput(false); - - level = 0; - select = 0; -} - void ServSettings::printSettings() { Query db(this); @@ -109,10 +99,7 @@ void ServSettings::printSettings() db.addColumn(COLUMN_BAN_LIMIT); db.addColumn(COLUMN_LOCK_LIMIT); db.addColumn(COLUMN_MAXSESSIONS); - db.addColumn(COLUMN_ZIPBIN); - db.addColumn(COLUMN_ZIPCOMPRESS); - db.addColumn(COLUMN_ZIPEXTRACT); - db.addColumn(COLUMN_INITGROUP); + db.addColumn(COLUMN_INITRANK); db.addColumn(COLUMN_MAILERBIN); db.addColumn(COLUMN_MAIL_SEND); db.addColumn(COLUMN_ENABLE_CONFIRM); @@ -137,12 +124,8 @@ void ServSettings::printSettings() txtOut << "Autoban Threshold: " << db.getData(COLUMN_BAN_LIMIT).toUInt() << endl; txtOut << "Autolock Threshold: " << db.getData(COLUMN_LOCK_LIMIT).toUInt() << endl; txtOut << "Maximum Sub-Channels: " << db.getData(COLUMN_MAX_SUB_CH).toUInt() << endl; - txtOut << "Initial Group: " << db.getData(COLUMN_INITGROUP).toString() << endl; + txtOut << "Initial Host Rank: " << db.getData(COLUMN_INITRANK).toUInt() << endl; txtOut << "Database Path: " << sqlDataPath() << endl; - txtOut << "Modules Install Path: " << modDataPath() << endl; - txtOut << "Archiver Executable: " << db.getData(COLUMN_ZIPBIN).toString() << endl; - txtOut << "Archiver Compress Command: " << db.getData(COLUMN_ZIPCOMPRESS).toString() << endl; - txtOut << "Archiver Extract Command: " << db.getData(COLUMN_ZIPEXTRACT).toString() << endl; txtOut << "Mailer Executable: " << db.getData(COLUMN_MAILERBIN).toString() << endl; txtOut << "Mailer Command: " << db.getData(COLUMN_MAIL_SEND).toString() << endl << endl; @@ -158,12 +141,10 @@ void ServSettings::printOptions() txtOut << "[01] Autoban Threshold [02] Autolock Threshold" << endl; txtOut << "[03] Max Sessions [04] Public Registration" << endl; - txtOut << "[05] Initial Group [06] Archiver Exe" << endl; - txtOut << "[07] Compress Command [08] Extract Command" << endl; - txtOut << "[09] Mailer Exe [10] Mailer Command" << endl; - txtOut << "[11] Password Resets [12] Email Verify" << endl; - txtOut << "[13] Active Update [14] Max Sub-Channels" << endl; - txtOut << "[00] Exit" << endl << endl; + txtOut << "[05] Initial Rank [06] Mailer Exe" << endl; + txtOut << "[07] Mailer Command [08] Password Resets" << endl; + txtOut << "[09] Email Verify [10] Active Update" << endl; + txtOut << "[11] Max Sub-Channels [00] Exit" << endl << endl; txtOut << "Select an option: "; level = 1; @@ -182,13 +163,11 @@ void ServSettings::returnToStart() printOptions(); } -void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ServSettings::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs); - if (dType == TEXT) { - if (moreInputEnabled()) + if (flags & MORE_INPUT) { if (level == 1) { @@ -212,7 +191,7 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn else if ((select == 2) && ok) { txtOut << "" << endl; - txtOut << "The autolock threshold is an integar value that determines how many" << endl; + txtOut << "The autolock threshold is an integer value that determines how many" << endl; txtOut << "failed login attempts can be made before the user account is locked" << endl; txtOut << "by the host." << endl << endl; txtOut << "note: the " << ROOT_USER << " user never gets locked. instead, the offenders are blocked" << endl; @@ -244,48 +223,15 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn } else if ((select == 5) && ok) { - txtOut << "" << endl; - txtOut << "The initial group is the group any new user rergistered using the" << endl; - txtOut << "new_user command is attached to. the group must already exists and" << endl; - txtOut << "will not be allowed to get deleted as long as it is the initial" << endl; - txtOut << "group." << endl << endl; - txtOut << "Enter a new group (leave blank to cancel): "; + txtOut << "" << endl; + txtOut << "The initial host rank is the rank all new user accounts are registered" << endl; + txtOut << "with when created. the host rank itself is an integer value that" << endl; + txtOut << "determine what commands each user can or cannot run." << endl << endl; + txtOut << "Enter a new value (leave blank to cancel): "; level = 2; } else if ((select == 6) && ok) - { - txtOut << "" << endl; - txtOut << "This is the path to zip archiver's executable file that the" << endl; - txtOut << "host can call when it needs to extract or create archive files" << endl; - txtOut << "like .zip, .tar, etc.." << endl << endl; - txtOut << "Enter a new path (leave blank to cancel): "; - - level = 2; - } - else if ((select == 7) && ok) - { - txtOut << "" << endl; - txtOut << "This is the command line the host will use when calling the archiver" << endl; - txtOut << "to create a zip file. it must contain the keywords " << OUTPUT_DIR_SUB << endl; - txtOut << "and " << INPUT_DIR_SUB << ". the host will substitute these keywords for" << endl; - txtOut << "for the actual input/output directories when calling the command." << endl << endl; - txtOut << "Enter a new command line (leave blank to cancel): "; - - level = 2; - } - else if ((select == 8) && ok) - { - txtOut << "" << endl; - txtOut << "This is the command line the host will use when calling the archiver" << endl; - txtOut << "to extract a zip file. it must contain the keywords " << OUTPUT_DIR_SUB << endl; - txtOut << "and " << INPUT_DIR_SUB << ". the host will substitute these keywords for" << endl; - txtOut << "for the actual input/output directories when calling the command." << endl << endl; - txtOut << "Enter a new command line (leave blank to cancel): "; - - level = 2; - } - else if ((select == 9) && ok) { txtOut << "" << endl; txtOut << "This is the path to the command line email client executable" << endl; @@ -296,7 +242,7 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn level = 2; } - else if ((select == 10) && ok) + else if ((select == 7) && ok) { txtOut << "" << endl; txtOut << "This is the command line that will be used with the email client" << endl; @@ -308,7 +254,7 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn level = 2; } - else if ((select == 11) && ok) + else if ((select == 8) && ok) { txtOut << "" << endl; txtOut << "This enables automated password resets via email so users can" << endl; @@ -321,7 +267,7 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn level = 2; } - else if ((select == 12) && ok) + else if ((select == 9) && ok) { txtOut << "" << endl; txtOut << "This enables automated email confirmations. this tells the" << endl; @@ -332,7 +278,7 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn level = 2; } - else if ((select == 13) && ok) + else if ((select == 10) && ok) { txtOut << "" << endl; txtOut << "This option tells the host if all sub-channels should be considered" << endl; @@ -347,7 +293,7 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn level = 2; } - else if ((select == 14) && ok) + else if ((select == 11) && ok) { txtOut << "" << endl; txtOut << "This option sets the maximum amount of sub-channels each channel can" << endl; @@ -358,7 +304,7 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn } else if ((select == 0) && ok) { - term(); + flags &= ~MORE_INPUT; } else { @@ -378,10 +324,10 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn } else { - if ((select == 1) || (select == 2) || (select == 3)) + if ((select == 1) || (select == 2) || (select == 3) || (select == 5)) { - bool ok; - uint num = value.toUInt(&ok, 10); + bool ok; + quint32 num = value.toUInt(&ok, 10); if (!ok) { @@ -401,19 +347,20 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn if (select == 1) db.addColumn(COLUMN_BAN_LIMIT, num); else if (select == 2) db.addColumn(COLUMN_LOCK_LIMIT, num); - else db.addColumn(COLUMN_MAXSESSIONS, num); + else if (select == 3) db.addColumn(COLUMN_MAXSESSIONS, num); + else db.addColumn(COLUMN_INITRANK, num); db.exec(); - if (select == 5) + if (select == 3) { - emit backendDataOut(ASYNC_MAXSES, wrInt(num, 32), PRIV_IPC); + async(ASYNC_MAXSES, PRIV_IPC, wrInt(num, BLKSIZE_HOST_LOAD * 8)); } returnToStart(); } } - else if ((select == 4) || (select == 11) || (select == 12) || (select == 13)) + else if ((select == 4) || (select == 8) || (select == 9) || (select == 10)) { if (!isBool(value)) { @@ -424,10 +371,10 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn { QString column; - if (select == 4) column = COLUMN_PUB_USERS; - else if (select == 11) column = COLUMN_ENABLE_PW_RESET; - else if (select == 12) column = COLUMN_ENABLE_CONFIRM; - else column = COLUMN_ACTIVE_UPDATE; + if (select == 4) column = COLUMN_PUB_USERS; + else if (select == 8) column = COLUMN_ENABLE_PW_RESET; + else if (select == 9) column = COLUMN_ENABLE_CONFIRM; + else column = COLUMN_ACTIVE_UPDATE; Query db(this); @@ -438,30 +385,7 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn returnToStart(); } } - else if (select == 5) - { - if (!validGroupName(value)) - { - errTxt("err: Invalid group name.\n"); - mainTxt("Enter a new group (leave blank to cancel): "); - } - else if (!groupExists(value)) - { - errTxt("err: '" + value + "' does not exists.\n"); - mainTxt("Enter a new group (leave blank to cancel): "); - } - else - { - Query db(this); - - db.setType(Query::UPDATE, TABLE_SERV_SETTINGS); - db.addColumn(COLUMN_INITGROUP, value); - db.exec(); - - returnToStart(); - } - } - else if ((select == 6) || (select == 9)) + else if (select == 6) { if (!QFile::exists(expandEnvVariables(value))) { @@ -472,51 +396,14 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn { Query db(this); - QString column; - - if (select == 6) column = COLUMN_ZIPBIN; - else column = COLUMN_MAILERBIN; - db.setType(Query::UPDATE, TABLE_SERV_SETTINGS); - db.addColumn(column, value); + db.addColumn(COLUMN_MAILERBIN, value); db.exec(); returnToStart(); } } - else if ((select == 7) || (select == 8)) - { - if (!value.contains(INPUT_DIR_SUB, Qt::CaseInsensitive)) - { - errTxt("err: The '" + QString(INPUT_DIR_SUB) + "' keyword is missing.\n"); - mainTxt("Enter a new command line (leave blank to cancel): "); - } - else if (!value.contains(OUTPUT_DIR_SUB, Qt::CaseInsensitive)) - { - errTxt("err: The '" + QString(OUTPUT_DIR_SUB) + "' keyword is missing.\n"); - mainTxt("Enter a new command line (leave blank to cancel): "); - } - else - { - Query db(this); - - db.setType(Query::UPDATE, TABLE_SERV_SETTINGS); - - if (select == 7) - { - db.addColumn(COLUMN_ZIPCOMPRESS, value); - } - else - { - db.addColumn(COLUMN_ZIPEXTRACT, value); - } - - db.exec(); - - returnToStart(); - } - } - else if (select == 10) + else if (select == 7) { if (!value.contains(SUBJECT_SUB, Qt::CaseInsensitive)) { @@ -544,7 +431,7 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn returnToStart(); } } - else if (select == 14) + else if (select == 11) { if (!isInt(value)) { @@ -572,7 +459,9 @@ void ServSettings::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn } else { - emit enableMoreInput(true); + select = 0; + level = 0; + flags |= MORE_INPUT; printSettings(); printOptions(); diff --git a/src/commands/admin.h b/src/commands/admin.h index 30be75a..7a2a9c4 100644 --- a/src/commands/admin.h +++ b/src/commands/admin.h @@ -18,8 +18,9 @@ // . #include "../common.h" +#include "../cmd_object.h" -class CloseHost : public InternCommand +class CloseHost : public CmdObject { Q_OBJECT @@ -27,14 +28,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit CloseHost(QObject *parent = nullptr); }; //-------------------------------------- -class RestartHost : public InternCommand +class RestartHost : public CmdObject { Q_OBJECT @@ -42,14 +43,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit RestartHost(QObject *parent = nullptr); }; //-------------------------------------- -class ServSettings : public InternCommand +class ServSettings : public CmdObject { Q_OBJECT @@ -66,8 +67,7 @@ public: static QString cmdName(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit ServSettings(QObject *parent = nullptr); }; diff --git a/src/commands/auth.cpp b/src/commands/auth.cpp index 5f78ee4..f00ce0a 100644 --- a/src/commands/auth.cpp +++ b/src/commands/auth.cpp @@ -16,40 +16,31 @@ // along with MRCI under the LICENSE.md file. If not, see // . -Auth::Auth(QObject *parent) : InternCommand(parent) {} +Auth::Auth(QObject *parent) : CmdObject(parent) {} AuthLog::AuthLog(QObject *parent) : TableViewer(parent) { - setParams(TABLE_AUTH_LOG, QStringList() << COLUMN_TIME - << COLUMN_IPADDR - << COLUMN_USERNAME - << COLUMN_AUTH_ATTEMPT - << COLUMN_RECOVER_ATTEMPT - << COLUMN_COUNT - << COLUMN_ACCEPTED, true); + setParams(TABLE_AUTH_LOG, false); + addJointColumn(TABLE_USERS, COLUMN_USER_ID); + addTableColumn(TABLE_AUTH_LOG, COLUMN_TIME); + addTableColumn(TABLE_AUTH_LOG, COLUMN_IPADDR); + addTableColumn(TABLE_USERS, COLUMN_USERNAME); + addTableColumn(TABLE_AUTH_LOG, COLUMN_AUTH_ATTEMPT); + addTableColumn(TABLE_AUTH_LOG, COLUMN_RECOVER_ATTEMPT); + addTableColumn(TABLE_AUTH_LOG, COLUMN_COUNT); + addTableColumn(TABLE_AUTH_LOG, COLUMN_ACCEPTED); } QString Auth::cmdName() {return "auth";} QString AuthLog::cmdName() {return "ls_auth_log";} -void Auth::term() -{ - emit enableMoreInput(false); - - newPassword = false; - newUserName = false; - loginOk = false; - - uName.clear(); -} - -void Auth::addToThreshold(const SharedObjs *sharedObjs) +void Auth::addToThreshold() { Query db(this); db.setType(Query::PUSH, TABLE_AUTH_LOG); - db.addColumn(COLUMN_USERNAME, uName); - db.addColumn(COLUMN_IPADDR, *sharedObjs->sessionAddr); + db.addColumn(COLUMN_USER_ID, uId); + db.addColumn(COLUMN_IPADDR, ip); db.addColumn(COLUMN_AUTH_ATTEMPT, true); db.addColumn(COLUMN_RECOVER_ATTEMPT, false); db.addColumn(COLUMN_COUNT, true); @@ -58,8 +49,8 @@ void Auth::addToThreshold(const SharedObjs *sharedObjs) db.setType(Query::PULL, TABLE_SERV_SETTINGS); - uint maxAttempts = 0; - bool isRoot = false; + quint32 maxAttempts = 0; + bool isRoot = false; if (noCaseMatch(ROOT_USER, uName)) { @@ -80,37 +71,38 @@ void Auth::addToThreshold(const SharedObjs *sharedObjs) db.setType(Query::PULL, TABLE_AUTH_LOG); db.addColumn(COLUMN_IPADDR); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.addCondition(COLUMN_AUTH_ATTEMPT, true); db.addCondition(COLUMN_COUNT, true); db.addCondition(COLUMN_ACCEPTED, false); - if (isRoot) db.addCondition(COLUMN_IPADDR, *sharedObjs->sessionAddr); + if (isRoot) db.addCondition(COLUMN_IPADDR, ip); db.exec(); - if (static_cast(db.rows()) > maxAttempts) + if (static_cast(db.rows()) > maxAttempts) { if (isRoot) { - if (!QHostAddress(*sharedObjs->sessionAddr).isLoopback()) + if (!QHostAddress(ip).isLoopback()) { db.setType(Query::PUSH, TABLE_IPBANS); - db.addColumn(COLUMN_IPADDR, *sharedObjs->sessionAddr); + db.addColumn(COLUMN_IPADDR, ip); db.exec(); - emit closeSession(); + async(ASYNC_UPDATE_BANS, PRIV_IPC); + async(ASYNC_END_SESSION, PRIV_IPC); } } else { db.setType(Query::UPDATE, TABLE_USERS); db.addColumn(COLUMN_LOCKED, true); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); } - term(); + flags &= ~MORE_INPUT; } else { @@ -119,48 +111,43 @@ void Auth::addToThreshold(const SharedObjs *sharedObjs) } } -void Auth::confirmAuth(const SharedObjs *sharedObjs) +void Auth::confirmAuth() { - *rwSharedObjs->userName = uName; - *rwSharedObjs->displayName = dName; - *rwSharedObjs->userId = uId; - *rwSharedObjs->groupName = getUserGroup(uName); - *rwSharedObjs->hostRank = getRankForGroup(*sharedObjs->groupName); - Query db(this); db.setType(Query::UPDATE, TABLE_AUTH_LOG); db.addColumn(COLUMN_COUNT, false); db.addCondition(COLUMN_COUNT, true); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.addCondition(COLUMN_AUTH_ATTEMPT, true); if (noCaseMatch(ROOT_USER, uName)) { - db.addCondition(COLUMN_IPADDR, *sharedObjs->sessionAddr); + db.addCondition(COLUMN_IPADDR, ip); } db.exec(); db.setType(Query::PUSH, TABLE_AUTH_LOG); - db.addColumn(COLUMN_USERNAME, uName); - db.addColumn(COLUMN_IPADDR, *sharedObjs->sessionAddr); + db.addColumn(COLUMN_USER_ID, uId); + db.addColumn(COLUMN_IPADDR, ip); db.addColumn(COLUMN_COUNT, false); db.addColumn(COLUMN_ACCEPTED, true); db.addColumn(COLUMN_AUTH_ATTEMPT, true); db.addColumn(COLUMN_RECOVER_ATTEMPT, false); db.exec(); - mainTxt("Access granted.\n"); + flags &= ~MORE_INPUT; - emit authOk(); + async(ASYNC_USER_LOGIN, PRIV_IPC, uId); + mainTxt("Access granted.\n"); } -void Auth::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ +void Auth::procIn(const QByteArray &binIn, quint8 dType) +{ if (dType == TEXT) { - if (moreInputEnabled()) + if (flags & MORE_INPUT) { QString text = fromTEXT(binIn); @@ -171,7 +158,8 @@ void Auth::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar if (text.isEmpty()) { mainTxt("\n"); - term(); + + flags &= ~MORE_INPUT; } else if (!validPassword(text)) { @@ -180,13 +168,13 @@ void Auth::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar } else { - updatePassword(uName, text, TABLE_USERS); + updatePassword(uId, text, TABLE_USERS); Query db(this); db.setType(Query::UPDATE, TABLE_USERS); db.addColumn(COLUMN_NEED_PASS, false); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); newPassword = false; @@ -197,8 +185,7 @@ void Auth::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar } else { - confirmAuth(sharedObjs); - term(); + confirmAuth(); } } } @@ -207,7 +194,8 @@ void Auth::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar if (text.isEmpty()) { mainTxt("\n"); - term(); + + flags &= ~MORE_INPUT; } else if (!validUserName(text)) { @@ -226,31 +214,31 @@ void Auth::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar db.setType(Query::UPDATE, TABLE_USERS); db.addColumn(COLUMN_NEED_NAME, false); db.addColumn(COLUMN_USERNAME, text); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); - emit backendDataOut(ASYNC_USER_RENAMED, toTEXT("-old '" + escapeChars(uName, '\\', '\'') + "' -new '" + escapeChars(text, '\\', '\'') + "'"), PUB_IPC); + async(ASYNC_USER_RENAMED, PUB_IPC, uId + fixedToTEXT(text, BLKSIZE_USER_NAME)); uName = text; newUserName = false; - confirmAuth(sharedObjs); - term(); + confirmAuth(); } } } else if (text.isEmpty()) { mainTxt("\n"); - term(); + + flags &= ~MORE_INPUT; } else if (!validPassword(text)) { - addToThreshold(sharedObjs); + addToThreshold(); } - else if (!auth(uName, text, TABLE_USERS)) + else if (!auth(uId, text, TABLE_USERS)) { - addToThreshold(sharedObjs); + addToThreshold(); } else { @@ -266,8 +254,7 @@ void Auth::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar } else { - confirmAuth(sharedObjs); - term(); + confirmAuth(); } } } @@ -277,40 +264,40 @@ void Auth::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar QString email = getParam("-email", args); QString name = getParam("-user", args); - if (!email.isEmpty() && validEmailAddr(email)) name = getUserNameForEmail(email); + if (!email.isEmpty() && validEmailAddr(email)) + { + name = getUserNameForEmail(email); + } if (name.isEmpty() || !validUserName(name)) { errTxt("err: The -user or -email argument is empty, not found or invalid.\n"); } - else if (!userExists(name)) + else if (!userExists(name, &uId)) { errTxt("err: No such user.\n"); } - else if (isLocked(name)) + else if (isLocked(uId)) { errTxt("err: The requested user account is locked.\n"); } else { - emit enableMoreInput(true); - - uName = name; - Query db(this); db.setType(Query::PULL, TABLE_USERS); - db.addColumn(COLUMN_DISPLAY_NAME); db.addColumn(COLUMN_NEED_NAME); db.addColumn(COLUMN_NEED_PASS); db.addColumn(COLUMN_USER_ID); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); + loginOk = false; + uName = name; + flags |= MORE_INPUT; + ip = rdStringFromBlock(clientIp, BLKSIZE_CLIENT_IP); newPassword = db.getData(COLUMN_NEED_PASS).toBool(); newUserName = db.getData(COLUMN_NEED_NAME).toBool(); - dName = db.getData(COLUMN_DISPLAY_NAME).toString(); - uId = db.getData(COLUMN_USER_ID).toByteArray(); privTxt("Enter password (leave blank to cancel): "); } diff --git a/src/commands/auth.h b/src/commands/auth.h index 9920d93..8259880 100644 --- a/src/commands/auth.h +++ b/src/commands/auth.h @@ -18,9 +18,10 @@ // . #include "../common.h" +#include "../cmd_object.h" #include "table_viewer.h" -class Auth : public InternCommand +class Auth : public CmdObject { Q_OBJECT @@ -28,20 +29,19 @@ private: QByteArray uId; QString uName; - QString dName; + QString ip; bool loginOk; bool newPassword; bool newUserName; - void confirmAuth(const SharedObjs *sharedObjs); - void addToThreshold(const SharedObjs *sharedObjs); + void confirmAuth(); + void addToThreshold(); public: static QString cmdName(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit Auth(QObject *parent = nullptr); }; diff --git a/src/commands/bans.cpp b/src/commands/bans.cpp index 3253580..7faa2d0 100644 --- a/src/commands/bans.cpp +++ b/src/commands/bans.cpp @@ -18,20 +18,25 @@ ListBans::ListBans(QObject *parent) : TableViewer(parent) { - setParams(TABLE_IPBANS, QStringList() << COLUMN_TIME << COLUMN_IPADDR, true); + setParams(TABLE_IPBANS, true); + addTableColumn(TABLE_IPBANS, COLUMN_TIME); + addTableColumn(TABLE_IPBANS, COLUMN_IPADDR); } -BanIP::BanIP(QObject *parent) : InternCommand(parent) {} -UnBanIP::UnBanIP(QObject *parent) : InternCommand(parent) {} +BanIP::BanIP(QObject *parent) : CmdObject(parent) {} +UnBanIP::UnBanIP(QObject *parent) : CmdObject(parent) {} QString ListBans::cmdName() {return "ls_bans";} QString BanIP::cmdName() {return "add_ban";} QString UnBanIP::cmdName() {return "rm_ban";} -void BanIP::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ListBans::onDel() { - Q_UNUSED(sharedObjs); + async(ASYNC_UPDATE_BANS, PRIV_IPC); +} +void BanIP::procIn(const QByteArray &binIn, quint8 dType) +{ if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); @@ -54,14 +59,14 @@ void BanIP::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar db.setType(Query::PUSH, TABLE_IPBANS); db.addColumn(COLUMN_IPADDR, addr.toString()); db.exec(); + + async(ASYNC_UPDATE_BANS, PRIV_IPC); } } } -void UnBanIP::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void UnBanIP::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs); - if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); @@ -84,6 +89,8 @@ void UnBanIP::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uch db.setType(Query::DEL, TABLE_IPBANS); db.addCondition(COLUMN_IPADDR, addr.toString()); db.exec(); + + async(ASYNC_UPDATE_BANS, PRIV_IPC); } } } diff --git a/src/commands/bans.h b/src/commands/bans.h index 069fbbb..96b9e27 100644 --- a/src/commands/bans.h +++ b/src/commands/bans.h @@ -18,12 +18,17 @@ // . #include "../common.h" +#include "../cmd_object.h" #include "table_viewer.h" class ListBans : public TableViewer { Q_OBJECT +private: + + void onDel(); + public: static QString cmdName(); @@ -33,7 +38,7 @@ public: //--------------------------------- -class BanIP : public InternCommand +class BanIP : public CmdObject { Q_OBJECT @@ -41,14 +46,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit BanIP(QObject *parent = nullptr); }; //-------------------------------- -class UnBanIP : public InternCommand +class UnBanIP : public CmdObject { Q_OBJECT @@ -56,7 +61,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit UnBanIP(QObject *parent = nullptr); }; diff --git a/src/commands/cast.cpp b/src/commands/cast.cpp index 8edcda9..9e8b6c4 100644 --- a/src/commands/cast.cpp +++ b/src/commands/cast.cpp @@ -16,17 +16,22 @@ // along with MRCI under the LICENSE.md file. If not, see // . -Cast::Cast(QObject *parent) : InternCommand(parent) {} -OpenSubChannel::OpenSubChannel(QObject *parent) : InternCommand(parent) {} -CloseSubChannel::CloseSubChannel(QObject *parent) : InternCommand(parent) {} -LsOpenChannels::LsOpenChannels(QObject *parent) : InternCommand(parent) {} -PingPeers::PingPeers(QObject *parent) : InternCommand(parent) {} -AddRDOnlyFlag::AddRDOnlyFlag(QObject *parent) : InternCommand(parent) {} -RemoveRDOnlyFlag::RemoveRDOnlyFlag(QObject *parent) : InternCommand(parent) {} +Cast::Cast(QObject *parent) : CmdObject(parent) {} +OpenSubChannel::OpenSubChannel(QObject *parent) : CmdObject(parent) {} +CloseSubChannel::CloseSubChannel(QObject *parent) : CmdObject(parent) {} +LsOpenChannels::LsOpenChannels(QObject *parent) : CmdObject(parent) {} +PingPeers::PingPeers(QObject *parent) : CmdObject(parent) {} +AddRDOnlyFlag::AddRDOnlyFlag(QObject *parent) : CmdObject(parent) {} +RemoveRDOnlyFlag::RemoveRDOnlyFlag(QObject *parent) : CmdObject(parent) {} ListRDonlyFlags::ListRDonlyFlags(QObject *parent) : TableViewer(parent) { - setParams(TABLE_RDONLY_CAST, QStringList() << COLUMN_SUB_CH_ID << COLUMN_ACCESS_LEVEL << COLUMN_CHANNEL_NAME, false); + setParams(TABLE_RDONLY_CAST, false); + addJointColumn(TABLE_CHANNELS, COLUMN_CHANNEL_ID); + addTableColumn(TABLE_CHANNELS, COLUMN_CHANNEL_NAME); + addTableColumn(TABLE_RDONLY_CAST, COLUMN_CHANNEL_ID); + addTableColumn(TABLE_RDONLY_CAST, COLUMN_SUB_CH_ID); + addTableColumn(TABLE_RDONLY_CAST, COLUMN_ACCESS_LEVEL); } QString Cast::cmdName() {return "cast";} @@ -38,22 +43,48 @@ QString AddRDOnlyFlag::cmdName() {return "add_rdonly_flag";} QString RemoveRDOnlyFlag::cmdName() {return "rm_rdonly_flag";} QString ListRDonlyFlags::cmdName() {return "ls_rdonly_flags";} -void Cast::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +bool canOpenSubChannel(const QByteArray &uId, const char *override, quint64 chId, quint8 subId) { - Q_UNUSED(sharedObjs); + int uLevel = channelAccessLevel(uId, override, chId); + int sLevel = lowestAcessLevel(chId, subId); - emit castToPeers(binIn, dType); + return uLevel <= sLevel; } -void OpenSubChannel::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +int lowestAcessLevel(quint64 chId, quint8 subId) { - Q_UNUSED(sharedObjs); + int ret = 5000; + Query db; + + db.setType(Query::PULL, TABLE_SUB_CHANNELS); + db.addColumn(COLUMN_LOWEST_LEVEL); + db.addCondition(COLUMN_CHANNEL_ID, chId); + db.addCondition(COLUMN_SUB_CH_ID, subId); + db.exec(); + + if (db.rows()) + { + ret = db.getData(COLUMN_LOWEST_LEVEL).toInt(); + } + + return ret; +} + +void Cast::procIn(const QByteArray &binIn, quint8 dType) +{ + async(ASYNC_CAST, dType, binIn); +} + +void OpenSubChannel::procIn(const QByteArray &binIn, quint8 dType) +{ if (dType == TEXT) { QStringList args = parseArgs(binIn, 4); QString ch = getParam("-ch_name", args); QString sub = getParam("-sub_name", args); + quint64 chId; + quint8 subId; if (ch.isEmpty()) { @@ -63,22 +94,34 @@ void OpenSubChannel::procBin(const SharedObjs *sharedObjs, const QByteArray &bin { errTxt("err: Sub-Channel name (-sub_name) argument not found or is empty.\n"); } + else if (!channelExists(ch, &chId)) + { + errTxt("err: The requested channel does not exists.\n"); + } + else if (!channelSubExists(chId, sub, &subId)) + { + errTxt("err: The requested sub-channel does not exists.\n"); + } + else if (!canOpenSubChannel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId, subId)) + { + errTxt("err: Access denied.\n"); + } else { - emit openChByName(ch, sub); + async(ASYNC_OPEN_SUBCH, PRIV_IPC, wrInt(chId, 64) + wrInt(subId, 8)); } } } -void CloseSubChannel::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void CloseSubChannel::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs); - if (dType == TEXT) { QStringList args = parseArgs(binIn, 4); QString ch = getParam("-ch_name", args); QString sub = getParam("-sub_name", args); + quint64 chId; + quint8 subId; if (ch.isEmpty()) { @@ -88,16 +131,24 @@ void CloseSubChannel::procBin(const SharedObjs *sharedObjs, const QByteArray &bi { errTxt("err: Sub-Channel name (-sub_name) argument not found or is empty.\n"); } + else if (!channelExists(ch, &chId)) + { + errTxt("err: The requested channel does not exists.\n"); + } + else if (!channelSubExists(chId, sub, &subId)) + { + errTxt("err: The requested sub-channel does not exists.\n"); + } else { - emit closeChByName(ch, sub); + async(ASYNC_CLOSE_SUBCH, PRIV_IPC, wrInt(chId, 64) + wrInt(subId, 8)); } } } -void LsOpenChannels::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void LsOpenChannels::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(binIn); + Q_UNUSED(binIn) if (dType == TEXT) { @@ -115,29 +166,29 @@ void LsOpenChannels::procBin(const SharedObjs *sharedObjs, const QByteArray &bin tableData.append(QStringList() << COLUMN_CHANNEL_NAME << COLUMN_SUB_CH_NAME << COLUMN_CHANNEL_ID << COLUMN_SUB_CH_ID << "read_only"); tableData.append(separators); - for (int i = 0; i < sharedObjs->chIds->size(); i += 9) + for (int i = 0; i < MAX_OPEN_SUB_CHANNELS; i += BLKSIZE_SUB_CHANNEL) { - quint64 chId = rdInt(QByteArray::fromRawData(sharedObjs->chIds->data() + i, 8)); - quint64 subId = rdInt(QByteArray::fromRawData(sharedObjs->chIds->data() + (i + 8), 1)); + quint64 chId = rd64BitFromBlock(openSubChs + i); + quint8 subId = rd8BitFromBlock(openSubChs + (i + 8)); if (chId) { QStringList columnData; - db.setType(Query::PULL, TABLE_SUB_CHANNELS); - db.addColumn(COLUMN_SUB_CH_NAME); - db.addColumn(COLUMN_CHANNEL_NAME); + db.setType(Query::INNER_JOIN_PULL, TABLE_SUB_CHANNELS); + db.addTableColumn(TABLE_SUB_CHANNELS, COLUMN_SUB_CH_NAME); + db.addTableColumn(TABLE_CHANNELS, COLUMN_CHANNEL_NAME); + db.addJoinCondition(COLUMN_CHANNEL_ID, TABLE_CHANNELS); db.addCondition(COLUMN_CHANNEL_ID, chId); db.addCondition(COLUMN_SUB_CH_ID, subId); db.exec(); - QByteArray subCh = QByteArray::fromRawData(sharedObjs->chIds->data() + i, 9); - QString chName = db.getData(COLUMN_CHANNEL_NAME).toString(); - QString subName = db.getData(COLUMN_SUB_CH_NAME).toString(); - QString rdOnly; + QString chName = db.getData(COLUMN_CHANNEL_NAME).toString(); + QString subName = db.getData(COLUMN_SUB_CH_NAME).toString(); + QString rdOnly; - if (chPos(subCh, *sharedObjs->wrAbleChIds) != -1) - { + if (posOfBlock(openSubChs + i, openWritableSubChs, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL) == -1) + { rdOnly = "1"; } else @@ -166,7 +217,7 @@ void LsOpenChannels::procBin(const SharedObjs *sharedObjs, const QByteArray &bin { for (int i = 0; i < row.size(); ++i) { - mainTxt(row[i].leftJustified(justLens[i] + 2, ' ')); + mainTxt(row[i].leftJustified(justLens[i] + 4, ' ')); } mainTxt("\n"); @@ -174,27 +225,24 @@ void LsOpenChannels::procBin(const SharedObjs *sharedObjs, const QByteArray &bin } } -void PingPeers::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void PingPeers::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(binIn); + Q_UNUSED(binIn) if (dType == TEXT) { - if (!(*sharedObjs->activeUpdate)) + if (rd8BitFromBlock(activeUpdate) == 0) { errTxt("err: You don't currently have any active update sub-channels open. sending a ping request is pointless because peers won't be able to respond.\n"); } else { - QByteArray castHeader = *sharedObjs->chIds + wrInt(PING_PEERS, 8); - QByteArray data = toPEER_INFO(sharedObjs); - - emit backendDataOut(ASYNC_LIMITED_CAST, castHeader + data, PUB_IPC); + async(ASYNC_PING_PEERS, PUB_IPC); } } } -void AddRDOnlyFlag::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void AddRDOnlyFlag::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { @@ -202,6 +250,7 @@ void AddRDOnlyFlag::procBin(const SharedObjs *sharedObjs, const QByteArray &binI QString chName = getParam("-ch_name", args); QString subId = getParam("-sub_id", args); QString level = getParam("-level", args); + quint64 chId; if (chName.isEmpty()) { @@ -227,34 +276,36 @@ void AddRDOnlyFlag::procBin(const SharedObjs *sharedObjs, const QByteArray &binI { errTxt("err: Invalid privilage level. valid range (1-5).\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } - else if (channelAccessLevel(sharedObjs, chName) > ADMIN) + else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > ADMIN) { errTxt("err: Access denied.\n"); } - else if (rdOnlyFlagExists(chName, static_cast(subId.toInt()), level.toInt())) + else if (rdOnlyFlagExists(chName, static_cast(subId.toInt()), level.toInt())) { - errTxt("err: A read only flag for sub-id: " + QString::number(subId.toInt()) + " level: " + QString::number(level.toInt()) + " already exists.\n"); + errTxt("err: A read only flag for sub_id: " + QString::number(subId.toInt()) + " level: " + QString::number(level.toInt()) + " already exists.\n"); } else - { + { + QByteArray frame = wrInt(chId, 64) + wrInt(subId.toUInt(), 8) + wrInt(level.toUInt(), 8); + Query db(this); db.setType(Query::PUSH, TABLE_RDONLY_CAST); - db.addColumn(COLUMN_CHANNEL_NAME, chName); + db.addColumn(COLUMN_CHANNEL_ID, chId); db.addColumn(COLUMN_SUB_CH_ID, subId.toInt()); db.addColumn(COLUMN_ACCESS_LEVEL, level.toInt()); db.exec(); - emit backendDataOut(ASYNC_ADD_RDONLY, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_ADD_RDONLY, PUB_IPC_WITH_FEEDBACK, frame); } } } -void RemoveRDOnlyFlag::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void RemoveRDOnlyFlag::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { @@ -262,6 +313,7 @@ void RemoveRDOnlyFlag::procBin(const SharedObjs *sharedObjs, const QByteArray &b QString chName = getParam("-ch_name", args); QString subId = getParam("-sub_id", args); QString level = getParam("-level", args); + quint64 chId; if (chName.isEmpty()) { @@ -287,45 +339,48 @@ void RemoveRDOnlyFlag::procBin(const SharedObjs *sharedObjs, const QByteArray &b { errTxt("err: Invalid privilage level. valid range (1-5).\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } - else if (channelAccessLevel(sharedObjs, chName) > ADMIN) + else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > ADMIN) { errTxt("err: Access denied.\n"); } - else if (!rdOnlyFlagExists(chName, static_cast(subId.toInt()), level.toInt())) + else if (!rdOnlyFlagExists(chName, static_cast(subId.toInt()), level.toInt())) { - errTxt("err: A read only flag for sub-id: " + QString::number(subId.toInt()) + " level: " + QString::number(level.toInt()) + " does not exists.\n"); + errTxt("err: A read only flag for sub_id: " + QString::number(subId.toInt()) + " level: " + QString::number(level.toInt()) + " does not exists.\n"); } else { + QByteArray frame = wrInt(chId, 64) + wrInt(subId.toUInt(), 8) + wrInt(level.toUInt(), 8); + Query db(this); db.setType(Query::DEL, TABLE_RDONLY_CAST); - db.addCondition(COLUMN_CHANNEL_NAME, chName); + db.addCondition(COLUMN_CHANNEL_ID, chId); db.addCondition(COLUMN_SUB_CH_ID, subId.toInt()); db.addCondition(COLUMN_ACCESS_LEVEL, level.toInt()); db.exec(); - emit backendDataOut(ASYNC_RM_RDONLY, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_RM_RDONLY, PUB_IPC_WITH_FEEDBACK, frame); } } } -void ListRDonlyFlags::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ListRDonlyFlags::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { - if (moreInputEnabled()) + if (flags & MORE_INPUT) { - TableViewer::procBin(sharedObjs, binIn, dType); + TableViewer::procIn(binIn, dType); } else { QStringList args = parseArgs(binIn, 2); QString chName = getParam("-ch_name", args); + quint64 chId; if (chName.isEmpty()) { @@ -335,19 +390,19 @@ void ListRDonlyFlags::procBin(const SharedObjs *sharedObjs, const QByteArray &bi { errTxt("err: Invalid channel name.\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } else { - if (channelAccessLevel(sharedObjs, chName) > REGULAR) + if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > REGULAR) { - TableViewer::procBin(sharedObjs, toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName + " -" + QString(COLUMN_LOWEST_LEVEL) + " " + QString::number(PUBLIC)), dType); + TableViewer::procIn(toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName + " -" + QString(COLUMN_LOWEST_LEVEL) + " " + QString::number(PUBLIC)), dType); } else { - TableViewer::procBin(sharedObjs, toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName), dType); + TableViewer::procIn(toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName), dType); } } } diff --git a/src/commands/cast.h b/src/commands/cast.h index 4f579fc..68df38e 100644 --- a/src/commands/cast.h +++ b/src/commands/cast.h @@ -18,9 +18,13 @@ // . #include "../common.h" +#include "../cmd_object.h" #include "table_viewer.h" -class Cast : public InternCommand +bool canOpenSubChannel(const QByteArray &uId, const char *override, quint64 chId, quint8 subId); +int lowestAcessLevel(quint64 chId, quint8 subId); + +class Cast : public CmdObject { Q_OBJECT @@ -28,14 +32,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit Cast(QObject *parent = nullptr); }; //---------------------------------- -class OpenSubChannel : public InternCommand +class OpenSubChannel : public CmdObject { Q_OBJECT @@ -43,14 +47,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit OpenSubChannel(QObject *parent = nullptr); }; //----------------------------------- -class CloseSubChannel : public InternCommand +class CloseSubChannel : public CmdObject { Q_OBJECT @@ -58,14 +62,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit CloseSubChannel(QObject *parent = nullptr); }; //---------------------------------- -class LsOpenChannels : public InternCommand +class LsOpenChannels : public CmdObject { Q_OBJECT @@ -73,14 +77,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit LsOpenChannels(QObject *parent = nullptr); }; //---------------------------------- -class PingPeers : public InternCommand +class PingPeers : public CmdObject { Q_OBJECT @@ -88,14 +92,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit PingPeers(QObject *parent = nullptr); }; //--------------------------------- -class AddRDOnlyFlag : public InternCommand +class AddRDOnlyFlag : public CmdObject { Q_OBJECT @@ -103,14 +107,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit AddRDOnlyFlag(QObject *parent = nullptr); }; //------------------------------- -class RemoveRDOnlyFlag : public InternCommand +class RemoveRDOnlyFlag : public CmdObject { Q_OBJECT @@ -118,7 +122,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit RemoveRDOnlyFlag(QObject *parent = nullptr); }; @@ -133,7 +137,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit ListRDonlyFlags(QObject *parent = nullptr); }; diff --git a/src/commands/certs.cpp b/src/commands/certs.cpp index 4751891..39f5955 100644 --- a/src/commands/certs.cpp +++ b/src/commands/certs.cpp @@ -18,22 +18,21 @@ ListCerts::ListCerts(QObject *parent) : TableViewer(parent) { - setParams(TABLE_CERT_DATA, QStringList() << COLUMN_COMMON_NAME, false); + setParams(TABLE_CERT_DATA, false); + addTableColumn(TABLE_CERT_DATA, COLUMN_COMMON_NAME); } -CertInfo::CertInfo(QObject *parent) : InternCommand(parent) {} -AddCert::AddCert(QObject *parent) : InternCommand(parent) {} -RemoveCert::RemoveCert(QObject *parent) : InternCommand(parent) {} +CertInfo::CertInfo(QObject *parent) : CmdObject(parent) {} +AddCert::AddCert(QObject *parent) : CmdObject(parent) {} +RemoveCert::RemoveCert(QObject *parent) : CmdObject(parent) {} QString ListCerts::cmdName() {return "ls_certs";} QString CertInfo::cmdName() {return "cert_info";} QString AddCert::cmdName() {return "add_cert";} QString RemoveCert::cmdName() {return "rm_cert";} -void CertInfo::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void CertInfo::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs); - if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); @@ -75,15 +74,6 @@ void CertInfo::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uc } } -void AddCert::term() -{ - coName.clear(); - certBa.clear(); - privBa.clear(); - - emit enableMoreInput(false); -} - void AddCert::run() { Query db(this); @@ -94,27 +84,25 @@ void AddCert::run() db.addColumn(COLUMN_PRIV_KEY, privBa); db.exec(); - term(); + flags &= ~MORE_INPUT; } void AddCert::ask() { - emit enableMoreInput(true); + flags |= MORE_INPUT; mainTxt("Common name: '" + coName + "' already exists. do you want to replace it? (y/n): "); } -void AddCert::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void AddCert::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs); - - if ((dType == TEXT) && moreInputEnabled()) + if ((dType == TEXT) && (flags & MORE_INPUT)) { QString ans = fromTEXT(binIn); if (noCaseMatch("n", ans)) { - term(); + flags &= ~MORE_INPUT; } else if (noCaseMatch("y", ans)) { @@ -159,7 +147,7 @@ void AddCert::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uch } else if (!validCommonName(coName)) { - errTxt("err: The common name must be less than or equal to 200 chars long and contain no spaces.\n"); + errTxt("err: The common name must be less than or equal to 136 chars long and contain no spaces.\n"); } else if (!certFile.open(QFile::ReadOnly)) { @@ -210,34 +198,25 @@ void RemoveCert::run() db.addCondition(COLUMN_COMMON_NAME, coName); db.exec(); - term(); -} - -void RemoveCert::term() -{ - emit enableMoreInput(false); - - coName.clear(); + flags &= ~MORE_INPUT; } void RemoveCert::ask() { - emit enableMoreInput(true); + flags |= MORE_INPUT; mainTxt("Are you sure you want to remove the cert for common name: " + coName + "? (y/n): "); } -void RemoveCert::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void RemoveCert::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs); - - if ((dType == TEXT) && moreInputEnabled()) + if ((dType == TEXT) && (flags & MORE_INPUT)) { QString ans = fromTEXT(binIn); if (noCaseMatch("n", ans)) { - term(); + flags &= ~MORE_INPUT; } else if (noCaseMatch("y", ans)) { @@ -260,7 +239,7 @@ void RemoveCert::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } else if (!validCommonName(name)) { - errTxt("err: The common name must be lass than or equal to 200 chars long and contain no spaces.\n"); + errTxt("err: Invalid common name.\n"); } else if (!certExists(name)) { diff --git a/src/commands/certs.h b/src/commands/certs.h index c703361..1e14392 100644 --- a/src/commands/certs.h +++ b/src/commands/certs.h @@ -18,6 +18,7 @@ // . #include "../common.h" +#include "../cmd_object.h" #include "../make_cert.h" #include "table_viewer.h" @@ -34,7 +35,7 @@ public: //-------------------------- -class CertInfo : public InternCommand +class CertInfo : public CmdObject { Q_OBJECT @@ -42,14 +43,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit CertInfo(QObject *parent = nullptr); }; //---------------------------- -class AddCert : public InternCommand +class AddCert : public CmdObject { Q_OBJECT @@ -67,15 +68,14 @@ public: static QString cmdName(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit AddCert(QObject *parent = nullptr); }; //----------------------------- -class RemoveCert : public InternCommand +class RemoveCert : public CmdObject { Q_OBJECT @@ -90,8 +90,7 @@ public: static QString cmdName(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit RemoveCert(QObject *parent = nullptr); }; diff --git a/src/commands/channels.cpp b/src/commands/channels.cpp index 83f93d1..a455ed2 100644 --- a/src/commands/channels.cpp +++ b/src/commands/channels.cpp @@ -16,39 +16,57 @@ // along with MRCI under the LICENSE.md file. If not, see // . -CreateChannel::CreateChannel(QObject *parent) : InternCommand(parent) {} -RemoveChannel::RemoveChannel(QObject *parent) : InternCommand(parent) {} -RenameChannel::RenameChannel(QObject *parent) : InternCommand(parent) {} -SetActiveState::SetActiveState(QObject *parent) : InternCommand(parent) {} -CreateSubCh::CreateSubCh(QObject *parent) : InternCommand(parent) {} -RemoveSubCh::RemoveSubCh(QObject *parent) : InternCommand(parent) {} -RenameSubCh::RenameSubCh(QObject *parent) : InternCommand(parent) {} -InviteToCh::InviteToCh(QObject *parent) : InternCommand(parent) {} -DeclineChInvite::DeclineChInvite(QObject *parent) : InternCommand(parent) {} -AcceptChInvite::AcceptChInvite(QObject *parent) : InternCommand(parent) {} -RemoveChMember::RemoveChMember(QObject *parent) : InternCommand(parent) {} -SetMemberLevel::SetMemberLevel(QObject *parent) : InternCommand(parent) {} -SetSubAcessLevel::SetSubAcessLevel(QObject *parent) : InternCommand(parent) {} -OwnerOverride::OwnerOverride(QObject *parent) : InternCommand(parent) {} +CreateChannel::CreateChannel(QObject *parent) : CmdObject(parent) {} +RemoveChannel::RemoveChannel(QObject *parent) : CmdObject(parent) {} +RenameChannel::RenameChannel(QObject *parent) : CmdObject(parent) {} +SetActiveState::SetActiveState(QObject *parent) : CmdObject(parent) {} +CreateSubCh::CreateSubCh(QObject *parent) : CmdObject(parent) {} +RemoveSubCh::RemoveSubCh(QObject *parent) : CmdObject(parent) {} +RenameSubCh::RenameSubCh(QObject *parent) : CmdObject(parent) {} +InviteToCh::InviteToCh(QObject *parent) : CmdObject(parent) {} +DeclineChInvite::DeclineChInvite(QObject *parent) : CmdObject(parent) {} +AcceptChInvite::AcceptChInvite(QObject *parent) : CmdObject(parent) {} +RemoveChMember::RemoveChMember(QObject *parent) : CmdObject(parent) {} +SetMemberLevel::SetMemberLevel(QObject *parent) : CmdObject(parent) {} +SetSubAcessLevel::SetSubAcessLevel(QObject *parent) : CmdObject(parent) {} +OwnerOverride::OwnerOverride(QObject *parent) : CmdObject(parent) {} ListChannels::ListChannels(QObject *parent) : TableViewer(parent) { - setParams(TABLE_CH_MEMBERS, QStringList() << COLUMN_CHANNEL_ID << COLUMN_CHANNEL_NAME << COLUMN_PENDING_INVITE << COLUMN_ACCESS_LEVEL << COLUMN_USERNAME, false); + setParams(TABLE_CH_MEMBERS, false); + addTableColumn(TABLE_CH_MEMBERS, COLUMN_CHANNEL_ID); + addTableColumn(TABLE_CHANNELS, COLUMN_CHANNEL_NAME); + addTableColumn(TABLE_CH_MEMBERS, COLUMN_PENDING_INVITE); + addTableColumn(TABLE_CH_MEMBERS, COLUMN_ACCESS_LEVEL); + addJointColumn(TABLE_CHANNELS, COLUMN_CHANNEL_ID); + addJointColumn(TABLE_USERS, COLUMN_USER_ID); } ListSubCh::ListSubCh(QObject *parent) : TableViewer(parent) { - setParams(TABLE_SUB_CHANNELS, QStringList() << COLUMN_CHANNEL_NAME << COLUMN_CHANNEL_ID << COLUMN_SUB_CH_ID << COLUMN_SUB_CH_NAME << COLUMN_LOWEST_LEVEL << COLUMN_ACTIVE_UPDATE, false); + setParams(TABLE_SUB_CHANNELS, false); + addTableColumn(TABLE_SUB_CHANNELS, COLUMN_SUB_CH_ID); + addTableColumn(TABLE_SUB_CHANNELS, COLUMN_SUB_CH_NAME); + addTableColumn(TABLE_SUB_CHANNELS, COLUMN_LOWEST_LEVEL); + addTableColumn(TABLE_SUB_CHANNELS, COLUMN_ACTIVE_UPDATE); } SearchChannels::SearchChannels(QObject *parent) : TableViewer(parent) { - setParams(TABLE_CHANNELS, QStringList() << COLUMN_CHANNEL_ID << COLUMN_CHANNEL_NAME, false); + setParams(TABLE_CHANNELS, false); + addTableColumn(TABLE_CHANNELS, COLUMN_CHANNEL_ID); + addTableColumn(TABLE_CHANNELS, COLUMN_CHANNEL_NAME); } ListMembers::ListMembers(QObject *parent) : TableViewer(parent) { - setParams(TABLE_CH_MEMBERS, QStringList() << COLUMN_CHANNEL_ID << COLUMN_CHANNEL_NAME << COLUMN_PENDING_INVITE << COLUMN_ACCESS_LEVEL << COLUMN_USERNAME, false); + setParams(TABLE_CH_MEMBERS, false); + addTableColumn(TABLE_CH_MEMBERS, COLUMN_CHANNEL_ID); + addTableColumn(TABLE_USERS, COLUMN_USERNAME); + addTableColumn(TABLE_USERS, COLUMN_DISPLAY_NAME); + addTableColumn(TABLE_CH_MEMBERS, COLUMN_PENDING_INVITE); + addTableColumn(TABLE_CH_MEMBERS, COLUMN_ACCESS_LEVEL); + addJointColumn(TABLE_USERS, COLUMN_USER_ID); } QString CreateChannel::cmdName() {return "add_ch";} @@ -70,26 +88,51 @@ QString SetSubAcessLevel::cmdName() {return "set_sub_ch_level";} QString ListMembers::cmdName() {return "ls_ch_members";} QString OwnerOverride::cmdName() {return "ch_owner_override";} -void ListChannels::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +QByteArray createChMemberAsyncFrame(quint64 chId, const QByteArray &userId, bool invite, quint8 level, const QString &userName, const QString &dispName, const QString &chName) { - Q_UNUSED(binIn); + QByteArray ret; - if (moreInputEnabled()) + ret.append(wrInt(chId, 64)); + ret.append(userId); + + if (invite) { - TableViewer::procBin(sharedObjs, binIn, dType); + ret.append(wrInt(1, 8)); } else { - TableViewer::procBin(sharedObjs, toTEXT("-" + QString(COLUMN_USERNAME) + " " + *sharedObjs->userName), dType); + ret.append(wrInt(0, 8)); + } + + ret.append(wrInt(level, 8)); + ret.append(nullTermTEXT(userName)); + ret.append(nullTermTEXT(dispName)); + ret.append(nullTermTEXT(chName)); + + return ret; +} + +void ListChannels::procIn(const QByteArray &binIn, quint8 dType) +{ + Q_UNUSED(binIn) + + if (flags & MORE_INPUT) + { + TableViewer::procIn(binIn, dType); + } + else + { + TableViewer::procIn(toTEXT("-" + QString(COLUMN_USERNAME) + " " + rdStringFromBlock(userName, BLKSIZE_USER_NAME)), dType); } } -void ListSubCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ListSubCh::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); QString chName = getParam("-ch_name", args); + quint64 chId; if (chName.isEmpty()) { @@ -99,31 +142,31 @@ void ListSubCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, u { errTxt("err: Invalid channel name.\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } else { - if (channelAccessLevel(sharedObjs, getChId(chName)) > REGULAR) + if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > REGULAR) { - TableViewer::procBin(sharedObjs, toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName + " -" + QString(COLUMN_LOWEST_LEVEL) + " " + QString::number(PUBLIC)), dType); + TableViewer::procIn(toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName + " -" + QString(COLUMN_LOWEST_LEVEL) + " " + QString::number(PUBLIC)), dType); } else { - TableViewer::procBin(sharedObjs, toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName), dType); + TableViewer::procIn(toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + chName), dType); } } } } -void SearchChannels::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void SearchChannels::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { - if (moreInputEnabled()) + if (flags & MORE_INPUT) { - TableViewer::procBin(sharedObjs, binIn, dType); + TableViewer::procIn(binIn, dType); } else { @@ -141,33 +184,35 @@ void SearchChannels::procBin(const SharedObjs *sharedObjs, const QByteArray &bin } else if (!name.isEmpty()) { - TableViewer::procBin(sharedObjs, toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + name), dType); + TableViewer::procIn(toTEXT("-" + QString(COLUMN_CHANNEL_NAME) + " " + name), dType); } else if (!chId.isEmpty()) { - TableViewer::procBin(sharedObjs, toTEXT("-" + QString(COLUMN_CHANNEL_ID) + " " + chId), dType); + TableViewer::procIn(toTEXT("-" + QString(COLUMN_CHANNEL_ID) + " " + chId), dType); } else { - TableViewer::procBin(sharedObjs, QByteArray(), dType); + TableViewer::procIn(QByteArray(), dType); } } } } -void ListMembers::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ListMembers::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { - if (moreInputEnabled()) + if (flags & MORE_INPUT) { - TableViewer::procBin(sharedObjs, binIn, dType); + TableViewer::procIn(binIn, dType); } else { - QStringList args = parseArgs(binIn, 2); + QStringList args = parseArgs(binIn, 6); QString chName = getParam("-ch_name", args); - QString userFind = getParam("-find", args); + QString userFind = getParam("-user_name", args); + QString dispFind = getParam("-disp_name", args); + quint64 chId; if (chName.isEmpty()) { @@ -177,7 +222,11 @@ void ListMembers::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, { errTxt("err: Invalid channel name.\n"); } - else if (channelAccessLevel(sharedObjs, chName) > REGULAR) + else if (!channelExists(chName, &chId)) + { + errTxt("err: Channel name '" + chName + "' does not exists.\n"); + } + else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > REGULAR) { errTxt("err: You are not currently a member of the channel: '" + chName + "'\n"); } @@ -187,21 +236,27 @@ void ListMembers::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, if (!userFind.isEmpty()) { - argsBa.append(" " + toTEXT("-" + QString(COLUMN_USERNAME) + " " + userFind)); + argsBa.append(toTEXT(" -" + QString(COLUMN_USERNAME) + " " + userFind)); } - TableViewer::procBin(sharedObjs, argsBa, dType); + if (!dispFind.isEmpty()) + { + argsBa.append(toTEXT(" -" + QString(COLUMN_DISPLAY_NAME) + " " + dispFind)); + } + + TableViewer::procIn(argsBa, dType); } } } } -void CreateChannel::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void CreateChannel::procIn(const QByteArray &binIn, uchar dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); QString chName = getParam("-ch_name", args); + quint64 chId; if (chName.isEmpty()) { @@ -211,7 +266,7 @@ void CreateChannel::procBin(const SharedObjs *sharedObjs, const QByteArray &binI { errTxt("err: Invalid channel name. it must be between 4-32 chars long and contain no spaces.\n"); } - else if (channelExists(chName)) + else if (channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' already exists.\n"); } @@ -223,34 +278,31 @@ void CreateChannel::procBin(const SharedObjs *sharedObjs, const QByteArray &binI db.addColumn(COLUMN_CHANNEL_NAME, chName); db.exec(); - quint64 chId = getChId(chName); + QByteArray uId = rdFromBlock(userId, BLKSIZE_USER_ID); + QString uName = rdStringFromBlock(userName, BLKSIZE_USER_NAME); + QString dName = rdStringFromBlock(displayName, BLKSIZE_DISP_NAME); db.setType(Query::PUSH, TABLE_CH_MEMBERS); db.addColumn(COLUMN_CHANNEL_ID, chId); - db.addColumn(COLUMN_USERNAME, *sharedObjs->userName); - db.addColumn(COLUMN_CHANNEL_NAME, chName); + db.addColumn(COLUMN_USER_ID, uId); db.addColumn(COLUMN_ACCESS_LEVEL, OWNER); db.addColumn(COLUMN_PENDING_INVITE, false); db.exec(); - args.append("-user"); - args.append("'" + escapeChars(*sharedObjs->userName, '\\', '\'') + "'"); - args.append("-level"); - args.append(QString::number(OWNER)); - args.append("-ch_id"); - args.append(QString::number(chId)); + QByteArray frame = createChMemberAsyncFrame(chId, uId, false, OWNER, uName, dName, chName); - emit backendDataOut(ASYNC_NEW_CH_MEMBER, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_NEW_CH_MEMBER, PUB_IPC_WITH_FEEDBACK, frame); } } } -void RemoveChannel::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void RemoveChannel::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); QString chName = getParam("-ch_name", args); + quint64 chId; if (chName.isEmpty()) { @@ -260,39 +312,35 @@ void RemoveChannel::procBin(const SharedObjs *sharedObjs, const QByteArray &binI { errTxt("err: Invalid channel name.\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } - else if (channelAccessLevel(sharedObjs, chName) != OWNER) + else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) != OWNER) { errTxt("err: Only the channel owner can delete it.\n"); } else { - quint64 id = getChId(chName); - Query db(this); db.setType(Query::DEL, TABLE_CHANNELS); - db.addCondition(COLUMN_CHANNEL_NAME, chName); + db.addCondition(COLUMN_CHANNEL_ID, chId); db.exec(); - args.append("-ch_id"); - args.append(QString::number(id)); - - emit backendDataOut(ASYNC_DEL_CH, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_DEL_CH, PUB_IPC_WITH_FEEDBACK, wrInt(chId, 64)); } } } -void RenameChannel::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void RenameChannel::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 4); QString chName = getParam("-ch_name", args); QString newName = getParam("-new_name", args); + quint64 chId; if (chName.isEmpty()) { @@ -310,11 +358,11 @@ void RenameChannel::procBin(const SharedObjs *sharedObjs, const QByteArray &binI { errTxt("err: Invalid new channel name. it must be between 4-32 chars long and contain no spaces.\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } - else if (channelAccessLevel(sharedObjs, chName) != OWNER) + else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) != OWNER) { errTxt("err: Only the channel owner can rename it.\n"); } @@ -328,15 +376,17 @@ void RenameChannel::procBin(const SharedObjs *sharedObjs, const QByteArray &binI db.setType(Query::UPDATE, TABLE_CHANNELS); db.addColumn(COLUMN_CHANNEL_NAME, newName); - db.addCondition(COLUMN_CHANNEL_NAME, chName); + db.addCondition(COLUMN_CHANNEL_ID, chId); db.exec(); - emit backendDataOut(ASYNC_RENAME_CH, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + QByteArray frame = wrInt(chId, 64) + nullTermTEXT(newName); + + async(ASYNC_RENAME_CH, PUB_IPC_WITH_FEEDBACK, frame); } } } -void SetActiveState::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void SetActiveState::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { @@ -344,6 +394,8 @@ void SetActiveState::procBin(const SharedObjs *sharedObjs, const QByteArray &bin QString chName = getParam("-ch_name", args); QString subName = getParam("-sub_name", args); QString state = getParam("-state", args); + quint64 chId; + quint8 subId; if (chName.isEmpty()) { @@ -369,15 +421,15 @@ void SetActiveState::procBin(const SharedObjs *sharedObjs, const QByteArray &bin { errTxt("err: '" + state + "' is not a valid boolean value. it must be 0 or 1.\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } - else if (channelAccessLevel(sharedObjs, chName) > ADMIN) + else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > ADMIN) { errTxt("err: Access denied.\n"); } - else if (!channelSubExists(chName, subName)) + else if (!channelSubExists(chId, subName, &subId)) { errTxt("err: Sub-channel name '" + chName + "' does not exists.\n"); } @@ -387,8 +439,8 @@ void SetActiveState::procBin(const SharedObjs *sharedObjs, const QByteArray &bin db.setType(Query::UPDATE, TABLE_SUB_CHANNELS); db.addColumn(COLUMN_ACTIVE_UPDATE, static_cast(state.toInt())); - db.addCondition(COLUMN_CHANNEL_NAME, chName); - db.addCondition(COLUMN_SUB_CH_NAME, subName); + db.addCondition(COLUMN_CHANNEL_ID, chId); + db.addCondition(COLUMN_SUB_CH_ID, subId); db.exec(); if (globalActiveFlag()) @@ -397,20 +449,21 @@ void SetActiveState::procBin(const SharedObjs *sharedObjs, const QByteArray &bin } else { - emit backendDataOut(ASYNC_CH_ACT_FLAG, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_CH_ACT_FLAG, PUB_IPC_WITH_FEEDBACK, wrInt(chId, 64) + wrInt(subId, 8)); } } } } -void CreateSubCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void CreateSubCh::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 4); QString chName = getParam("-ch_name", args); QString subName = getParam("-sub_name", args); - int subId = 0; + quint64 chId; + quint8 subId; if (chName.isEmpty()) { @@ -428,56 +481,50 @@ void CreateSubCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, { errTxt("err: Invalid sub-channel name. it must be between 4-32 chars long and contain no spaces.\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } - else if (channelAccessLevel(sharedObjs, chName) > ADMIN) + else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > ADMIN) { errTxt("err: Access denied.\n"); } - else if (channelSubExists(chName, subName)) + else if (channelSubExists(chId, subName)) { errTxt("err: Sub-channel name '" + subName + "' already exists.\n"); } - else if (!genSubId(chName, &subId)) + else if (!genSubId(chId, &subId)) { errTxt("err: This channel has reached the maximum amount sub-channels it can have (" + QString::number(maxSubChannels()) + ").\n"); } else { - quint64 chId = getChId(chName); - Query db(this); db.setType(Query::PUSH, TABLE_SUB_CHANNELS); db.addColumn(COLUMN_SUB_CH_NAME, subName); db.addColumn(COLUMN_SUB_CH_ID, subId); db.addColumn(COLUMN_LOWEST_LEVEL, REGULAR); - db.addColumn(COLUMN_CHANNEL_NAME, chName); db.addColumn(COLUMN_CHANNEL_ID, chId); db.addColumn(COLUMN_ACTIVE_UPDATE, false); db.exec(); - args.append("-ch_id"); - args.append(QString::number(chId)); - args.append("-level"); - args.append(QString::number(REGULAR)); - args.append("-sub_id"); - args.append(QString::number(subId)); + QByteArray frame = wrInt(chId, 64) + wrInt(subId, 8) + wrInt(REGULAR, 8) + wrInt(0, 8) + nullTermTEXT(subName); - emit backendDataOut(ASYNC_NEW_SUB_CH, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_NEW_SUB_CH, PUB_IPC_WITH_FEEDBACK, frame); } } } -void RemoveSubCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void RemoveSubCh::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 4); QString chName = getParam("-ch_name", args); QString subName = getParam("-sub_name", args); + quint64 chId; + quint8 subId; if (chName.isEmpty()) { @@ -495,37 +542,33 @@ void RemoveSubCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, { errTxt("err: Invalid sub-channel name.\n"); } - else if (channelAccessLevel(sharedObjs, chName) > ADMIN) + else if (!channelExists(chName, &chId)) + { + errTxt("err: Channel name '" + chName + "' does not exists.\n"); + } + else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > ADMIN) { errTxt("err: Access denied.\n"); } - else if (!channelSubExists(chName, subName)) + else if (!channelSubExists(chId, subName, &subId)) { errTxt("err: Sub-channel name '" + subName + "' does not exists.\n"); } else { - quint64 chId = getChId(chName); - uchar subId = getSubId(chName, subName); - Query db(this); db.setType(Query::DEL, TABLE_SUB_CHANNELS); - db.addCondition(COLUMN_CHANNEL_NAME, chName); - db.addCondition(COLUMN_SUB_CH_NAME, subName); + db.addCondition(COLUMN_CHANNEL_ID, chId); + db.addCondition(COLUMN_SUB_CH_ID, subId); db.exec(); - args.append("-ch_id"); - args.append(QString::number(chId)); - args.append("-sub_id"); - args.append(QString::number(subId)); - - emit backendDataOut(ASYNC_RM_SUB_CH, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_RM_SUB_CH, PUB_IPC_WITH_FEEDBACK, wrInt(chId, 64) + wrInt(subId, 8)); } } } -void RenameSubCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void RenameSubCh::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { @@ -533,6 +576,8 @@ void RenameSubCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, QString chName = getParam("-ch_name", args); QString subName = getParam("-sub_name", args); QString newName = getParam("-new_name", args); + quint64 chId; + quint8 subId; if (chName.isEmpty()) { @@ -558,11 +603,15 @@ void RenameSubCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, { errTxt("err: Invalid new sub-channel name. it must be between 4-32 chars long and contain no spaces.\n"); } - else if (channelAccessLevel(sharedObjs, chName) > ADMIN) + else if (!channelExists(chName, &chId)) + { + errTxt("err: Channel name '" + chName + "' does not exists.\n"); + } + else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > ADMIN) { errTxt("err: Access denied.\n"); } - else if (!channelSubExists(chName, subName)) + else if (!channelSubExists(chId, subName, &subId)) { errTxt("err: Sub-channel name '" + subName + "' does not exists.\n"); } @@ -572,22 +621,26 @@ void RenameSubCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, db.setType(Query::UPDATE, TABLE_SUB_CHANNELS); db.addColumn(COLUMN_SUB_CH_NAME, newName); - db.addCondition(COLUMN_SUB_CH_NAME, subName); - db.addCondition(COLUMN_CHANNEL_NAME, chName); + db.addCondition(COLUMN_CHANNEL_ID, chId); + db.addCondition(COLUMN_SUB_CH_ID, subId); db.exec(); - emit backendDataOut(ASYNC_RENAME_SUB_CH, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + QByteArray frame = wrInt(chId, 64) + wrInt(subId, 8) + nullTermTEXT(newName); + + async(ASYNC_RENAME_SUB_CH, PUB_IPC_WITH_FEEDBACK, frame); } } } -void InviteToCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void InviteToCh::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 4); QString chName = getParam("-ch_name", args); QString uName = getParam("-user", args); + QByteArray uId; + quint64 chId; if (chName.isEmpty()) { @@ -605,23 +658,23 @@ void InviteToCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, { errTxt("err: Invalid user name.\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } - else if (!userExists(uName)) + else if (!userExists(uName, &uId)) { errTxt("err: User name '" + uName + "' does not exists.\n"); } - else if (channelAccessLevel(sharedObjs, chName) > OFFICER) + else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > OFFICER) { errTxt("err: Access denied.\n"); } - else if (inviteExists(uName, chName)) + else if (inviteExists(uId, chId)) { errTxt("err: User name '" + uName + "' already has an invitation to channel '" + chName + ".'\n"); } - else if (channelAccessLevel(uName, chName) < PUBLIC) + else if (channelAccessLevel(uId, chId) < PUBLIC) { errTxt("err: User name '" + uName + "' is already a member of the requested channel '" + chName + ".'\n"); } @@ -630,34 +683,36 @@ void InviteToCh::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, Query db(this); db.setType(Query::PUSH, TABLE_CH_MEMBERS); - db.addColumn(COLUMN_USERNAME, uName); - db.addColumn(COLUMN_CHANNEL_NAME, chName); - db.addColumn(COLUMN_CHANNEL_ID, getChId(chName)); + db.addColumn(COLUMN_USER_ID, uId); + db.addColumn(COLUMN_CHANNEL_ID, chId); db.addColumn(COLUMN_PENDING_INVITE, true); db.addColumn(COLUMN_ACCESS_LEVEL, REGULAR); db.exec(); - emit backendDataOut(ASYNC_INVITED_TO_CH, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + QByteArray frame = createChMemberAsyncFrame(chId, uId, true, REGULAR, uName, getDispName(uId), chName); + + async(ASYNC_INVITED_TO_CH, PUB_IPC_WITH_FEEDBACK, frame); } } } -void DeclineChInvite::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void DeclineChInvite::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); QString chName = getParam("-ch_name", args); + quint64 chId; if (chName.isEmpty()) { errTxt("err: The channel name (-ch_name) argument was not found or is empty.\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } - else if (!inviteExists(*sharedObjs->userName, chName)) + else if (!inviteExists(rdFromBlock(userId, BLKSIZE_USER_ID), chId)) { errTxt("err: You don't currently have an invitation to channel '" + chName + ".'\n"); } @@ -666,36 +721,32 @@ void DeclineChInvite::procBin(const SharedObjs *sharedObjs, const QByteArray &bi Query db(this); db.setType(Query::DEL, TABLE_CH_MEMBERS); - db.addCondition(COLUMN_CHANNEL_NAME, chName); - db.addCondition(COLUMN_USERNAME, *sharedObjs->userName); + db.addCondition(COLUMN_CHANNEL_ID, chId); + db.addCondition(COLUMN_USER_ID, rdFromBlock(userId, BLKSIZE_USER_ID)); db.exec(); - args.append("-ch_id"); - args.append(QString::number(getChId(chName))); - args.append("-user"); - args.append("'" + escapeChars(*sharedObjs->userName, '\\', '\'') + "'"); - - emit backendDataOut(ASYNC_RM_CH_MEMBER, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_RM_CH_MEMBER, PUB_IPC_WITH_FEEDBACK, wrInt(chId, 64) + rdFromBlock(userId, BLKSIZE_USER_ID)); } } } -void AcceptChInvite::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void AcceptChInvite::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); QString chName = getParam("-ch_name", args); + quint64 chId; if (chName.isEmpty()) { errTxt("err: The channel name (-ch_name) argument was not found or is empty.\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } - else if (!inviteExists(*sharedObjs->userName, chName)) + else if (!inviteExists(rdFromBlock(userId, BLKSIZE_USER_ID), chId)) { errTxt("err: You don't currently have an invitation to channel '" + chName + ".'\n"); } @@ -705,25 +756,52 @@ void AcceptChInvite::procBin(const SharedObjs *sharedObjs, const QByteArray &bin db.setType(Query::UPDATE, TABLE_CH_MEMBERS); db.addColumn(COLUMN_PENDING_INVITE, false); - db.addCondition(COLUMN_CHANNEL_NAME, chName); - db.addCondition(COLUMN_USERNAME, *sharedObjs->userName); + db.addCondition(COLUMN_CHANNEL_ID, chId); + db.addCondition(COLUMN_USER_ID, rdFromBlock(userId, BLKSIZE_USER_ID)); db.exec(); - args.append("-user"); - args.append("'" + escapeChars(*sharedObjs->userName, '\\', '\'') + "'"); - - emit backendDataOut(ASYNC_INVITE_ACCEPTED, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_INVITE_ACCEPTED, PUB_IPC_WITH_FEEDBACK, wrInt(chId, 64) + rdFromBlock(userId, BLKSIZE_USER_ID)); } } } -void RemoveChMember::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +bool RemoveChMember::allowMemberDel(const QByteArray &uId, quint64 chId) +{ + QByteArray myId = rdFromBlock(userId, BLKSIZE_USER_ID); + bool ret = false; + bool leaving = (uId == myId); + int targetLevel = channelAccessLevel(uId, chId); + int myLevel = channelAccessLevel(myId, chOwnerOverride, BLKSIZE_USER_ID); + + if (leaving && (myLevel == OWNER)) + { + errTxt("err: The channel owner cannot leave the channel. please assign a new owner before doing so.\n"); + } + else if (targetLevel == PUBLIC) + { + errTxt("err: The target user is not a member of the channel.\n"); + } + else if (leaving) + { + ret = myLevel != PUBLIC; + } + else + { + ret = myLevel < targetLevel; + } + + return ret; +} + +void RemoveChMember::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 4); QString chName = getParam("-ch_name", args); QString uName = getParam("-user", args); + QByteArray uId; + quint64 chId; if (chName.isEmpty()) { @@ -741,38 +819,41 @@ void RemoveChMember::procBin(const SharedObjs *sharedObjs, const QByteArray &bin { errTxt("err: Invalid user name.\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } - else if (!userExists(uName)) + else if (!userExists(uName, &uId)) { errTxt("err: User name '" + uName + "' does not exists.\n"); } - else if (!allowMemberDel(sharedObjs, uName, chName)) + else if (!allowMemberDel(uId, chId)) { errTxt("err: Access denied.\n"); } else { - quint64 id = getChId(chName); - Query db(this); db.setType(Query::DEL, TABLE_CH_MEMBERS); - db.addCondition(COLUMN_CHANNEL_NAME, chName); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_CHANNEL_ID, chId); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); - args.append("-ch_id"); - args.append(QString::number(id)); - - emit backendDataOut(ASYNC_RM_CH_MEMBER, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_RM_CH_MEMBER, PUB_IPC_WITH_FEEDBACK, wrInt(chId, 64) + uId); } } } -void SetMemberLevel::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +bool SetMemberLevel::allowLevelChange(const QByteArray &uId, int newLevel, quint64 chId) +{ + int targetLevel = channelAccessLevel(uId, chId); + int myLevel = channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, BLKSIZE_USER_ID); + + return (newLevel >= myLevel) && (targetLevel > myLevel); +} + +void SetMemberLevel::procIn(const QByteArray &binIn, uchar dType) { if (dType == TEXT) { @@ -780,6 +861,8 @@ void SetMemberLevel::procBin(const SharedObjs *sharedObjs, const QByteArray &bin QString chName = getParam("-ch_name", args); QString uName = getParam("-user", args); QString level = getParam("-level", args); + quint64 chId; + QByteArray uId; if (chName.isEmpty()) { @@ -805,72 +888,54 @@ void SetMemberLevel::procBin(const SharedObjs *sharedObjs, const QByteArray &bin { errTxt("err: Invalid privilege level. it must be an integer between 1-4.\n"); } - else if (!channelExists(chName)) + else if (!channelExists(chName, &chId)) { errTxt("err: Channel name '" + chName + "' does not exists.\n"); } - else if (!userExists(uName)) + else if (!userExists(uName, &uId)) { errTxt("err: User name '" + uName + "' does not exists.\n"); } - else if (!allowLevelChange(sharedObjs, level.toInt(), chName)) + else if (!allowLevelChange(uId, level.toInt(), chId)) { errTxt("err: Access denied.\n"); } - else if (channelAccessLevel(uName, chName) < PUBLIC) - { - errTxt("err: The target user '" + uName + "' is not a member of the channel.\n"); - } else { - quint64 id = getChId(chName); - Query db(this); db.setType(Query::PULL, TABLE_CH_MEMBERS); - db.addColumn(COLUMN_USERNAME); + db.addColumn(COLUMN_USER_ID); db.addCondition(COLUMN_ACCESS_LEVEL, OWNER); - db.addCondition(COLUMN_CHANNEL_NAME, chName); + db.addCondition(COLUMN_CHANNEL_ID, chId); db.exec(); - QString owner = db.getData(COLUMN_USERNAME).toString(); + int newLevel = level.toInt(); + QByteArray owner = db.getData(COLUMN_USER_ID).toByteArray(); db.setType(Query::UPDATE, TABLE_CH_MEMBERS); - db.addColumn(COLUMN_ACCESS_LEVEL, level.toInt()); - db.addCondition(COLUMN_CHANNEL_NAME, chName); - db.addCondition(COLUMN_USERNAME, uName); + db.addColumn(COLUMN_ACCESS_LEVEL, newLevel); + db.addCondition(COLUMN_CHANNEL_ID, chId); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); - args.append("-ch_id"); - args.append(QString::number(id)); - - emit backendDataOut(ASYNC_MEM_LEVEL_CHANGED, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_MEM_LEVEL_CHANGED, PUB_IPC_WITH_FEEDBACK, wrInt(chId, 64) + uId + wrInt(newLevel, 8)); if (level.toInt() == OWNER) { db.setType(Query::UPDATE, TABLE_CH_MEMBERS); db.addColumn(COLUMN_ACCESS_LEVEL, ADMIN); - db.addCondition(COLUMN_CHANNEL_NAME, chName); - db.addCondition(COLUMN_USERNAME, owner); + db.addCondition(COLUMN_CHANNEL_ID, chId); + db.addCondition(COLUMN_USER_ID, owner); db.exec(); - args.clear(); - args.append("-user"); - args.append("'" + escapeChars(owner, '\\', '\'') + "'"); - args.append("-ch_name"); - args.append("'" + escapeChars(chName, '\\', '\'') + "'"); - args.append("-ch_id"); - args.append(QString::number(id)); - args.append("-level"); - args.append(QString::number(ADMIN)); - - emit backendDataOut(ASYNC_MEM_LEVEL_CHANGED, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_MEM_LEVEL_CHANGED, PUB_IPC_WITH_FEEDBACK, wrInt(chId, 64) + owner + wrInt(ADMIN, 8)); } } } } -void SetSubAcessLevel::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void SetSubAcessLevel::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { @@ -878,6 +943,8 @@ void SetSubAcessLevel::procBin(const SharedObjs *sharedObjs, const QByteArray &b QString chName = getParam("-ch_name", args); QString subName = getParam("-sub_name", args); QString level = getParam("-level", args); + quint64 chId; + quint8 subId; if (chName.isEmpty()) { @@ -903,41 +970,35 @@ void SetSubAcessLevel::procBin(const SharedObjs *sharedObjs, const QByteArray &b { errTxt("err: Invalid privilege level. it must be an integer between 1-5.\n"); } - else if (channelAccessLevel(sharedObjs, chName) > ADMIN) + else if (!channelExists(chName, &chId)) + { + errTxt("err: Channel name '" + chName + "' does not exists.\n"); + } + else if (channelAccessLevel(rdFromBlock(userId, BLKSIZE_USER_ID), chOwnerOverride, chId) > ADMIN) { errTxt("err: Access denied.\n"); } - else if (!channelSubExists(chName, subName)) + else if (!channelSubExists(chId, subName, &subId)) { errTxt("err: Sub-channel name '" + subName + "' does not exists.\n"); } else { - quint64 chId = getChId(chName); - uchar subId = getSubId(chName, subName); - Query db(this); db.setType(Query::UPDATE, TABLE_SUB_CHANNELS); db.addColumn(COLUMN_LOWEST_LEVEL, level.toInt()); - db.addCondition(COLUMN_SUB_CH_NAME, subName); - db.addCondition(COLUMN_CHANNEL_NAME, chName); + db.addCondition(COLUMN_SUB_CH_ID, subId); + db.addCondition(COLUMN_CHANNEL_ID, chId); db.exec(); - args.append("-ch_id"); - args.append(QString::number(chId)); - args.append("-sub_id"); - args.append(QString::number(subId)); - - emit backendDataOut(ASYNC_SUB_CH_LEVEL_CHG, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_SUB_CH_LEVEL_CHG, PUB_IPC_WITH_FEEDBACK, wrInt(chId, 64) + wrInt(subId, 8) + wrInt(level.toInt(), 8)); } } } -void OwnerOverride::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void OwnerOverride::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs); - if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); @@ -953,7 +1014,7 @@ void OwnerOverride::procBin(const SharedObjs *sharedObjs, const QByteArray &binI } else { - *rwSharedObjs->chOwnerOverride = state.toInt(); + wr8BitToBlock(static_cast(state.toUInt()), chOwnerOverride); } } } diff --git a/src/commands/channels.h b/src/commands/channels.h index e77b7e5..f840df7 100644 --- a/src/commands/channels.h +++ b/src/commands/channels.h @@ -18,9 +18,12 @@ // . #include "../common.h" +#include "../cmd_object.h" #include "table_viewer.h" -class CreateChannel : public InternCommand +QByteArray createChMemberAsyncFrame(quint64 chId, const QByteArray &userId, bool invite, quint8 level, const QString &userName, const QString &dispName, const QString &chName); + +class CreateChannel : public CmdObject { Q_OBJECT @@ -28,14 +31,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, uchar dType); explicit CreateChannel(QObject *parent = nullptr); }; //----------------------- -class RemoveChannel : public InternCommand +class RemoveChannel : public CmdObject { Q_OBJECT @@ -43,14 +46,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit RemoveChannel(QObject *parent = nullptr); }; //---------------------- -class RenameChannel : public InternCommand +class RenameChannel : public CmdObject { Q_OBJECT @@ -58,14 +61,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit RenameChannel(QObject *parent = nullptr); }; //------------------------ -class SetActiveState : public InternCommand +class SetActiveState : public CmdObject { Q_OBJECT @@ -73,14 +76,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit SetActiveState(QObject *parent = nullptr); }; //------------------------- -class CreateSubCh : public InternCommand +class CreateSubCh : public CmdObject { Q_OBJECT @@ -88,14 +91,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit CreateSubCh(QObject *parent = nullptr); }; //------------------------- -class RemoveSubCh : public InternCommand +class RemoveSubCh : public CmdObject { Q_OBJECT @@ -103,14 +106,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit RemoveSubCh(QObject *parent = nullptr); }; //-------------------------- -class RenameSubCh : public InternCommand +class RenameSubCh : public CmdObject { Q_OBJECT @@ -118,7 +121,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit RenameSubCh(QObject *parent = nullptr); }; @@ -133,7 +136,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit ListSubCh(QObject *parent = nullptr); }; @@ -148,7 +151,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit ListChannels(QObject *parent = nullptr); }; @@ -163,14 +166,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit SearchChannels(QObject *parent = nullptr); }; //------------------------------ -class InviteToCh : public InternCommand +class InviteToCh : public CmdObject { Q_OBJECT @@ -178,14 +181,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit InviteToCh(QObject *parent = nullptr); }; //------------------------------- -class DeclineChInvite : public InternCommand +class DeclineChInvite : public CmdObject { Q_OBJECT @@ -193,14 +196,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit DeclineChInvite(QObject *parent = nullptr); }; //------------------------------- -class AcceptChInvite : public InternCommand +class AcceptChInvite : public CmdObject { Q_OBJECT @@ -208,44 +211,48 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit AcceptChInvite(QObject *parent = nullptr); }; //------------------------------ -class RemoveChMember : public InternCommand +class RemoveChMember : public CmdObject { Q_OBJECT + bool allowMemberDel(const QByteArray &uId, quint64 chId); + public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit RemoveChMember(QObject *parent = nullptr); }; //------------------------------ -class SetMemberLevel : public InternCommand +class SetMemberLevel : public CmdObject { Q_OBJECT + bool allowLevelChange(const QByteArray &uId, int newLevel, quint64 chId); + public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, uchar dType); explicit SetMemberLevel(QObject *parent = nullptr); }; //------------------------------ -class SetSubAcessLevel : public InternCommand +class SetSubAcessLevel : public CmdObject { Q_OBJECT @@ -253,7 +260,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit SetSubAcessLevel(QObject *parent = nullptr); }; @@ -268,14 +275,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit ListMembers(QObject *parent = nullptr); }; //----------------------------- -class OwnerOverride : public InternCommand +class OwnerOverride : public CmdObject { Q_OBJECT @@ -283,7 +290,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit OwnerOverride(QObject *parent = nullptr); }; diff --git a/src/commands/cmd_ranks.cpp b/src/commands/cmd_ranks.cpp index 1d86b72..aeb8f4a 100644 --- a/src/commands/cmd_ranks.cpp +++ b/src/commands/cmd_ranks.cpp @@ -16,26 +16,42 @@ // along with MRCI under the LICENSE.md file. If not, see // . -LsCmdRanks::LsCmdRanks(QObject *parent) : TableViewer(parent) +bool commandHasRank(const QString &mod, const QString &cmdName) { - setParams(TABLE_CMD_RANKS, QStringList() << COLUMN_COMMAND << COLUMN_HOST_RANK, false); + Query db; + + db.setType(Query::PULL, TABLE_CMD_RANKS); + db.addColumn(COLUMN_COMMAND); + db.addCondition(COLUMN_MOD_MAIN, mod); + db.addCondition(COLUMN_COMMAND, cmdName); + db.exec(); + + return db.rows(); } -AssignCmdRank::AssignCmdRank(QObject *parent) : InternCommand(parent) {} -RemoveCmdRank::RemoveCmdRank(QObject *parent) : InternCommand(parent) {} +LsCmdRanks::LsCmdRanks(QObject *parent) : TableViewer(parent) +{ + setParams(TABLE_CMD_RANKS, false); + + addTableColumn(TABLE_CMD_RANKS, COLUMN_MOD_MAIN); + addTableColumn(TABLE_CMD_RANKS, COLUMN_COMMAND); + addTableColumn(TABLE_CMD_RANKS, COLUMN_HOST_RANK); +} + +AssignCmdRank::AssignCmdRank(QObject *parent) : CmdObject(parent) {} +RemoveCmdRank::RemoveCmdRank(QObject *parent) : CmdObject(parent) {} QString LsCmdRanks::cmdName() {return "ls_ranked_cmds";} QString AssignCmdRank::cmdName() {return "add_ranked_cmd";} QString RemoveCmdRank::cmdName() {return "rm_ranked_cmd";} -void AssignCmdRank::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void AssignCmdRank::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs); - if (dType == TEXT) { - QStringList args = parseArgs(binIn, 4); + QStringList args = parseArgs(binIn, 6); QString cmdName = getParam("-command", args); + QString mod = getParam("-mod", args); QString rank = getParam("-rank", args); if (cmdName.isEmpty()) @@ -48,15 +64,19 @@ void AssignCmdRank::procBin(const SharedObjs *sharedObjs, const QByteArray &binI } else if (!isInt(rank)) { - errTxt("err: The given rank is not a valid unsigned integer.\n"); + errTxt("err: The given rank is not a valid 32bit unsigned integer.\n"); } else if (!validCommandName(cmdName)) { - errTxt("err: Invalid command name. it must be 1-64 chars long and can only contain letters, numbers, '_' or '?'.\n"); + errTxt("err: Invalid command name. it must be 1-64 chars long and contain no spaces.\n"); } - else if (commandHasRank(cmdName)) + else if (!validModPath(mod)) { - errTxt("err: The given command name already has an assigned rank.\n"); + errTxt("err: Invalid module path. it cannot contain any of the following chars '|*:\"?<>'\n"); + } + else if (commandHasRank(mod, cmdName)) + { + errTxt("err: The given mod - command name combo already has an assigned rank.\n"); } else { @@ -64,22 +84,22 @@ void AssignCmdRank::procBin(const SharedObjs *sharedObjs, const QByteArray &binI db.setType(Query::PUSH, TABLE_CMD_RANKS); db.addColumn(COLUMN_COMMAND, cmdName); + db.addColumn(COLUMN_MOD_MAIN, mod); db.addColumn(COLUMN_HOST_RANK, rank.toUInt()); db.exec(); - emit backendDataOut(ASYNC_CMD_RANKS_CHANGED, QByteArray(), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_CMD_RANKS_CHANGED, PUB_IPC_WITH_FEEDBACK); } } } -void RemoveCmdRank::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void RemoveCmdRank::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs); - if (dType == TEXT) { - QStringList args = parseArgs(binIn, 2); + QStringList args = parseArgs(binIn, 4); QString cmdName = getParam("-command", args); + QString mod = getParam("-mod", args); if (cmdName.isEmpty()) { @@ -89,9 +109,13 @@ void RemoveCmdRank::procBin(const SharedObjs *sharedObjs, const QByteArray &binI { errTxt("err: Invalid command name.\n"); } - else if (!commandHasRank(cmdName)) + else if (!validModPath(mod)) { - errTxt("err: The given command name does not have an assigned rank.\n"); + errTxt("err: Invalid module path.\n"); + } + else if (!commandHasRank(mod, cmdName)) + { + errTxt("err: The given mod - command name combo does not have an assigned rank.\n"); } else { @@ -99,9 +123,10 @@ void RemoveCmdRank::procBin(const SharedObjs *sharedObjs, const QByteArray &binI db.setType(Query::DEL, TABLE_CMD_RANKS); db.addCondition(COLUMN_COMMAND, cmdName); + db.addCondition(COLUMN_MOD_MAIN, mod); db.exec(); - emit backendDataOut(ASYNC_CMD_RANKS_CHANGED, QByteArray(), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_CMD_RANKS_CHANGED, PUB_IPC_WITH_FEEDBACK); } } } diff --git a/src/commands/cmd_ranks.h b/src/commands/cmd_ranks.h index a2b5b50..7bd1072 100644 --- a/src/commands/cmd_ranks.h +++ b/src/commands/cmd_ranks.h @@ -18,8 +18,11 @@ // . #include "../common.h" +#include "../cmd_object.h" #include "table_viewer.h" +bool commandHasRank(const QString &mod, const QString &cmdName); + class LsCmdRanks : public TableViewer { Q_OBJECT @@ -33,7 +36,7 @@ public: //------------------------ -class AssignCmdRank : public InternCommand +class AssignCmdRank : public CmdObject { Q_OBJECT @@ -41,14 +44,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit AssignCmdRank(QObject *parent = nullptr); }; //------------------------ -class RemoveCmdRank : public InternCommand +class RemoveCmdRank : public CmdObject { Q_OBJECT @@ -56,7 +59,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit RemoveCmdRank(QObject *parent = nullptr); }; diff --git a/src/commands/cmd_state.cpp b/src/commands/cmd_state.cpp deleted file mode 100644 index 2917fec..0000000 --- a/src/commands/cmd_state.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "cmd_state.h" - -// This file is part of MRCI. - -// MRCI is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// MRCI is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with MRCI under the LICENSE.md file. If not, see -// . - -Term::Term(QObject *parent) : InternCommand(parent) {} -Pause::Pause(QObject *parent) : InternCommand(parent) {} -Resume::Resume(QObject *parent) : InternCommand(parent) {} - -QString Term::cmdName() {return "term";} -QString Pause::cmdName() {return "pause";} -QString Resume::cmdName() {return "resume";} - -void Term::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - if (dType == CMD_ID) - { - if (binIn.isEmpty()) - { - emit termAllCommands(); - } - else - { - auto cmdId = static_cast(rdInt(binIn)); - - if (!sharedObjs->cmdNames->contains(cmdId)) - { - errTxt("err: No such command id: '" + QString::number(cmdId) + "'\n"); - } - else - { - emit termCommandId(cmdId); - } - } - } -} - -void Pause::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - if (dType == CMD_ID) - { - if (binIn.isEmpty()) - { - *rwSharedObjs->pausedCmds = *rwSharedObjs->activeLoopCmds; - } - else - { - auto cmdId = static_cast(rdInt(binIn)); - - if (!sharedObjs->cmdNames->contains(cmdId)) - { - errTxt("err: No such command id: '" + QString::number(cmdId) + "'\n"); - } - else if (!sharedObjs->activeLoopCmds->contains(cmdId)) - { - errTxt("err: The command is not currently in a loop state.\n"); - } - else - { - uniqueAdd(cmdId, *rwSharedObjs->pausedCmds); - } - } - } -} - -void Resume::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - if (dType == CMD_ID) - { - if (binIn.isEmpty()) - { - rwSharedObjs->pausedCmds->clear(); - } - else - { - auto cmdId = static_cast(rdInt(binIn)); - - if (!sharedObjs->cmdNames->contains(cmdId)) - { - errTxt("err: No such command id: '" + QString::number(cmdId) + "'\n"); - } - else if (!sharedObjs->pausedCmds->contains(cmdId)) - { - errTxt("err: The command is not currently in a paused state.\n"); - } - else - { - rwSharedObjs->pausedCmds->removeAll(cmdId); - } - } - } -} diff --git a/src/commands/cmd_state.h b/src/commands/cmd_state.h deleted file mode 100644 index cf79b2b..0000000 --- a/src/commands/cmd_state.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef CMD_STATE_H -#define CMD_STATE_H - -// This file is part of MRCI. - -// MRCI is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// MRCI is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with MRCI under the LICENSE.md file. If not, see -// . - -#include "../common.h" - -class Term : public InternCommand -{ - Q_OBJECT - -public: - - static QString cmdName(); - - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); - - explicit Term(QObject *parent = nullptr); -}; - -//----------------------- - -class Pause : public InternCommand -{ - Q_OBJECT - -public: - - static QString cmdName(); - - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); - - explicit Pause(QObject *parent = nullptr); -}; - -//---------------------- - -class Resume : public InternCommand -{ - Q_OBJECT - -public: - - static QString cmdName(); - - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); - - explicit Resume(QObject *parent = nullptr); -}; - -#endif // CMD_STATE_H diff --git a/src/commands/command.cpp b/src/commands/command.cpp deleted file mode 100644 index aec6ebc..0000000 --- a/src/commands/command.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "command.h" - -// This file is part of MRCI. - -// MRCI is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// MRCI is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with MRCI under the LICENSE.md file. If not, see -// . - -SharedObjs::SharedObjs(QObject *parent) : QObject(parent) -{ - p2pAccepted = nullptr; - p2pPending = nullptr; - chIds = nullptr; - wrAbleChIds = nullptr; - chList = nullptr; - activeUpdate = nullptr; - chOwnerOverride = nullptr; - sessionAddr = nullptr; - userName = nullptr; - groupName = nullptr; - displayName = nullptr; - appName = nullptr; - clientMajor = nullptr; - clientMinor = nullptr; - clientPatch = nullptr; - sessionId = nullptr; - userId = nullptr; - moreInputCmds = nullptr; - activeLoopCmds = nullptr; - pausedCmds = nullptr; - hostRank = nullptr; - cmdNames = nullptr; -} - -bool ExternCommand::errState() -{ - return errSent; -} - -void ExternCommand::mainTxt(const QString &txt) -{ - emit dataToClient(QTextCodec::codecForName(TXT_CODEC)->fromUnicode(txt).mid(2), TEXT); -} - -void ExternCommand::errTxt(const QString &txt) -{ - errSent = true; - - emit dataToClient(QTextCodec::codecForName(TXT_CODEC)->fromUnicode(txt).mid(2), ERR); -} - -void ExternCommand::privTxt(const QString &txt) -{ - emit dataToClient(QTextCodec::codecForName(TXT_CODEC)->fromUnicode(txt).mid(2), PRIV_TEXT); -} - -void ExternCommand::bigTxt(const QString &txt) -{ - emit dataToClient(QTextCodec::codecForName(TXT_CODEC)->fromUnicode(txt).mid(2), BIG_TEXT); -} diff --git a/src/commands/command.h b/src/commands/command.h deleted file mode 100644 index 1d12473..0000000 --- a/src/commands/command.h +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef EXTERN_COMMAND_H -#define EXTERN_COMMAND_H - -// This file is part of MRCI. - -// MRCI is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// MRCI is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with MRCI under the LICENSE.md file. If not, see -// . - -#define TXT_CODEC "UTF-16LE" -#define TXT_CODEC_BITS 16 - -#include -#include -#include - -enum TypeID -{ - GEN_FILE = 30, - TEXT = 31, - ERR = 32, - PRIV_TEXT = 33, - IDLE = 34, - HOST_CERT = 35, - FILE_INFO = 36, - PEER_INFO = 37, - MY_INFO = 38, - PEER_STAT = 39, - P2P_REQUEST = 40, - P2P_CLOSE = 41, - P2P_OPEN = 42, - BYTES = 43, - SESSION_ID = 44, - NEW_CMD = 45, - CMD_ID = 46, - BIG_TEXT = 47 -}; - -enum ChannelMemberLevel -{ - OWNER = 1, - ADMIN = 2, - OFFICER = 3, - REGULAR = 4, - PUBLIC = 5 -}; - -class ExternCommand; - -class SharedObjs : public QObject -{ - Q_OBJECT - -public: - - const QHash *cmdNames; - const QList *chList; - const QList *p2pAccepted; - const QList *p2pPending; - const QList *moreInputCmds; - const QList *activeLoopCmds; - const QList *pausedCmds; - const QString *sessionAddr; - const QString *userName; - const QString *groupName; - const QString *displayName; - const QString *appName; - const ushort *clientMajor; - const ushort *clientMinor; - const ushort *clientPatch; - const QByteArray *chIds; - const QByteArray *wrAbleChIds; - const QByteArray *sessionId; - const QByteArray *userId; - const bool *activeUpdate; - const bool *chOwnerOverride; - const uint *hostRank; - - explicit SharedObjs(QObject *parent = nullptr); -}; - -class ExternCommand : public QObject -{ - Q_OBJECT - -protected: - - void mainTxt(const QString &txt); - void errTxt(const QString &txt); - void privTxt(const QString &txt); - void bigTxt(const QString &txt); - -public: - - explicit ExternCommand(QObject *parent = nullptr) : QObject(parent) {} - - virtual ~ExternCommand() {} - - virtual void procBin(const SharedObjs *, const QByteArray &, uchar) {} - virtual void aboutToDelete() {} - virtual void term() {} - virtual bool errState(); - virtual bool handlesGenfile() {return false;} - virtual QString shortText() {return "";} - virtual QString ioText() {return "";} - virtual QString longText() {return "";} - virtual QString libText() {return "";} - virtual QStringList internRequest() {return QStringList();} - - QHash internCommands; - quint16 cmdId; - bool errSent; - bool inLoopMode; - bool inMoreInputMode; - -signals: - - void dataToClient(const QByteArray &data, uchar typeId = TEXT); - void castToPeers(const QByteArray &data, uchar typeId = TEXT); - void toPeer(const QByteArray &dst, const QByteArray &data, uchar typeId = TEXT); - void closeChByName(const QString &ch, const QString &sub); - void closeChById(quint64 id, uchar subId); - void openChByName(const QString &ch, const QString &sub); - void openChById(quint64 id, uchar subId); - void enableLoop(bool state); - void enableMoreInput(bool state); - void closeSession(); - void cmdFinished(); - void logout(); -}; - -class CommandLoader : public QObject -{ - Q_OBJECT - -public: - - explicit CommandLoader(QObject *parent = nullptr) : QObject(parent) {} - - virtual ~CommandLoader() {} - - virtual void modPath(const QString &) {} - virtual void aboutToDelete() {} - virtual bool hostRevOk(quint64, quint16, quint16, quint16) {return false;} - virtual QString lastError() {return "";} - virtual quint64 rev() {return 0;} - virtual QStringList pubCmdList() {return QStringList();} - virtual QStringList cmdList() {return QStringList();} - virtual QStringList rankExemptList() {return QStringList();} - virtual ExternCommand *cmdObj(const QString &) {return nullptr;} -}; - -#endif // EXTERN_COMMAND_H diff --git a/src/commands/fs.cpp b/src/commands/fs.cpp index 681b760..fa4ad29 100644 --- a/src/commands/fs.cpp +++ b/src/commands/fs.cpp @@ -16,16 +16,63 @@ // along with MRCI under the LICENSE.md file. If not, see // . -DownloadFile::DownloadFile(QObject *parent) : InternCommand(parent) {file = new QFile(this);} -UploadFile::UploadFile(QObject *parent) : InternCommand(parent) {file = new QFile(this);} -Delete::Delete(QObject *parent) : InternCommand(parent) {} -Copy::Copy(QObject *parent) : InternCommand(parent) {src = new QFile(this); dst = new QFile(this);} -Move::Move(QObject *parent) : Copy(parent) {} -MakePath::MakePath(QObject *parent) : InternCommand(parent) {} -ListFiles::ListFiles(QObject *parent) : InternCommand(parent) {} -FileInfo::FileInfo(QObject *parent) : InternCommand(parent) {} -ChangeDir::ChangeDir(QObject *parent) : InternCommand(parent) {} -Tree::Tree(QObject *parent) : InternCommand(parent) {} +QByteArray toFILE_INFO(const QFileInfo &info) +{ + // this function converts some information extracted from a QFileInfo object to + // a FILE_INFO frame. + + // format: [1byte(flags)][8bytes(createTime)][8bytes(modTime)][8bytes(fileSize)] + // [TEXT(fileName)][TEXT(symLinkTarget)] + + // note: the TEXT strings are 16bit NULL terminated meaning 2 bytes of 0x00 + // indicate the end of the string. + + // note: the integer data found in flags, modTime, createTime and fileSize + // are formatted in little endian byte order (unsigned). + + char flags = 0; + + if (info.isFile()) flags |= IS_FILE; + if (info.isDir()) flags |= IS_DIR; + if (info.isSymLink()) flags |= IS_SYMLNK; + if (info.isReadable()) flags |= CAN_READ; + if (info.isWritable()) flags |= CAN_WRITE; + if (info.isExecutable()) flags |= CAN_EXE; + if (info.exists()) flags |= EXISTS; + + QByteArray ret; + QByteArray strTerm(2, 0); + + ret.append(flags); + ret.append(wrInt(info.birthTime().toMSecsSinceEpoch(), 64)); + ret.append(wrInt(info.lastModified().toMSecsSinceEpoch(), 64)); + ret.append(wrInt(info.size(), 64)); + ret.append(toTEXT(info.fileName()) + strTerm); + ret.append(toTEXT(info.symLinkTarget() + strTerm)); + + return ret; +} + +QByteArray toFILE_INFO(const QString &path) +{ + return toFILE_INFO(QFileInfo(path)); +} + +void mkPathForFile(const QString &path) +{ + mkPath(QFileInfo(path).absolutePath()); +} + +DownloadFile::DownloadFile(QObject *parent) : CmdObject(parent) {file = new QFile(this);} +UploadFile::UploadFile(QObject *parent) : CmdObject(parent) {file = new QFile(this);} +Delete::Delete(QObject *parent) : CmdObject(parent) {} +Copy::Copy(QObject *parent) : CmdObject(parent) {src = new QFile(this); dst = new QFile(this);} +Move::Move(QObject *parent) : Copy(parent) {} +MakePath::MakePath(QObject *parent) : CmdObject(parent) {} +ListFiles::ListFiles(QObject *parent) : CmdObject(parent) {} +FileInfo::FileInfo(QObject *parent) : CmdObject(parent) {} +ChangeDir::ChangeDir(QObject *parent) : CmdObject(parent) {} +Tree::Tree(QObject *parent) : CmdObject(parent) {} QString DownloadFile::cmdName() {return "fs_download";} QString UploadFile::cmdName() {return "fs_upload";} @@ -38,22 +85,14 @@ QString FileInfo::cmdName() {return "fs_info";} QString ChangeDir::cmdName() {return "fs_cd";} QString Tree::cmdName() {return "fs_tree";} -bool DownloadFile::handlesGenfile() -{ - return true; -} - -void DownloadFile::term() +void DownloadFile::clear() { file->close(); - buffSize = static_cast(qPow(2, MAX_FRAME_BITS) - 1); - ssMode = false; - dataSent = 0; - len = 0; - - emit enableLoop(false); - emit enableMoreInput(false); + buffSize = static_cast(qPow(2, MAX_FRAME_BITS) - 1); + dataSent = 0; + len = 0; + flags = 0; } void DownloadFile::sendChunk() @@ -64,26 +103,26 @@ void DownloadFile::sendChunk() dataSent += data.size(); - emit dataToClient(data, GEN_FILE); + emit procOut(data, GEN_FILE); mainTxt(QString::number(dataSent) + "/" + QString::number(len) + "\n"); if ((dataSent >= len) || file->atEnd()) { - term(); + clear(); } } -void DownloadFile::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void DownloadFile::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs) - - if ((dType == GEN_FILE) && (moreInputEnabled() || loopEnabled())) + if ((dType == GEN_FILE) && (flags & (MORE_INPUT | LOOPING))) { sendChunk(); } else if (dType == GEN_FILE) { + clear(); + QStringList args = parseArgs(binIn, 11); QString path = getParam("-remote_file", args); QString offStr = getParam("-offset", args); @@ -119,8 +158,8 @@ void DownloadFile::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn { QString genfileRet = "-from_host"; - ssMode = argExists("-single_step", args); len = lenStr.toLongLong(); + flags |= MORE_INPUT; if ((len == 0) || (len > file->size())) { @@ -129,33 +168,28 @@ void DownloadFile::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn len = file->size(); } + if (!argExists("-single_step", args)) + { + flags |= LOOPING; + } + file->seek(offStr.toLongLong()); - emit enableMoreInput(true); - emit enableLoop(!ssMode); - emit dataToClient(toTEXT(genfileRet), GEN_FILE); + emit procOut(toTEXT(genfileRet), GEN_FILE); } } } -bool UploadFile::handlesGenfile() -{ - return true; -} - -void UploadFile::term() +void UploadFile::clear() { file->close(); force = false; confirm = false; - ssMode = false; dataReceived = 0; len = 0; + flags = 0; mode = nullptr; - - emit enableLoop(false); - emit enableMoreInput(false); } void UploadFile::wrToFile(const QByteArray &data) @@ -168,11 +202,11 @@ void UploadFile::wrToFile(const QByteArray &data) if (dataReceived >= len) { - term(); + clear(); } - else if (ssMode) + else if (flags & SINGLE_STEP_MODE) { - emit dataToClient(QByteArray(), GEN_FILE); + emit procOut(QByteArray(), GEN_FILE); } } @@ -187,19 +221,17 @@ void UploadFile::run() { if (file->open(mode)) { - emit dataToClient(QByteArray(), GEN_FILE); + emit procOut(QByteArray(), GEN_FILE); } else { errTxt("err: Unable to open the remote file for writing. reason: " + file->errorString() + "\n"); - term(); + clear(); } } -void UploadFile::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void UploadFile::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs) - if (((dType == GEN_FILE) || (dType == TEXT)) && confirm) { QString ans = fromTEXT(binIn); @@ -212,19 +244,21 @@ void UploadFile::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } else if (noCaseMatch("n", ans)) { - term(); + clear(); } else { ask(); } } - else if ((dType == GEN_FILE) && moreInputEnabled()) + else if ((dType == GEN_FILE) && (flags & MORE_INPUT)) { wrToFile(binIn); } else if (dType == GEN_FILE) { + clear(); + QStringList args = parseArgs(binIn, 11); QString lenStr = getParam("-len", args); QString offStr = getParam("-offset", args); @@ -233,8 +267,19 @@ void UploadFile::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, file->setFileName(dst); - if (argExists("-truncate", args)) mode = QFile::ReadWrite | QFile::Truncate; - else mode = QFile::ReadWrite; + if (argExists("-truncate", args)) + { + mode = QFile::ReadWrite | QFile::Truncate; + } + else + { + mode = QFile::ReadWrite; + } + + if (argExists("-single_step", args)) + { + flags |= SINGLE_STEP_MODE; + } if (dst.isEmpty()) { @@ -254,14 +299,13 @@ void UploadFile::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } else { - ssMode = argExists("-single_step", args); force = argExists("-force", args); len = lenStr.toLongLong(); + flags |= MORE_INPUT; file->seek(offStr.toLongLong()); - emit enableMoreInput(true); - emit dataToClient(toTEXT("-to_host"), GEN_FILE); + emit procOut(toTEXT("-to_host"), GEN_FILE); if (exists && !force) ask(); else run(); @@ -269,16 +313,9 @@ void UploadFile::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } } -void Delete::term() -{ - emit enableMoreInput(false); - - path.clear(); -} - void Delete::ask() { - emit enableMoreInput(true); + flags |= MORE_INPUT; mainTxt("Are you sure you want to delete the object? (y/n): "); } @@ -287,7 +324,9 @@ void Delete::run() { bool ok; - if (QFileInfo(path).isFile() || QFileInfo(path).isSymLink()) + QFileInfo info(path); + + if (info.isFile() || info.isSymLink()) { ok = QFile::remove(path); } @@ -296,26 +335,35 @@ void Delete::run() ok = QDir(path).removeRecursively(); } - if (!ok) errTxt("err: Could not delete '" + path + "' for an unknown reason.\n"); + if (!ok && !info.exists()) + { + errTxt("err: '" + path + "' already does not exists.\n"); + } + else if (!ok && info.isWritable()) + { + errTxt("err: Could not delete '" + path + "' for an unknown reason.\n"); + } + else if (!ok) + { + errTxt("err: Could not delete '" + path + ".' permission denied.\n"); + } } -void Delete::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void Delete::procIn(const QByteArray &binIn, uchar dType) { - Q_UNUSED(sharedObjs) - - if (moreInputEnabled() && (dType == TEXT)) + if ((flags & MORE_INPUT) && (dType == TEXT)) { QString ans = fromTEXT(binIn); if (noCaseMatch("y", ans)) { - emit enableMoreInput(false); + flags &= ~MORE_INPUT; run(); } else if (noCaseMatch("n", ans)) { - term(); + flags &= ~MORE_INPUT; } else { @@ -348,12 +396,13 @@ void Delete::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, ucha } } -void Copy::term() +void Copy::clear() { fromQueue = false; procedAFile = false; yToAll = false; nToAll = false; + flags = 0; src->close(); dst->close(); @@ -362,14 +411,11 @@ void Copy::term() srcPath.clear(); dstPath.clear(); oriSrcPath.clear(); - - emit enableLoop(false); - emit enableMoreInput(false); } void Copy::ask() { - emit enableMoreInput(true); + flags |= MORE_INPUT; QString opts; @@ -427,7 +473,7 @@ void Copy::run() else { errTxt("err: Unable to re-create the source symlink at the destination path. writing to the path is not possible/denied.\n"); - term(); + clear(); } } else if (QFileInfo(srcPath).isDir()) @@ -435,19 +481,19 @@ void Copy::run() mkPath(dstPath); listDir(queue, srcPath, dstPath); - emit enableLoop(true); + flags |= LOOPING; } else { if (!dst->open(QFile::WriteOnly | QFile::Truncate)) { errTxt("err: Unable to open the destination file '" + dstPath + "' for writing. reason: " + dst->errorString() + "\n"); - term(); + clear(); } else if (!src->open(QFile::ReadOnly)) { errTxt("err: Unable to open the source file '" + srcPath + "' for reading. reason: " + src->errorString() + "\n"); - term(); + clear(); } else { @@ -455,16 +501,14 @@ void Copy::run() mainTxt("'" + srcPath + "' --> '" + dstPath + "'\n\n"); - emit enableLoop(true); + flags |= LOOPING; } } } -void Copy::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void Copy::procIn(const QByteArray &binIn, uchar dType) { - Q_UNUSED(sharedObjs) - - if (loopEnabled()) + if (flags & LOOPING) { if (src->isOpen() || dst->isOpen()) { @@ -497,7 +541,7 @@ void Copy::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar if (exists && !yToAll && !nToAll) { - enableLoop(false); + flags &= ~LOOPING; ask(); } @@ -509,43 +553,50 @@ void Copy::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar else { preFinish(); - term(); + clear(); } } - else if ((dType == TEXT) && moreInputEnabled()) + else if ((dType == TEXT) && (flags & MORE_INPUT)) { QString ans = fromTEXT(binIn); if (noCaseMatch("y", ans)) { - emit enableLoop(fromQueue); - emit enableMoreInput(false); + if (fromQueue) flags |= LOOPING; + + flags &= ~MORE_INPUT; run(); } else if (noCaseMatch("n", ans)) { - emit enableLoop(fromQueue); - emit enableMoreInput(false); + flags &= ~MORE_INPUT; - if (!loopEnabled()) term(); + if (fromQueue) + { + flags |= LOOPING; + } + else + { + clear(); + } } else if (fromQueue) { if (noCaseMatch("y-all", ans)) { - emit enableLoop(fromQueue); - emit enableMoreInput(false); + if (fromQueue) flags |= LOOPING; + flags &= ~MORE_INPUT; yToAll = true; run(); } else if (noCaseMatch("n-all", ans)) { - emit enableLoop(fromQueue); - emit enableMoreInput(false); + if (fromQueue) flags |= LOOPING; + flags &= ~MORE_INPUT; nToAll = true; } else @@ -560,6 +611,8 @@ void Copy::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar } else if (dType == TEXT) { + clear(); + QStringList args = parseArgs(binIn, 5); bool force = argExists("-force", args); @@ -574,19 +627,19 @@ void Copy::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar if (srcPath.isEmpty()) { - errTxt("err: The source file (-src) argument was not found or is empty.\n"); + errTxt("err: The source (-src) argument was not found or is empty.\n"); } else if (dstPath.isEmpty()) { - errTxt("err: The destination file (-dst) argument was not found or is empty.\n"); + errTxt("err: The destination (-dst) argument was not found or is empty.\n"); } else if (!QFileInfo(srcPath).exists()) { - errTxt("err: The source file does not exists.\n"); + errTxt("err: The source does not exists.\n"); } else if (dstExists && !matchedFsObjTypes(srcPath, dstPath)) { - errTxt("err: The existing destination object type does not match the source object type.\n"); + errTxt("err: The existing destination object type (file,dir,symmlink) does not match the source object type.\n"); } else if (permissionsOk(dstExists)) { @@ -615,7 +668,7 @@ void Move::runOnMatchingVolume() if (!QFile::rename(srcPath, dstPath)) { errTxt("err: Unable to do move operation. it's likely the command failed to remove the existing destination object or writing to the path is not possible/denied.\n"); - term(); + clear(); } } @@ -652,10 +705,8 @@ bool Move::permissionsOk(bool dstExists) return ret; } -void MakePath::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void MakePath::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs) - if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); @@ -672,10 +723,8 @@ void MakePath::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uc } } -void ListFiles::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ListFiles::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs) - if (dType == TEXT) { QStringList args = parseArgs(binIn, 4); @@ -723,7 +772,7 @@ void ListFiles::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, u { if (infoFrame) { - emit dataToClient(toFILE_INFO(info), FILE_INFO); + emit procOut(toFILE_INFO(info), FILE_INFO); } else if (info.isDir()) { @@ -738,10 +787,8 @@ void ListFiles::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, u } } -void FileInfo::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void FileInfo::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs) - if (dType == TEXT) { QStringList args = parseArgs(binIn, 3); @@ -762,7 +809,7 @@ void FileInfo::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uc { if (infoFrame) { - emit dataToClient(toFILE_INFO(info), FILE_INFO); + emit procOut(toFILE_INFO(info), FILE_INFO); } else { @@ -792,10 +839,8 @@ void FileInfo::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uc } } -void ChangeDir::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ChangeDir::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs) - if (dType == TEXT) { QStringList args = parseArgs(binIn, 3); @@ -818,14 +863,16 @@ void ChangeDir::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, u QDir::setCurrent(path); mainTxt(QDir::currentPath() + "\n"); + async(ASYNC_SET_DIR, PRIV_IPC, toTEXT(path)); } } } -void Tree::term() +void Tree::clear() { queue.clear(); + flags = 0; infoFrames = false; noHidden = false; } @@ -851,7 +898,7 @@ void Tree::printList(const QString &path) { if (infoFrames) { - emit dataToClient(toFILE_INFO(info), FILE_INFO); + emit procOut(toFILE_INFO(info), FILE_INFO); } else if (info.isDir()) { @@ -870,21 +917,17 @@ void Tree::printList(const QString &path) if (queue.isEmpty()) { - term(); - - emit enableLoop(false); + clear(); } else { - emit enableLoop(true); + flags |= LOOPING; } } -void Tree::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void Tree::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs) - - if (loopEnabled()) + if (flags & LOOPING) { printList(queue.takeFirst().filePath()); } diff --git a/src/commands/fs.h b/src/commands/fs.h index 06722fc..34edcc6 100644 --- a/src/commands/fs.h +++ b/src/commands/fs.h @@ -18,8 +18,13 @@ // . #include "../common.h" +#include "../cmd_object.h" -class DownloadFile : public InternCommand +QByteArray toFILE_INFO(const QString &path); +QByteArray toFILE_INFO(const QFileInfo &info); +void mkPathForFile(const QString &path); + +class DownloadFile : public CmdObject { Q_OBJECT @@ -29,7 +34,6 @@ private: qint64 buffSize; qint64 len; qint64 dataSent; - bool ssMode; void sendChunk(); @@ -37,16 +41,15 @@ public: static QString cmdName(); - bool handlesGenfile(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void clear(); + void procIn(const QByteArray &binIn, quint8 dType); explicit DownloadFile(QObject *parent = nullptr); }; //----------------------- -class UploadFile : public InternCommand +class UploadFile : public CmdObject { Q_OBJECT @@ -68,16 +71,15 @@ public: static QString cmdName(); - bool handlesGenfile(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void clear(); + void procIn(const QByteArray &binIn, quint8 dType); explicit UploadFile(QObject *parent = nullptr); }; //----------------------- -class Delete : public InternCommand +class Delete : public CmdObject { Q_OBJECT @@ -92,15 +94,14 @@ public: static QString cmdName(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, uchar dType); explicit Delete(QObject *parent = nullptr); }; //------------------------ -class Copy : public InternCommand +class Copy : public CmdObject { Q_OBJECT @@ -130,8 +131,8 @@ public: static QString cmdName(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void clear(); + void procIn(const QByteArray &binIn, quint8 dType); explicit Copy(QObject *parent = nullptr); }; @@ -159,7 +160,7 @@ public: //----------------------- -class MakePath : public InternCommand +class MakePath : public CmdObject { Q_OBJECT @@ -167,14 +168,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit MakePath(QObject *parent = nullptr); }; //----------------------- -class ListFiles : public InternCommand +class ListFiles : public CmdObject { Q_OBJECT @@ -182,14 +183,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit ListFiles(QObject *parent = nullptr); }; //----------------------- -class FileInfo : public InternCommand +class FileInfo : public CmdObject { Q_OBJECT @@ -197,14 +198,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit FileInfo(QObject *parent = nullptr); }; //----------------------- -class ChangeDir : public InternCommand +class ChangeDir : public CmdObject { Q_OBJECT @@ -212,14 +213,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit ChangeDir(QObject *parent = nullptr); }; //-------------------------- -class Tree : public InternCommand +class Tree : public CmdObject { Q_OBJECT @@ -235,8 +236,8 @@ public: static QString cmdName(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void clear(); + void procIn(const QByteArray &binIn, quint8 dType); explicit Tree(QObject *parent = nullptr); }; diff --git a/src/commands/groups.cpp b/src/commands/groups.cpp deleted file mode 100644 index 098682a..0000000 --- a/src/commands/groups.cpp +++ /dev/null @@ -1,286 +0,0 @@ -#include "groups.h" - -// This file is part of MRCI. - -// MRCI is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// MRCI is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with MRCI under the LICENSE.md file. If not, see -// . - -ListGroups::ListGroups(QObject *parent) : TableViewer(parent) -{ - setParams(TABLE_GROUPS, QStringList() << COLUMN_GRNAME << COLUMN_HOST_RANK, false); -} - -CreateGroup::CreateGroup(QObject *parent) : InternCommand(parent) {} -RemoveGroup::RemoveGroup(QObject *parent) : InternCommand(parent) {} -TransGroup::TransGroup(QObject *parent) : InternCommand(parent) {} -SetGroupRank::SetGroupRank(QObject *parent) : InternCommand(parent) {} -RenameGroup::RenameGroup(QObject *parent) : InternCommand(parent) {} - -QString ListGroups::cmdName() {return "ls_groups";} -QString CreateGroup::cmdName() {return "add_group";} -QString RemoveGroup::cmdName() {return "rm_group";} -QString TransGroup::cmdName() {return "trans_group";} -QString SetGroupRank::cmdName() {return "set_group_rank";} -QString RenameGroup::cmdName() {return "rename_group";} - -void CreateGroup::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - Q_UNUSED(sharedObjs); - - if (dType == TEXT) - { - QStringList args = parseArgs(binIn, 2); - QString grName = getParam("-name", args); - - if (grName.isEmpty()) - { - errTxt("err: The group name argument (-name) was not found or is empty.\n"); - } - else if (!validGroupName(grName)) - { - errTxt("err: The group name must be 1-12 chars long and contain no spaces.\n"); - } - else if (groupExists(grName)) - { - errTxt("err: The requested group already exists.\n"); - } - else - { - Query db(this); - - db.setType(Query::PUSH, TABLE_GROUPS); - db.addColumn(COLUMN_GRNAME, grName); - db.addColumn(COLUMN_HOST_RANK, 2); - db.exec(); - } - } -} - -void RemoveGroup::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - if (dType == TEXT) - { - QStringList args = parseArgs(binIn, 2); - QString grName = getParam("-name", args); - - if (grName.isEmpty()) - { - errTxt("err: The group name (-name) argument was not found or is empty.\n"); - } - else if (noCaseMatch(ROOT_USER, grName)) - { - errTxt("err: '" + QString(ROOT_USER) + "' is a protected group, you cannot delete it.\n"); - } - else if (!validGroupName(grName)) - { - errTxt("err: Invalid group name.\n"); - } - else if (!groupExists(grName)) - { - errTxt("err: No such group found in the database.\n"); - } - else if (noCaseMatch(grName, initGroup())) - { - errTxt("err: '" + grName + "' is the initial group for new users, unable to delete it.\n"); - } - else if (!checkRank(*sharedObjs->groupName, grName)) - { - errTxt("err: The target group out ranks or is equal to your own group. access denied.\n"); - } - else - { - Query db(this); - - db.setType(Query::DEL, TABLE_GROUPS); - db.addCondition(COLUMN_GRNAME, grName); - - if (!db.exec()) - { - errTxt("err: Unable to delete group. some user accounts might still be attached to it.\n"); - } - } - } -} - -void TransGroup::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - if (dType == TEXT) - { - QStringList args = parseArgs(binIn, 4); - QString src = getParam("-src", args); - QString dst = getParam("-dst", args); - - if (src.isEmpty()) - { - errTxt("err: The source group (-src) argument was not found or is empty.\n"); - } - else if (dst.isEmpty()) - { - errTxt("err: The destination group (-dst) argument was not found or is empty.\n"); - } - else if (noCaseMatch(src, ROOT_USER) || noCaseMatch(dst, ROOT_USER)) - { - errTxt("err: Unable to transfer to or from protected group '" + QString(ROOT_USER) + "'\n"); - } - else if (!validGroupName(src)) - { - errTxt("err: Invalid source group name.\n"); - } - else if (!validGroupName(dst)) - { - errTxt("err: Invalid destination group name.\n"); - } - else if (!groupExists(src)) - { - errTxt("err: '" + src + "' does not exists.\n"); - } - else if (!groupExists(dst)) - { - errTxt("err: '" + dst + "' does not exists.\n"); - } - else if (!checkRank(*sharedObjs->groupName, src)) - { - errTxt("err: The source group out ranks your own group. access denied.\n"); - } - else if (!checkRank(*sharedObjs->groupName, dst)) - { - errTxt("err: The destination group out ranks or is equal to your own group. access denied.\n"); - } - else - { - Query db(this); - - db.setType(Query::UPDATE, TABLE_USERS); - db.addColumn(COLUMN_GRNAME, dst); - db.addCondition(COLUMN_GRNAME, src); - db.exec(); - - emit backendDataOut(ASYNC_GRP_TRANS, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); - } - } -} - -void SetGroupRank::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - if (dType == TEXT) - { - QStringList args = parseArgs(binIn, 4); - QString grName = getParam("-name", args); - QString rank = getParam("-rank", args); - - if (grName.isEmpty()) - { - errTxt("err: The group name (-name) argument was not found or is empty.\n"); - } - else if (noCaseMatch(ROOT_USER, grName)) - { - errTxt("err: '" + QString(ROOT_USER) + "' is a protected group, you cannot change it.\n"); - } - else if (!validGroupName(grName)) - { - errTxt("err: Invalid group name.\n"); - } - else if (!isInt(rank)) - { - errTxt("err: Invalid rank.\n"); - } - else if (rank.toUInt() < *sharedObjs->hostRank) - { - errTxt("err: you cannot set a rank higher than your own.\n"); - } - else if (!groupExists(grName)) - { - errTxt("err: Group name '" + grName + "' does not exists.\n"); - } - else if (!checkRank(*sharedObjs->groupName, grName)) - { - errTxt("err: The target group out ranks or is equal to your own group. access denied.\n"); - } - else - { - Query db(this); - - db.setType(Query::UPDATE, TABLE_GROUPS); - db.addColumn(COLUMN_HOST_RANK, rank.toUInt()); - db.addCondition(COLUMN_GRNAME, grName); - db.exec(); - - emit backendDataOut(ASYNC_GROUP_UPDATED, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); - } - } -} - -void RenameGroup::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - if (dType == TEXT) - { - QStringList args = parseArgs(binIn, 4); - QString name = getParam("-name", args); - QString newName = getParam("-new_name", args); - - if (name.isEmpty()) - { - errTxt("err: The group name (-name) argument was not found or is empty.\n"); - } - else if (newName.isEmpty()) - { - errTxt("err: The new name (-new_name) argument was not found or is empty.\n"); - } - else if (noCaseMatch(name, ROOT_USER)) - { - errTxt("err: Cannot rename protected group '" + QString(ROOT_USER) + "'\n"); - } - else if (noCaseMatch(newName, ROOT_USER)) - { - errTxt("err: Cannot use '" + QString(ROOT_USER) + "' for a new name. it is reserved.\n"); - } - else if (!validGroupName(name)) - { - errTxt("err: Invalid group name.\n"); - } - else if (!validGroupName(newName)) - { - errTxt("err: Invalid new group name. the group name must be between 1-12 chars long and contain no spaces.\n"); - } - else if (!groupExists(name)) - { - errTxt("err: Group name '" + name + "' does not exists.\n"); - } - else if (groupExists(newName)) - { - errTxt("err: Group name '" + newName + "' already exists.\n"); - } - else if (!checkRank(*sharedObjs->groupName, name)) - { - errTxt("err: The target group out ranks or is equal to your own group. access denied.\n"); - } - else - { - Query db(this); - - db.setType(Query::UPDATE, TABLE_GROUPS); - db.addColumn(COLUMN_GRNAME, newName); - db.addCondition(COLUMN_GRNAME, name); - db.exec(); - - args.clear(); - args.append("-src"); - args.append("'" + escapeChars(name, '\\', '\'') + "'"); - args.append("-dst"); - args.append("'" + escapeChars(newName, '\\', '\'') + "'"); - - emit backendDataOut(ASYNC_GROUP_RENAMED, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); - } - } -} diff --git a/src/commands/groups.h b/src/commands/groups.h deleted file mode 100644 index ca6a3b3..0000000 --- a/src/commands/groups.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef GROUP_CMDS_H -#define GROUP_CMDS_H - -// This file is part of MRCI. - -// MRCI is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// MRCI is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with MRCI under the LICENSE.md file. If not, see -// . - -#include "../common.h" -#include "table_viewer.h" - -class ListGroups : public TableViewer -{ - Q_OBJECT - -public: - - static QString cmdName(); - - explicit ListGroups(QObject *parent = nullptr); -}; - -//-------------------------------------- - -class CreateGroup : public InternCommand -{ - Q_OBJECT - -public: - - static QString cmdName(); - - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); - - explicit CreateGroup(QObject *parent = nullptr); -}; - -//-------------------------------------- - -class RemoveGroup : public InternCommand -{ - Q_OBJECT - -public: - - static QString cmdName(); - - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); - - explicit RemoveGroup(QObject *parent = nullptr); -}; - -//-------------------------------------- - -class TransGroup : public InternCommand -{ - Q_OBJECT - -public: - - static QString cmdName(); - - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); - - explicit TransGroup(QObject *parent = nullptr); -}; - -//------------------------------------- - -class SetGroupRank : public InternCommand -{ - Q_OBJECT - -public: - - static QString cmdName(); - - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); - - explicit SetGroupRank(QObject *parent = nullptr); -}; - -//------------------------------------ - -class RenameGroup : public InternCommand -{ - Q_OBJECT - -public: - - static QString cmdName(); - - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); - - explicit RenameGroup(QObject *parent = nullptr); -}; - -#endif // GROUP_CMDS_H diff --git a/src/commands/info.cpp b/src/commands/info.cpp index aad76ca..05315ab 100644 --- a/src/commands/info.cpp +++ b/src/commands/info.cpp @@ -18,103 +18,130 @@ IPHist::IPHist(QObject *parent) : TableViewer(parent) { - setParams(TABLE_IPHIST, QStringList() << COLUMN_TIME << COLUMN_IPADDR << COLUMN_SESSION_ID << COLUMN_CLIENT_VER << COLUMN_LOGENTRY, true); + setParams(TABLE_IPHIST, true); + addTableColumn(TABLE_IPHIST, COLUMN_TIME); + addTableColumn(TABLE_IPHIST, COLUMN_IPADDR); + addTableColumn(TABLE_IPHIST, COLUMN_APP_NAME); + addTableColumn(TABLE_IPHIST, COLUMN_SESSION_ID); + addTableColumn(TABLE_IPHIST, COLUMN_LOGENTRY); } ListDBG::ListDBG(QObject *parent) : TableViewer(parent) { - setParams(TABLE_DMESG, QStringList() << COLUMN_TIME << COLUMN_LOGENTRY, true); + setParams(TABLE_DMESG, true); + addTableColumn(TABLE_DMESG, COLUMN_TIME); + addTableColumn(TABLE_DMESG, COLUMN_LOGENTRY); } -ListCommands::ListCommands(QObject *parent) : InternCommand(parent) {} -HostInfo::HostInfo(QObject *parent) : InternCommand(parent) {} -MyInfo::MyInfo(QObject *parent) : InternCommand(parent) {} -CmdInfo::CmdInfo(QObject *parent) : InternCommand(parent) {} - -QString ListCommands::cmdName() {return "ls_cmds";} -QString HostInfo::cmdName() {return "host_info";} -QString IPHist::cmdName() {return "ls_act_log";} -QString ListDBG::cmdName() {return "ls_dbg";} -QString MyInfo::cmdName() {return "my_info";} -QString CmdInfo::cmdName() {return "cmd_info";} - -bool ListCommands::strInRowTxt(const QString &str, const QStringList &rowTxt) +ListCommands::ListCommands(const QStringList &cmdList, const QStringList &gen, QObject *parent) : CmdObject(parent) { - bool ret = false; + list = cmdList; + genfileList = gen; +} - for (auto&& strInList : rowTxt) +HostInfo::HostInfo(QObject *parent) : CmdObject(parent) {} +MyInfo::MyInfo(QObject *parent) : CmdObject(parent) {} + +QString HostInfo::cmdName() {return "host_info";} +QString IPHist::cmdName() {return "ls_act_log";} +QString ListDBG::cmdName() {return "ls_dbg";} +QString MyInfo::cmdName() {return "my_info";} + +QString ListCommands::parseMd(const QString &cmdName, int offset) +{ + QFile file(":/docs/intern_commands/" + cmdName + ".md", this); + QByteArray data; + + if (!file.open(QFile::ReadOnly)) { - if (strInList.contains(str, Qt::CaseInsensitive)) - { - ret = true; - - break; - } + qDebug() << "err: internal command: " << cmdName << " does not have a document file."; } - - return ret; -} - -void ListCommands::procBin(const SharedObjs *sharedObjs, const QByteArray &data, uchar dType) -{ - Q_UNUSED(sharedObjs); - - if (dType == TEXT) + else { - QString find = getParam("-find", parseArgs(data, 2)); - QStringList cmdNames = sharedObjs->cmdNames->values(); - QList tableData; - QStringList separators; - QList justLens; + data = file.readAll(); - for (int i = 0; i < 3; ++i) + int targetTags = offset * 6; + int pos = -1; + int len = 0; + + for (int i = 0, tags = 0; i < data.size(); ++i) { - justLens.append(12); - separators.append("-------"); - } - - cmdNames.sort(Qt::CaseInsensitive); - tableData.append(QStringList() << "command_id" << "command_name" << "summary"); - tableData.append(separators); - - for (auto&& cmdName: cmdNames) - { - QStringList rowData; - quint16 id = sharedObjs->cmdNames->key(cmdName); - - rowData.append(QString::number(id)); - rowData.append(cmdName); - rowData.append(rwSharedObjs->commands->value(id)->shortText()); - - if (find.isEmpty() || strInRowTxt(find, rowData)) + if (data[i] == '#') { - for (int k = 0; k < justLens.size(); ++k) + ++tags; + + if (pos != -1) { - if (justLens[k] < rowData[k].size()) justLens[k] = rowData[k].size(); + break; } - - tableData.append(rowData); } - } - - mainTxt("\n"); - - for (auto&& row : tableData) - { - for (int i = 0; i < row.size(); ++i) + else if (tags == targetTags) { - mainTxt(row[i].leftJustified(justLens[i] + 2, ' ')); - } + len++; - mainTxt("\n"); + if (pos == -1) + { + pos = i; + } + } } + + data = data.mid(pos, len).trimmed(); + + if (offset == 2) + { + data.chop(3); + data.remove(0, 3); + } + } + + file.close(); + + return data; +} + +QString ListCommands::shortText(const QString &cmdName) +{ + return parseMd(cmdName, 1); +} + +QString ListCommands::ioText(const QString &cmdName) +{ + return parseMd(cmdName, 2); +} + +QString ListCommands::longText(const QString &cmdName) +{ + return parseMd(cmdName, 3); +} + +void ListCommands::onIPCConnected() +{ + for (auto&& cmdName : list) + { + QByteArray frame; + QByteArray boolByte = QByteArray(1, 0x00); + + if (genfileList.contains(cmdName)) + { + boolByte = QByteArray(1, 0x01); + } + + frame.append(QByteArray(2, 0x00)); + frame.append(boolByte); + frame.append(fixedToTEXT(cmdName, BLKSIZE_CMD_NAME)); + frame.append(fixedToTEXT(libName(), BLKSIZE_LIB_NAME)); + frame.append(nullTermTEXT(shortText(cmdName))); + frame.append(nullTermTEXT(ioText(cmdName))); + frame.append(nullTermTEXT(longText(cmdName))); + + emit procOut(frame, NEW_CMD); } } -void HostInfo::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void HostInfo::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(binIn); - Q_UNUSED(sharedObjs); + Q_UNUSED(binIn) if (dType == TEXT) { @@ -129,12 +156,18 @@ void HostInfo::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uc QString txt; QTextStream txtOut(&txt); - txtOut << "Application: " << QCoreApplication::applicationName() << " v" << QCoreApplication::applicationVersion() << " " << QSysInfo::WordSize << "Bit" << endl; + hostSharedMem->lock(); + + quint32 sesCount = rd32BitFromBlock(hostLoad); + quint32 maxSes = db.getData(COLUMN_MAXSESSIONS).toUInt(); + + hostSharedMem->unlock(); + + txtOut << "Application: " << libName() << endl; txtOut << "Qt Base: " << QT_VERSION_STR << endl; - txtOut << "Import Rev: " << IMPORT_REV << endl; txtOut << "Host Name: " << QSysInfo::machineHostName() << endl; txtOut << "Host OS: " << QSysInfo::prettyProductName() << endl; - txtOut << "Load: " << rdSessionLoad() << "/" << db.getData(COLUMN_MAXSESSIONS).toUInt() << endl; + txtOut << "Load: " << sesCount << "/" << maxSes << endl; txtOut << "Listening Addr: " << db.getData(COLUMN_IPADDR).toString() << endl; txtOut << "Listening Port: " << db.getData(COLUMN_PORT).toUInt() << endl; @@ -142,103 +175,46 @@ void HostInfo::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uc } } -void MyInfo::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void MyInfo::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(binIn); + Q_UNUSED(binIn) if (dType == TEXT) { QString txt; QTextStream txtOut(&txt); - txtOut << "Session id: " << sharedObjs->sessionId->toHex() << endl; - txtOut << "IP Address: " << *sharedObjs->sessionAddr << endl; - txtOut << "Logged-in: " << boolStr(!sharedObjs->userName->isEmpty()) << endl; - txtOut << "App Name: " << *sharedObjs->appName << endl; + QString sesId = rdFromBlock(sessionId, BLKSIZE_SESSION_ID).toHex(); + QString ip = rdStringFromBlock(clientIp, BLKSIZE_CLIENT_IP); + QString app = rdStringFromBlock(appName, BLKSIZE_APP_NAME); - if (!sharedObjs->userName->isEmpty()) + txtOut << "Session id: " << sesId << endl; + txtOut << "IP Address: " << ip << endl; + txtOut << "App Name: " << app << endl; + + if (!isEmptyBlock(userId, BLKSIZE_USER_ID)) { + QByteArray uId = rdFromBlock(userId, BLKSIZE_USER_ID); + Query db(this); db.setType(Query::PULL, TABLE_USERS); db.addColumn(COLUMN_EMAIL); db.addColumn(COLUMN_TIME); db.addColumn(COLUMN_EMAIL_VERIFIED); - db.addCondition(COLUMN_USERNAME, *sharedObjs->userName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); - txtOut << "User Name: " << *sharedObjs->userName << endl; - txtOut << "Group Name: " << *sharedObjs->groupName << endl; - txtOut << "Display Name: " << *sharedObjs->displayName << endl; - txtOut << "User ID: " << sharedObjs->userId->toHex() << endl; + txtOut << "User Name: " << rdStringFromBlock(userName, BLKSIZE_USER_NAME) << endl; + txtOut << "Display Name: " << rdStringFromBlock(displayName, BLKSIZE_DISP_NAME) << endl; + txtOut << "User id: " << uId.toHex() << endl; txtOut << "Email: " << db.getData(COLUMN_EMAIL).toString() << endl; txtOut << "Register Date: " << db.getData(COLUMN_TIME).toString() << endl; txtOut << "Email Verified: " << boolStr(db.getData(COLUMN_EMAIL_VERIFIED).toBool()) << endl; - txtOut << "Owner Override: " << boolStr(*sharedObjs->chOwnerOverride) << endl; - txtOut << "Host Rank: " << *sharedObjs->hostRank << endl; + txtOut << "Owner Override: " << boolStr(rd8BitFromBlock(chOwnerOverride)) << endl; + txtOut << "Host Rank: " << rd32BitFromBlock(hostRank) << endl; } mainTxt(txt); } } - -void CmdInfo::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - if (dType == TEXT) - { - QStringList args = parseArgs(binIn, 2); - QString name = getParam("-cmd_name", args); - QString cmdId = getParam("-cmd_id", args); - - if (name.isEmpty() && cmdId.isEmpty()) - { - errTxt("err: Command name (-cmd_name) of command id (-cmd_id) parameter not found or is empty.\n"); - } - else if (!name.isEmpty() && !validCommandName(name)) - { - errTxt("err: Command name '" + name + "' is not valid.\n"); - } - else if (!cmdId.isEmpty() && !isInt(cmdId)) - { - errTxt("err: Command id '" + cmdId + "' is not a valid integer.\n"); - } - else if (!name.isEmpty() && !sharedObjs->cmdNames->values().contains(name.toLower())) - { - errTxt("err: No such command name '" + name + "'\n"); - } - else if (!cmdId.isEmpty() && !sharedObjs->cmdNames->contains(cmdId.toUShort())) - { - errTxt("err: No such command id '" + cmdId + "'\n"); - } - else - { - if (!name.isEmpty()) - { - cmdId = QString::number(sharedObjs->cmdNames->key(name)); - } - - if (!cmdId.isEmpty()) - { - name = sharedObjs->cmdNames->value(cmdId.toUShort()); - } - - QString txt; - QTextStream txtOut(&txt); - - ExternCommand *cmdObj = rwSharedObjs->commands->value(cmdId.toUShort()); - - txtOut << "Command name: " << name << endl; - txtOut << "Command id: " << cmdId << endl; - txtOut << "Gen file: " << boolStr(cmdObj->handlesGenfile()) << endl << endl; - - txtOut << "IO:" << endl << endl; - txtOut << cmdObj->ioText() << endl << endl; - txtOut << "Summary:" << endl << endl; - txtOut << cmdObj->shortText() << endl << endl; - txtOut << "Details:" << endl << endl; - - mainTxt(txt); - bigTxt(cmdObj->longText()); - } - } -} diff --git a/src/commands/info.h b/src/commands/info.h index 8cb3d50..9b59152 100644 --- a/src/commands/info.h +++ b/src/commands/info.h @@ -18,28 +18,35 @@ // . #include "../common.h" +#include "../cmd_object.h" #include "table_viewer.h" -class ListCommands : public InternCommand +class ListCommands : public CmdObject { Q_OBJECT private: - bool strInRowTxt(const QString &str, const QStringList &rowTxt); + QStringList list; + QStringList genfileList; + + QString parseMd(const QString &cmdName, int offset); + QString shortText(const QString &cmdName); + QString ioText(const QString &cmdName); + QString longText(const QString &cmdName); + +private slots: + + void onIPCConnected(); public: - static QString cmdName(); - - void procBin(const SharedObjs *sharedObjs, const QByteArray &data, uchar dType); - - explicit ListCommands(QObject *parent = nullptr); + explicit ListCommands(const QStringList &cmdList, const QStringList &gen, QObject *parent = nullptr); }; //-------------------------------------- -class HostInfo : public InternCommand +class HostInfo : public CmdObject { Q_OBJECT @@ -47,7 +54,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit HostInfo(QObject *parent = nullptr); }; @@ -67,7 +74,7 @@ public: //------------------------------------ -class MyInfo : public InternCommand +class MyInfo : public CmdObject { Q_OBJECT @@ -75,7 +82,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit MyInfo(QObject *parent = nullptr); }; @@ -93,19 +100,4 @@ public: explicit ListDBG(QObject *parent = nullptr); }; -//---------------------------------- - -class CmdInfo : public InternCommand -{ - Q_OBJECT - -public: - - static QString cmdName(); - - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); - - explicit CmdInfo(QObject *parent = nullptr); -}; - #endif // INFO_H diff --git a/src/commands/mods.cpp b/src/commands/mods.cpp index 1d975a8..39bda0c 100644 --- a/src/commands/mods.cpp +++ b/src/commands/mods.cpp @@ -18,338 +18,88 @@ ListMods::ListMods(QObject *parent) : TableViewer(parent) { - setParams(TABLE_MODULES, QStringList() << COLUMN_MOD_NAME << COLUMN_MOD_MAIN << COLUMN_LOCKED << COLUMN_CMD_ID_OFFS, false); + setParams(TABLE_MODULES, false); + addTableColumn(TABLE_MODULES, COLUMN_MOD_MAIN); } -UploadMod::UploadMod(QObject *parent) : InternCommand(parent) -{ - dSize = 0; - fileBuff = new QTemporaryFile(this); - proc = new QProcess(this); - - proc->setProcessChannelMode(QProcess::SeparateChannels); - - connect(proc, SIGNAL(finished(int)), this, SLOT(procFinished(int))); - connect(proc, SIGNAL(readyReadStandardOutput()), this, SLOT(rdTextFromProc())); - connect(proc, SIGNAL(readyReadStandardError()), this, SLOT(rdErrFromProc())); -} - -DelMod::DelMod(QObject *parent) : InternCommand(parent) {} +UploadMod::UploadMod(QObject *parent) : CmdObject(parent) {} +DelMod::DelMod(QObject *parent) : CmdObject(parent) {} QString ListMods::cmdName() {return "ls_mods";} QString DelMod::cmdName() {return "rm_mod";} QString UploadMod::cmdName() {return "add_mod";} -QString rmFileSuffix(const QString &filePath) +bool UploadMod::isExecutable(const QString &path) { - QString suffix = "." + QFileInfo(filePath).suffix(); + QFileInfo info(expandEnvVariables(path)); - return filePath.left(filePath.size() - suffix.size()); + return info.exists() && info.isExecutable(); } -bool validFileOnlyName(const QString &fileName) +void UploadMod::procIn(const QByteArray &binIn, quint8 dType) { - QString forbidden = "/\\:*?\"<>|"; - bool ret = true; - - if (fileName.contains("..")) - { - ret = false; - } - else - { - for (auto&& chr : forbidden) - { - if (fileName.contains(chr, Qt::CaseInsensitive)) - { - ret = false; - - break; - } - } - } - - return ret; -} - -bool UploadMod::handlesGenfile() -{ - return true; -} - -bool UploadMod::libExists(const QString &path) -{ -#ifdef Q_OS_WIN - - return QFileInfo::exists(path + ".dll") || - QFileInfo::exists(path + ".DLL"); -#endif - -#ifdef Q_OS_UNIX - - return QFileInfo::exists(path + ".so"); - -#endif - -#ifdef Q_OS_AIX - - return QFileInfo::exists(path + ".a"); - -#endif - -#ifdef Q_OS_MAC - - return QFileInfo::exists(path + ".so") || - QFileInfo::exists(path + ".dylib") || - QFileInfo::exists(path + ".bundle"); -#endif -} - -void UploadMod::clearOnfailure() -{ - QDir(modPath).removeRecursively(); - - Query db(this); - - db.setType(Query::DEL, TABLE_MODULES); - db.addCondition(COLUMN_MOD_NAME, modName); - db.exec(); -} - -void UploadMod::procFinished(int exStatus) -{ - if (exStatus != 0) - { - errTxt("\nerr: The file operation stopped on error.\n"); - clearOnfailure(); - } - else if (!libExists(modPath + "/main")) - { - errTxt("\nerr: The module's main library file does not exists or is not supported.\n"); - clearOnfailure(); - } - else - { - Query db(this); - - db.setType(Query::UPDATE, TABLE_MODULES); - db.addColumn(COLUMN_LOCKED, false); - db.addCondition(COLUMN_MOD_NAME, modName); - db.exec(); - - emit backendDataOut(ASYNC_ENABLE_MOD, toTEXT(modName), PUB_IPC_WITH_FEEDBACK); - - mainTxt("\nFinished..."); - } - - term(); -} - -void UploadMod::term() -{ - if (proc->state() == QProcess::Running) - { - proc->blockSignals(true); - proc->kill(); - } - - fileBuff->close(); - - modPath.clear(); - modName.clear(); - - dSize = 0; - - emit enableMoreInput(false); -} - -void UploadMod::rdErrFromProc() -{ - errTxt(proc->readAllStandardError()); -} - -void UploadMod::rdTextFromProc() -{ - mainTxt(proc->readAllStandardOutput()); -} - -void UploadMod::procStartError(QProcess::ProcessError err) -{ - if (err == QProcess::FailedToStart) - { - errTxt("err: Could not start the host archiver. reason: " + proc->errorString() + "\n"); - clearOnfailure(); - term(); - } -} - -void UploadMod::setup() -{ - mkPath(modPath); - - if (QLibrary::isLibrary(clientFile)) - { - QString suffix = QFileInfo(clientFile).completeSuffix(); - QString dst = modPath + "/main." + suffix; - - mainTxt("copy file: " + fileBuff->fileName() + " --> " + dst + "\n"); - - if (QFile::copy(fileBuff->fileName(), dst)) - { - procFinished(0); - } - else - { - procFinished(1); - } - } - else - { - Query db(this); - - db.setType(Query::PULL, TABLE_SERV_SETTINGS); - db.addColumn(COLUMN_ZIPBIN); - db.addColumn(COLUMN_ZIPEXTRACT); - db.exec(); - - QString exePath = db.getData(COLUMN_ZIPBIN).toString(); - QString cmdLine = db.getData(COLUMN_ZIPEXTRACT).toString(); - - cmdLine.replace(INPUT_DIR_SUB, "'" + fileBuff->fileName() + "'"); - cmdLine.replace(OUTPUT_DIR_SUB, "'" + modPath + "'"); - - proc->blockSignals(false); - proc->setProgram(expandEnvVariables(exePath)); - proc->setArguments(parseArgs(toTEXT(cmdLine), -1)); - proc->start(); - } -} - -void UploadMod::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - Q_UNUSED(sharedObjs) - - if (moreInputEnabled() && (dType == GEN_FILE) && (proc->state() == QProcess::NotRunning)) - { - if (fileBuff->write(binIn.data(), binIn.size()) == -1) - { - errTxt("err: Temp file write failure, unable to continue.\n"); - clearOnfailure(); - term(); - } - else - { - mainTxt(QString::number(fileBuff->pos()) + "/" + QString::number(dSize) + "\n"); - - if (fileBuff->size() >= dSize) - { - mainTxt("\nUpload complete...setting up.\n\n"); - setup(); - } - } - } - else if (dType == GEN_FILE) - { - QStringList args = parseArgs(binIn, -1); - QString name = getParam("-name", args); - QString len = getParam("-len", args); - bool sOk = false; - - clientFile = getParam("-client_file", args); - dSize = len.toLongLong(&sOk, 10); - modPath = modDataPath() + "/" + name; - modName = name; - - if (name.isEmpty()) - { - errTxt("err: Module name (-name) argument not found or is empty.\n"); - } - else if (len.isEmpty()) - { - errTxt("err: Data length (-len) argument not found or is empty.\n"); - } - if (maxedInstalledMods()) - { - errTxt("err: Host maximum amount of installed modules has been reached.\n"); - } - else if (!sOk) - { - errTxt("err: The given data length is not a valid integer.\n"); - } - else if (name.size() > 64) - { - errTxt("err: The module name cannot be larger than 64 chars long.\n"); - } - else if (!validFileOnlyName(name)) - { - errTxt("err: Module names cannot contain the following chars: /\\:*?\"<>| or the updir sequence: '..'\n"); - } - else if (modExists(name)) - { - errTxt("err: A module of the same name already exists.\n"); - } - else if (!fileBuff->open()) - { - errTxt("err: Could not open a new temp file for reading/writing.\n"); - } - else - { - Query db(this); - - db.setType(Query::PULL, TABLE_MODULES); - db.addColumn(COLUMN_MOD_NAME); - db.exec(); - - quint16 idOffs = static_cast((db.rows() + 2) * MAX_CMDS_PER_MOD) + 1; - - db.setType(Query::PUSH, TABLE_MODULES); - db.addColumn(COLUMN_MOD_NAME, name); - db.addColumn(COLUMN_MOD_MAIN, modPath + "/main"); - db.addColumn(COLUMN_CMD_ID_OFFS, idOffs); - db.addColumn(COLUMN_LOCKED, true); - db.exec(); - - mainTxt("Input hooked...uploading data.\n\n"); - - emit enableMoreInput(true); - emit dataToClient(toTEXT("-to_host"), GEN_FILE); - emit dataToClient(QByteArray(), GEN_FILE); - } - } -} - -void DelMod::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) -{ - Q_UNUSED(sharedObjs) - if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); - QString name = getParam("-name", args); + QString path = getParam("-mod_path", args); - if (name.isEmpty()) + if (path.isEmpty()) { - errTxt("err: Module name argument (-name) was not found or is empty.\n"); + errTxt("err: The path to module executable (-mod_path) argument not found or is empty.\n"); } - else if (!validFileOnlyName(name)) + else if (!validModPath(path)) { - errTxt("err: Module names cannot contain the following chars: /\\:*?\"<>| or the updir sequence: '..'\n"); + errTxt("err: The module path cannot contain the following chars: :*?\"<>|\n"); } - else if (!modExists(name)) + else if (modExists(path)) { - errTxt("err: No such module found: '" + name + "'\n"); + errTxt("err: The module already exists.\n"); + } + else if (!isExecutable(path)) + { + errTxt("err: Executable: " + path + " does not exists or does not have execution permissions.\n"); } else { Query db(this); - db.setType(Query::UPDATE, TABLE_MODULES); - db.addColumn(COLUMN_LOCKED, true); - db.addCondition(COLUMN_MOD_NAME, name); + db.setType(Query::PUSH, TABLE_MODULES); + db.addColumn(COLUMN_MOD_MAIN, path); db.exec(); - emit backendDataOut(ASYNC_DISABLE_MOD, toTEXT(name), PRIV_IPC); - emit backendDataOut(ASYNC_DISABLE_MOD, toTEXT(name), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_ENABLE_MOD, PUB_IPC_WITH_FEEDBACK, toTEXT(path)); + } + } +} + +void DelMod::procIn(const QByteArray &binIn, quint8 dType) +{ + if (dType == TEXT) + { + QStringList args = parseArgs(binIn, 2); + QString path = getParam("-mod_path", args); + + if (path.isEmpty()) + { + errTxt("err: The path to module executable (-mod_path) argument not found or is empty.\n"); + } + else if (!validModPath(path)) + { + errTxt("err: The module path cannot contain the following chars: :*?\"<>|\n"); + } + else if (!modExists(path)) + { + errTxt("err: No such module found: '" + path + "'\n"); + } + else + { + Query db(this); + + db.setType(Query::DEL, TABLE_MODULES); + db.addCondition(COLUMN_MOD_MAIN, path); + db.exec(); + + async(ASYNC_DISABLE_MOD, PUB_IPC_WITH_FEEDBACK, toTEXT(path)); } } } diff --git a/src/commands/mods.h b/src/commands/mods.h index 34bab23..d7625ab 100644 --- a/src/commands/mods.h +++ b/src/commands/mods.h @@ -18,49 +18,32 @@ // . #include "../common.h" +#include "../cmd_object.h" #include "table_viewer.h" QString rmFileSuffix(const QString &filePath); bool validFileOnlyName(const QString &fileName); -class UploadMod : public InternCommand +class UploadMod : public CmdObject { Q_OBJECT private: - QProcess *proc; - QString clientFile; - QString modName; - QString modPath; - QTemporaryFile *fileBuff; - qint64 dSize; - - void clearOnfailure(); - void setup(); - bool libExists(const QString &path); - -private slots: - - void rdTextFromProc(); - void rdErrFromProc(); - void procFinished(int exStatus); - void procStartError(QProcess::ProcessError err); + bool isExecutable(const QString &path); public: static QString cmdName(); - bool handlesGenfile(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit UploadMod(QObject *parent = nullptr); }; //------------------------------- -class DelMod : public InternCommand +class DelMod : public CmdObject { Q_OBJECT @@ -68,7 +51,7 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit DelMod(QObject *parent = nullptr); }; diff --git a/src/commands/p2p.cpp b/src/commands/p2p.cpp index d68e642..ac61d48 100644 --- a/src/commands/p2p.cpp +++ b/src/commands/p2p.cpp @@ -16,11 +16,11 @@ // along with MRCI under the LICENSE.md file. If not, see // . -ToPeer::ToPeer(QObject *parent) : InternCommand(parent) {} -P2PRequest::P2PRequest(QObject *parent) : InternCommand(parent) {} -P2POpen::P2POpen(QObject *parent) : InternCommand(parent) {} -P2PClose::P2PClose(QObject *parent) : InternCommand(parent) {} -LsP2P::LsP2P(QObject *parent) : InternCommand(parent) {} +ToPeer::ToPeer(QObject *parent) : CmdObject(parent) {} +P2PRequest::P2PRequest(QObject *parent) : CmdObject(parent) {} +P2POpen::P2POpen(QObject *parent) : CmdObject(parent) {} +P2PClose::P2PClose(QObject *parent) : CmdObject(parent) {} +LsP2P::LsP2P(QObject *parent) : CmdObject(parent) {} QString ToPeer::cmdName() {return "to_peer";} QString P2PRequest::cmdName() {return "p2p_request";} @@ -28,92 +28,133 @@ QString P2POpen::cmdName() {return "p2p_open";} QString P2PClose::cmdName() {return "p2p_close";} QString LsP2P::cmdName() {return "ls_p2p";} -void ToPeer::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ToPeer::procIn(const QByteArray &binIn, quint8 dType) { - QByteArray peerId = binIn.left(28); - - if (!sharedObjs->p2pAccepted->contains(peerId)) + if (binIn.size() >= BLKSIZE_SESSION_ID) { - errTxt("err: You don't current have an open p2p connection with the requested peer."); + errTxt("err: The p2p data does not contain a session id header.\n"); + } + if (posOfBlock(binIn.data(), p2pAccepted, MAX_P2P_LINKS, BLKSIZE_SESSION_ID) == -1) + { + errTxt("err: You don't currently have an open p2p connection with the requested peer.\n"); } else { - emit toPeer(peerId, binIn.mid(28), dType); + quint32 len = static_cast(binIn.size()); + QByteArray dst = rdFromBlock(binIn.data(), BLKSIZE_SESSION_ID); + QByteArray src = rdFromBlock(sessionId, BLKSIZE_SESSION_ID); + QByteArray data = rdFromBlock(binIn.data() + BLKSIZE_SESSION_ID, len - BLKSIZE_SESSION_ID); + QByteArray typeBa = wrInt(dType, 8); + + async(ASYNC_P2P, PUB_IPC, dst + src + typeBa + data); } } -void P2PRequest::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void P2PRequest::procIn(const QByteArray &binIn, quint8 dType) { if (dType == SESSION_ID) { - if (binIn.size() != 28) + if (binIn.size() != BLKSIZE_SESSION_ID) { - errTxt("err: The given client session id does not equal 28 bytes."); + errTxt("err: The given client session id does not equal " + QString::number(BLKSIZE_SESSION_ID) + " bytes.\n"); } - else if (sharedObjs->p2pAccepted->contains(binIn)) + else if (posOfBlock(binIn.data(), p2pAccepted, MAX_P2P_LINKS, BLKSIZE_SESSION_ID) != -1) { - errTxt("err: You already have an open p2p connection with the requested peer."); + errTxt("err: You already have an open p2p connection with the requested peer.\n"); } - else if (sharedObjs->p2pPending->contains(binIn)) + else if (posOfBlock(binIn.data(), p2pPending, MAX_P2P_LINKS, BLKSIZE_SESSION_ID) != -1) { - errTxt("err: There is already a pending p2p request for this peer."); + errTxt("err: There is already a pending p2p request for this peer.\n"); } else { - emit toPeer(binIn, QByteArray(), P2P_REQUEST); + QByteArray dst = rdFromBlock(binIn.data(), BLKSIZE_SESSION_ID); + QByteArray src = rdFromBlock(sessionId, BLKSIZE_SESSION_ID); + QByteArray typeBa = wrInt(P2P_REQUEST, 8); + + async(ASYNC_P2P, PUB_IPC, dst + src + typeBa + dst); } } } -void P2POpen::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void P2POpen::procIn(const QByteArray &binIn, quint8 dType) { if (dType == SESSION_ID) { - if (binIn.size() != 28) + if (binIn.size() != BLKSIZE_SESSION_ID) { - errTxt("err: The given client session id does not equal 28 bytes."); + errTxt("err: The given client session id does not equal " + QString::number(BLKSIZE_SESSION_ID) + " bytes.\n"); } - else if (sharedObjs->p2pAccepted->contains(binIn)) + else if (posOfBlock(binIn.data(), p2pAccepted, MAX_P2P_LINKS, BLKSIZE_SESSION_ID) != -1) { - errTxt("err: You already have an open p2p connection with the requested peer."); + errTxt("err: You already have an open p2p connection with the requested peer.\n"); } - else if (!sharedObjs->p2pPending->contains(binIn)) + else if (posOfBlock(binIn.data(), p2pPending, MAX_P2P_LINKS, BLKSIZE_SESSION_ID) != -1) { - errTxt("err: There is no pending p2p request for the given peer."); + errTxt("err: There is no pending p2p request for the given peer.\n"); } else { - emit toPeer(binIn, QByteArray(), P2P_OPEN); + QByteArray dst = rdFromBlock(binIn.data(), BLKSIZE_SESSION_ID); + QByteArray src = rdFromBlock(sessionId, BLKSIZE_SESSION_ID); + QByteArray typeBa = wrInt(P2P_OPEN, 8); + + async(ASYNC_P2P, PUB_IPC, dst + src + typeBa + dst); } } } -void P2PClose::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void P2PClose::procIn(const QByteArray &binIn, quint8 dType) { if (dType == SESSION_ID) { - if (binIn.size() != 28) + if (binIn.size() != BLKSIZE_SESSION_ID) { - errTxt("err: The given client session id does not equal 28 bytes."); + errTxt("err: The given client session id does not equal " + QString::number(BLKSIZE_SESSION_ID) + " bytes.\n"); } - else if (!sharedObjs->p2pAccepted->contains(binIn) && !sharedObjs->p2pPending->contains(binIn)) + else if ((posOfBlock(binIn.data(), p2pAccepted, MAX_P2P_LINKS, BLKSIZE_SESSION_ID) != -1) && + (posOfBlock(binIn.data(), p2pPending, MAX_P2P_LINKS, BLKSIZE_SESSION_ID) != -1)) { - errTxt("err: There is no pending p2p request or p2p connection with the given peer."); + errTxt("err: There is no pending p2p request or p2p connection with the given peer.\n"); } else { - emit toPeer(binIn, QByteArray(), P2P_CLOSE); + QByteArray dst = rdFromBlock(binIn.data(), BLKSIZE_SESSION_ID); + QByteArray src = rdFromBlock(sessionId, BLKSIZE_SESSION_ID); + QByteArray typeBa = wrInt(P2P_CLOSE, 8); + + async(P2P_CLOSE, PUB_IPC, dst + src + typeBa + dst); } } } -void LsP2P::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +QList LsP2P::lsBlocks(const char *blocks, int maxBlocks, int sizeOfBlock) { - Q_UNUSED(binIn); + QList ret; + + QByteArray blank(sizeOfBlock, 0x00); + + for (int i = 0; i < (maxBlocks * sizeOfBlock); i += sizeOfBlock) + { + QByteArray block = rdFromBlock(blocks + i, static_cast(sizeOfBlock)); + + if (block != blank) + { + ret.append(block); + } + } + + return ret; +} + +void LsP2P::procIn(const QByteArray &binIn, quint8 dType) +{ + Q_UNUSED(binIn) if (dType == TEXT) { - QList peerIds = *sharedObjs->p2pAccepted + *sharedObjs->p2pPending; + QList peerIds = lsBlocks(p2pAccepted, MAX_P2P_LINKS, BLKSIZE_SESSION_ID) + + lsBlocks(p2pPending, MAX_P2P_LINKS, BLKSIZE_SESSION_ID); QList tableData; QStringList separators; QList justLens; @@ -132,7 +173,7 @@ void LsP2P::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar QString pending = "0"; QStringList columnData; - if (sharedObjs->p2pPending->contains(peerId)) + if (posOfBlock(peerId.data(), p2pPending, MAX_P2P_LINKS, BLKSIZE_SESSION_ID) != -1) { pending = "1"; } @@ -154,7 +195,7 @@ void LsP2P::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar { for (int i = 0; i < row.size(); ++i) { - mainTxt(row[i].leftJustified(justLens[i] + 2, ' ')); + mainTxt(row[i].leftJustified(justLens[i] + 4, ' ')); } mainTxt("\n"); diff --git a/src/commands/p2p.h b/src/commands/p2p.h index c36e95e..7e347db 100644 --- a/src/commands/p2p.h +++ b/src/commands/p2p.h @@ -18,8 +18,9 @@ // . #include "../common.h" +#include "../cmd_object.h" -class ToPeer : public InternCommand +class ToPeer : public CmdObject { Q_OBJECT @@ -27,14 +28,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit ToPeer(QObject *parent = nullptr); }; //---------------------------------- -class P2PRequest : public InternCommand +class P2PRequest : public CmdObject { Q_OBJECT @@ -42,14 +43,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit P2PRequest(QObject *parent = nullptr); }; //----------------------------------- -class P2POpen : public InternCommand +class P2POpen : public CmdObject { Q_OBJECT @@ -57,14 +58,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit P2POpen(QObject *parent = nullptr); }; //---------------------------------- -class P2PClose : public InternCommand +class P2PClose : public CmdObject { Q_OBJECT @@ -72,22 +73,26 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit P2PClose(QObject *parent = nullptr); }; //---------------------------------- -class LsP2P : public InternCommand +class LsP2P : public CmdObject { Q_OBJECT +private: + + QList lsBlocks(const char* blocks, int maxBlocks, int sizeOfBlock); + public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit LsP2P(QObject *parent = nullptr); }; diff --git a/src/commands/table_viewer.cpp b/src/commands/table_viewer.cpp index 7654f5d..085c669 100644 --- a/src/commands/table_viewer.cpp +++ b/src/commands/table_viewer.cpp @@ -16,200 +16,244 @@ // along with MRCI under the LICENSE.md file. If not, see // . -TableViewer::TableViewer(QObject *parent) : InternCommand(parent) +TableViewer::TableViewer(QObject *parent) : CmdObject(parent) { delOption = false; - term(); + idle(); } -void TableViewer::term() +void TableViewer::idle() { - emit enableMoreInput(false); - - del = false; - offset = 0; - - cachedArgs.clear(); - db.setType(Query::PULL, ""); + flags = 0; + offset = 0; + delMode = false; + condAdded = false; } -void TableViewer::setParams(const QString &tbl, const QStringList &colms, bool allowDel) +void TableViewer::setParams(const QString &mainTbl, bool allowDel) { - columns = colms; - table = tbl; + rdQuery.setType(Query::INNER_JOIN_PULL, mainTbl); + delQuery.setType(Query::DEL, mainTbl); + delOption = allowDel; } -void TableViewer::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void TableViewer::addTableColumn(const QString &tbl, const QString &column) { - Q_UNUSED(sharedObjs); + if (columnType(column) == "BLOB") + { + blobIndexes.append(columns.size()); + } + if (columnRows.isEmpty()) + { + columnRows.append(QStringList()); + columnRows.append(QStringList()); + } + + tables.append(tbl); + columns.append(column); + rdQuery.addTableColumn(tbl, column); + + columnRows[0].append(column); + columnRows[1].append("-------"); +} + +void TableViewer::addJointColumn(const QString &tbl, const QString &column) +{ + rdQuery.addJoinCondition(column, tbl); +} + +void TableViewer::nextPage() +{ + rdQuery.setQueryLimit(MAX_LS_ENTRIES, offset); + rdQuery.exec(); + + dispData(); + + if (rdQuery.rows() == MAX_LS_ENTRIES) + { + offset += MAX_LS_ENTRIES; + + askPage(); + } + else + { + idle(); + } +} + +void TableViewer::addWhereConds(const QStringList &userArgs) +{ + if (delOption) + { + delMode = argExists("-delete", userArgs); + } + + rdQuery.clearConditions(); + delQuery.clearConditions(); + + for (int i = 0; i < columns.size(); ++i) + { + QString value = getParam("-" + columns[i], userArgs); + + if (!value.isEmpty()) + { + if (delMode) + { + mainTxt("delete parameter: " + columns[i] + " ~= " + value + "\n"); + + condAdded = true; + } + + rdQuery.addCondition(columns[i], value, Query::LIKE, tables[i]); + delQuery.addCondition(columns[i], value, Query::LIKE, tables[i]); + } + } + + if (condAdded) + { + mainTxt("\n"); + } +} + +void TableViewer::askDelete() +{ + if (condAdded) + { + mainTxt("This will delete rows from the table according to the parameters above. continue (y/n)?: "); + } + else + { + mainTxt("This will delete all rows in the table. continue (y/n)?: "); + } + + flags |= MORE_INPUT; +} + +void TableViewer::askPage() +{ + mainTxt("\nnext page (y/n)?: "); + + flags |= MORE_INPUT; +} + +QList TableViewer::toStrings(const QList > &data) +{ + QList ret; + + for (auto&& srcRow: data) + { + QStringList dstRow; + + for (int i = 0; i < srcRow.size(); ++i) + { + if (blobIndexes.contains(i)) + { + dstRow.append(srcRow[i].toByteArray().toHex()); + } + else + { + dstRow.append(srcRow[i].toString()); + } + } + + ret.append(dstRow); + } + + return ret; +} + +QList TableViewer::getColumnLens(const QList &data) +{ + QList ret; + + for (int i = 0; i < columns.size(); ++i) + { + ret.append(0); + } + + for (auto&& row: data) + { + for (int i = 0; i < row.size(); ++i) + { + if (row[i].size() >= ret[i]) + { + ret[i] = row[i].size() + 4; + } + } + } + + return ret; +} + +void TableViewer::dispData() +{ + QList tableRows = toStrings(rdQuery.allData()); + QList lens = getColumnLens(columnRows + tableRows); + + for (auto&& row: columnRows + tableRows) + { + for (int i = 0; i < row.size(); ++i) + { + mainTxt(row[i].leftJustified(lens[i], ' ')); + } + + mainTxt("\n"); + } +} + +void TableViewer::procIn(const QByteArray &binIn, quint8 dType) +{ if (dType == TEXT) { - QStringList args = parseArgs(binIn, -1); - bool close = false; - - if (delOption && argExists("-delete", args)) + if (flags & MORE_INPUT) { - del = true; - } + QString text = fromTEXT(binIn).toLower(); - if (moreInputEnabled() && !del) - { - if (args.contains("q", Qt::CaseInsensitive)) + if (text == "y") { - close = true; + if (delMode) + { + delQuery.exec(); + + mainTxt(delQuery.errDetail()); + idle(); + onDel(); + } + else + { + nextPage(); + } + } + else if (text == "n") + { + idle(); } else { - offset += MAX_LS_ENTRIES; - args = cachedArgs; - } - } - - if (close) - { - term(); - } - else if (moreInputEnabled() && del) - { - QString ans = fromTEXT(binIn); - - if (noCaseMatch("y", ans)) - { - db.exec(); - - term(); - } - else if (noCaseMatch("n", ans)) - { - term(); - } - else - { - mainTxt("continue? (y/n): "); + if (delMode) + { + askDelete(); + } + else + { + askPage(); + } } } else { - if (del) + addWhereConds(parseArgs(binIn, -1)); + + if (delMode) { - db.setType(Query::DEL, table); + askDelete(); } else { - db.setType(Query::PULL, table); - db.setQueryLimit(MAX_LS_ENTRIES, offset); - } - - bool noDelParams = true; - - for (int i = 0; i < columns.size(); ++i) - { - if (!del) db.addColumn(columns[i]); - - QString param = getParam("-" + columns[i], args); - - if (!param.isEmpty()) - { - if (del) - { - if (noDelParams) mainTxt("\n"); - - mainTxt("delete param: " + columns[i] + " = " + param + "\n"); - - noDelParams = false; - } - - db.addCondition(columns[i], param, Query::LIKE); - } - } - - if (del) - { - emit enableMoreInput(true); - - if (noDelParams) mainTxt("\nabout to delete ALL entries in this table.\n"); - - mainTxt("\ncontinue? (y/n): "); - } - else - { - db.exec(); - - QStringList separators; - QList tableData; - QList justLens; - - for (int i = 0; i < columns.size(); ++i) - { - justLens.append(0); - separators.append("-------"); - } - - for (int i = 0; i < db.rows() + 2; ++i) - { - QStringList columnData; - - if (i == 0) - { - columnData = columns; - } - else if (i == 1) - { - columnData = separators; - } - else - { - for (int j = 0; j < columns.size(); ++j) - { - if (columnType(columns[j]) == "BLOB") - { - columnData.append(db.getData(columns[j], i - 2).toByteArray().toHex()); - } - else - { - columnData.append(db.getData(columns[j], i - 2).toString()); - } - } - } - - for (int k = 0; k < justLens.size(); ++k) - { - if (justLens[k] < columnData[k].size()) justLens[k] = columnData[k].size(); - } - - tableData.append(columnData); - } - - mainTxt("\n"); - - for (auto&& row : tableData) - { - for (int i = 0; i < row.size(); ++i) - { - mainTxt(row[i].leftJustified(justLens[i] + 2, ' ')); - } - - mainTxt("\n"); - } - - if (db.rows() == MAX_LS_ENTRIES) - { - if (!moreInputEnabled()) - { - cachedArgs = args; - - emit enableMoreInput(true); - } - - mainTxt("\n[enter or any] more, [q] close: "); - } - else - { - term(); - } + nextPage(); } } } diff --git a/src/commands/table_viewer.h b/src/commands/table_viewer.h index 3f3692e..b9aa485 100644 --- a/src/commands/table_viewer.h +++ b/src/commands/table_viewer.h @@ -18,26 +18,42 @@ // . #include "../common.h" +#include "../cmd_object.h" -class TableViewer : public InternCommand +class TableViewer : public CmdObject { Q_OBJECT protected: - Query db; - QString table; - QStringList columns; - QStringList cachedArgs; - bool delOption; - bool del; - uint offset; + Query rdQuery; + Query delQuery; + bool condAdded; + bool delOption; + bool delMode; + quint32 offset; + QList columns; + QList tables; + QList blobIndexes; + QList columnRows; + + QList toStrings(const QList > &data); + QList getColumnLens(const QList &data); + void addWhereConds(const QStringList &userArgs); + void askDelete(); + void askPage(); + void dispData(); + + virtual void onDel() {} public: - void term(); - void setParams(const QString &tbl, const QStringList &colms, bool allowDel); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void idle(); + void nextPage(); + void addJointColumn(const QString &tbl, const QString &column); + void addTableColumn(const QString &tbl, const QString &column); + void setParams(const QString &mainTbl, bool allowDel); + void procIn(const QByteArray &binIn, quint8 dType); explicit TableViewer(QObject *parent = nullptr); }; diff --git a/src/commands/users.cpp b/src/commands/users.cpp index 7a73c8c..aaca888 100644 --- a/src/commands/users.cpp +++ b/src/commands/users.cpp @@ -18,26 +18,30 @@ ListUsers::ListUsers(QObject *parent) : TableViewer(parent) { - setParams(TABLE_USERS, QStringList() << COLUMN_TIME << COLUMN_USERNAME << COLUMN_GRNAME << COLUMN_USER_ID, false); + setParams(TABLE_USERS, false); + addTableColumn(TABLE_USERS, COLUMN_TIME); + addTableColumn(TABLE_USERS, COLUMN_USERNAME); + addTableColumn(TABLE_USERS, COLUMN_HOST_RANK); + addTableColumn(TABLE_USERS, COLUMN_USER_ID); } -LockUser::LockUser(QObject *parent) : InternCommand(parent) {} -CreateUser::CreateUser(QObject *parent) : InternCommand(parent) {} -RemoveUser::RemoveUser(QObject *parent) : InternCommand(parent) {} -ChangeGroup::ChangeGroup(QObject *parent) : InternCommand(parent) {} -ChangePassword::ChangePassword(QObject *parent) : InternCommand(parent) {} -ChangeDispName::ChangeDispName(QObject *parent) : InternCommand(parent) {} -ChangeUsername::ChangeUsername(QObject *parent) : InternCommand(parent) {} -OverWriteEmail::OverWriteEmail(QObject *parent) : InternCommand(parent) {} +LockUser::LockUser(QObject *parent) : CmdObject(parent) {} +CreateUser::CreateUser(QObject *parent) : CmdObject(parent) {} +RemoveUser::RemoveUser(QObject *parent) : CmdObject(parent) {} +ChangeUserRank::ChangeUserRank(QObject *parent) : CmdObject(parent) {} +ChangePassword::ChangePassword(QObject *parent) : CmdObject(parent) {} +ChangeDispName::ChangeDispName(QObject *parent) : CmdObject(parent) {} +ChangeUsername::ChangeUsername(QObject *parent) : CmdObject(parent) {} +OverWriteEmail::OverWriteEmail(QObject *parent) : CmdObject(parent) {} ChangeEmail::ChangeEmail(QObject *parent) : OverWriteEmail(parent) {} -PasswordChangeRequest::PasswordChangeRequest(QObject *parent) : InternCommand(parent) {} -NameChangeRequest::NameChangeRequest(QObject *parent) : InternCommand(parent) {} +PasswordChangeRequest::PasswordChangeRequest(QObject *parent) : CmdObject(parent) {} +NameChangeRequest::NameChangeRequest(QObject *parent) : PasswordChangeRequest(parent) {} QString ListUsers::cmdName() {return "ls_users";} QString LockUser::cmdName() {return "lock_acct";} QString CreateUser::cmdName() {return "add_acct";} QString RemoveUser::cmdName() {return "rm_acct";} -QString ChangeGroup::cmdName() {return "set_group";} +QString ChangeUserRank::cmdName() {return "set_user_rank";} QString ChangePassword::cmdName() {return "set_pw";} QString ChangeDispName::cmdName() {return "set_disp_name";} QString ChangeUsername::cmdName() {return "set_user_name";} @@ -46,13 +50,33 @@ QString ChangeEmail::cmdName() {return "set_email";} QString PasswordChangeRequest::cmdName() {return "request_new_pw";} QString NameChangeRequest::cmdName() {return "request_new_user_name";} -void LockUser::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +bool canModifyUser(const QByteArray &uId, quint32 myRank, bool equalAcceptable) +{ + Query db; + + db.setType(Query::PULL, TABLE_USERS); + db.addColumn(COLUMN_HOST_RANK); + db.addCondition(COLUMN_USER_ID, uId); + db.exec(); + + if (equalAcceptable) + { + return myRank <= db.getData(COLUMN_HOST_RANK).toUInt(); + } + else + { + return myRank < db.getData(COLUMN_HOST_RANK).toUInt(); + } +} + +void LockUser::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 4); QString uName = getParam("-user", args); QString state = getParam("-state", args); + QByteArray uId; if (uName.isEmpty()) { @@ -66,21 +90,17 @@ void LockUser::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uc { errTxt("err: Invalid user name.\n"); } - else if (noCaseMatch(ROOT_USER, uName)) - { - errTxt("err: Unable to lock/unlock protected user: '" + QString(ROOT_USER) + "'\n"); - } else if (!isBool(state)) { errTxt("err: The state bool value (-state) must be a 0 or 1.\n"); } - else if (!userExists(uName)) + else if (!userExists(uName, &uId)) { errTxt("err: The requested user name does not exists.\n"); } - else if (!checkRank(*sharedObjs->groupName, getUserGroup(uName))) + else if (!canModifyUser(uId, rd32BitFromBlock(hostRank), false)) { - errTxt("err: The target user account out ranks or is equal to your own rank. access denied.\n"); + errTxt("err: The target user account out ranks you or is equal to your own rank. access denied.\n"); } else { @@ -88,33 +108,31 @@ void LockUser::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uc db.setType(Query::UPDATE, TABLE_USERS); db.addColumn(COLUMN_LOCKED, static_cast(state.toInt())); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); } } } -void CreateUser::term() +void CreateUser::clear() { - emit enableMoreInput(false); + flags = 0; email.clear(); newName.clear(); } -void CreateUser::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void CreateUser::procIn(const QByteArray &binIn, quint8 dType) { - Q_UNUSED(sharedObjs); - if (dType == TEXT) { - if (moreInputEnabled()) + if (flags & MORE_INPUT) { QString password = fromTEXT(binIn); if (password.isEmpty()) { - term(); + clear(); } else if (!validPassword(password)) { @@ -124,16 +142,16 @@ void CreateUser::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, else if (!createUser(newName, email, dispName, password)) { errTxt("err: The requested User name already exists.\n"); - term(); + clear(); } else { - term(); + clear(); } } else { - QStringList args = parseArgs(binIn, 4); + QStringList args = parseArgs(binIn, 6); dispName = getParam("-disp", args); newName = getParam("-name", args); @@ -169,7 +187,7 @@ void CreateUser::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } else { - emit enableMoreInput(true); + flags |= MORE_INPUT; privTxt("Enter a new password (leave blank to cancel): "); } @@ -177,38 +195,31 @@ void CreateUser::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } } -void RemoveUser::term() -{ - emit enableMoreInput(false); - - uName.clear(); -} - void RemoveUser::rm() { Query db; db.setType(Query::DEL, TABLE_USERS); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); - emit backendDataOut(ASYNC_USER_DELETED, toTEXT(uName), PUB_IPC_WITH_FEEDBACK); + flags &= ~MORE_INPUT; - term(); + async(ASYNC_USER_DELETED, PUB_IPC_WITH_FEEDBACK, uId); } void RemoveUser::ask() { - emit enableMoreInput(true); + flags |= MORE_INPUT; mainTxt("Are you sure you want to permanently remove this user account? (y/n): "); } -void RemoveUser::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void RemoveUser::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { - if (moreInputEnabled()) + if (flags & MORE_INPUT) { QString ans = fromTEXT(binIn); @@ -218,7 +229,7 @@ void RemoveUser::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } else if (noCaseMatch("n", ans)) { - term(); + flags &= ~MORE_INPUT; } else { @@ -227,9 +238,8 @@ void RemoveUser::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } else { - QStringList args = parseArgs(binIn, 2); - - uName = getParam("-name", args); + QStringList args = parseArgs(binIn, 2); + QString uName = getParam("-name", args); if (uName.isEmpty()) { @@ -243,15 +253,15 @@ void RemoveUser::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, { errTxt("err: Invalid username.\n"); } - else if (!userExists(uName)) + else if (!userExists(uName, &uId)) { errTxt("err: The requested user name does not exists.\n"); } - else if (isChOwner(uName)) + else if (isChOwner(uId)) { - errTxt("err: The requested user name is the owner of one or more channels.\n"); + errTxt("err: The requested user name is the owner of one or more channels. assign new owners for these channels before attempting to delete this account.\n"); } - else if (!checkRank(*sharedObjs->groupName, getUserGroup(uName)) && !noCaseMatch(*sharedObjs->userName, uName)) + else if (!canModifyUser(uId, rd32BitFromBlock(hostRank), false) && (rdFromBlock(userId, BLKSIZE_USER_ID) != uId)) { errTxt("err: The target user account out ranks you, access denied.\n"); } @@ -270,76 +280,76 @@ void RemoveUser::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, } } -void ChangeGroup::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ChangeUserRank::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { - QStringList args = parseArgs(binIn, 4); - - QString uName = getParam("-user", args); - QString gName = getParam("-group", args); + QStringList args = parseArgs(binIn, 4); + QString uName = getParam("-user", args); + QString rank = getParam("-rank", args); + QByteArray uId; if (uName.isEmpty()) { - errTxt("err: User name argument -user not found or is empty.\n"); + errTxt("err: User name argument (-user) not found or is empty.\n"); } - else if (gName.isEmpty()) + else if (rank.isEmpty()) { - errTxt("err: Group name argument -group not found or is empty.\n"); + errTxt("err: New rank argument (-rank) not found or is empty.\n"); } else if (noCaseMatch(ROOT_USER, uName)) { - errTxt("err: You are not allowed to change the group of protected user: '" + QString(ROOT_USER) + "'\n"); - } - else if (noCaseMatch(ROOT_USER, gName)) - { - errTxt("err: No user created account is allowed to attach to protected group: '" + QString(ROOT_USER) + "'\n"); - } - else if (!validGroupName(gName)) - { - errTxt("err: Invalid group name.\n"); + errTxt("err: You are not allowed to change the rank of protected user: '" + QString(ROOT_USER) + "'\n"); } else if (!validUserName(uName)) { errTxt("err: Invalid username.\n"); } - else if (!userExists(uName)) + else if (!isInt(rank)) + { + errTxt("err: Invalid 32bit unsigned integer for the new rank.\n"); + } + else if (rank.toUInt() == 0) + { + errTxt("err: Rank 0 is invalid. please set a rank of 1 or higher.\n"); + } + else if (rank.toUInt() < rd32BitFromBlock(hostRank)) + { + errTxt("err: You cannot assign a rank higher than your own.\n"); + } + else if (!userExists(uName, &uId)) { errTxt("err: The requested user account does not exists.\n"); } - else if (!groupExists(gName)) + else if (!canModifyUser(uId, rd32BitFromBlock(hostRank), false)) { - errTxt("err: The requested group does not exists.\n"); - } - else if (!checkRank(*sharedObjs->groupName, gName, true)) - { - errTxt("err: The target group out ranks your own group. access denied.\n"); + errTxt("err: The target user out ranks you or is equal to your own rank. access denied.\n"); } else { Query db(this); db.setType(Query::UPDATE, TABLE_USERS); - db.addColumn(COLUMN_GRNAME, gName); - db.addCondition(COLUMN_USERNAME, uName); + db.addColumn(COLUMN_HOST_RANK, rank.toUInt()); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); - emit backendDataOut(ASYNC_USER_GROUP_CHANGED, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_USER_RANK_CHANGED, PUB_IPC_WITH_FEEDBACK, uId + wrInt(rank.toUInt(), 32)); } } } -void ChangePassword::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ChangePassword::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { - if (moreInputEnabled()) + if (flags & MORE_INPUT) { QString password = fromTEXT(binIn); if (password.isEmpty()) { - emit enableMoreInput(false); + flags &= ~MORE_INPUT; } else if (!validPassword(password)) { @@ -348,21 +358,21 @@ void ChangePassword::procBin(const SharedObjs *sharedObjs, const QByteArray &bin } else { - emit enableMoreInput(false); + flags &= ~MORE_INPUT; - updatePassword(*sharedObjs->userName, password, TABLE_USERS); + updatePassword(rdFromBlock(userId, BLKSIZE_USER_ID), password, TABLE_USERS); } } else { - emit enableMoreInput(true); + flags |= MORE_INPUT; privTxt("Enter a new password (leave blank to cancel): "); } } } -void ChangeUsername::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ChangeUsername::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { @@ -383,31 +393,31 @@ void ChangeUsername::procBin(const SharedObjs *sharedObjs, const QByteArray &bin } else { + QByteArray uId = rdFromBlock(userId, BLKSIZE_USER_ID); + QByteArray newNameBa = fixedToTEXT(newName, BLKSIZE_USER_NAME); + Query db(this); db.setType(Query::UPDATE, TABLE_USERS); db.addColumn(COLUMN_USERNAME, newName); - db.addCondition(COLUMN_USERNAME, *sharedObjs->userName); + db.addCondition(COLUMN_USER_ID, rdFromBlock(userId, BLKSIZE_USER_ID)); db.exec(); - args.append("-old"); - args.append("'" + escapeChars(*sharedObjs->userName, '\\', '\'') + "'"); - - emit backendDataOut(ASYNC_USER_RENAMED, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_USER_RENAMED, PUB_IPC_WITH_FEEDBACK, uId + newNameBa); } } } -void ChangeDispName::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ChangeDispName::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); - QString name = getParam("-name", args); + QString name = getParam("-new_name", args).trimmed(); - if (name.isEmpty()) + if (argExists("-new_name", args)) { - errTxt("err: New display name argument (-name) not found or is empty.\n"); + errTxt("err: New display name argument (-new_name) not found.\n"); } else if (!validDispName(name)) { @@ -417,21 +427,23 @@ void ChangeDispName::procBin(const SharedObjs *sharedObjs, const QByteArray &bin { Query db(this); + QByteArray uId = rdFromBlock(userId, BLKSIZE_USER_ID); + QByteArray newNameBa = fixedToTEXT(name, BLKSIZE_DISP_NAME); + db.setType(Query::UPDATE, TABLE_USERS); db.addColumn(COLUMN_DISPLAY_NAME, name); - db.addCondition(COLUMN_USERNAME, *sharedObjs->userName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); - args.append("-user"); - args.append("'" + escapeChars(*sharedObjs->userName, '\\', '\'') + "'"); - - emit backendDataOut(ASYNC_DISP_RENAMED, toTEXT(args.join(' ')), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_DISP_RENAMED, PUB_IPC_WITH_FEEDBACK, uId + newNameBa); } } } -void OverWriteEmail::procArgs(const QString &uName, const QString &newEmail, bool sameRank, const SharedObjs *sharedObjs) +void OverWriteEmail::procArgs(const QString &uName, const QString &newEmail, bool sameRank) { + QByteArray uId; + if (newEmail.isEmpty()) { errTxt("err: New email address (-new_email) argument was not found or is empty.\n"); @@ -452,13 +464,13 @@ void OverWriteEmail::procArgs(const QString &uName, const QString &newEmail, boo { errTxt("err: The requested email address is already in use.\n"); } - else if (!userExists(uName)) + else if (!userExists(uName, &uId)) { errTxt("err: The requested user account does not exists.\n"); } - else if (!checkRank(*sharedObjs->groupName, getUserGroup(uName), sameRank)) + else if (!canModifyUser(uId, rd32BitFromBlock(hostRank), sameRank)) { - errTxt("err: The target user account out ranks your own rank. access denied.\n"); + errTxt("err: Access denied.\n"); } else { @@ -467,14 +479,14 @@ void OverWriteEmail::procArgs(const QString &uName, const QString &newEmail, boo db.setType(Query::UPDATE, TABLE_USERS); db.addColumn(COLUMN_EMAIL, newEmail); db.addColumn(COLUMN_EMAIL_VERIFIED, false); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); - emit backendDataOut(ASYNC_RW_MY_INFO, toTEXT(uName), PUB_IPC_WITH_FEEDBACK); + async(ASYNC_RW_MY_INFO, PUB_IPC_WITH_FEEDBACK, uId); } } -void OverWriteEmail::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void OverWriteEmail::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { @@ -482,30 +494,39 @@ void OverWriteEmail::procBin(const SharedObjs *sharedObjs, const QByteArray &bin QString uName = getParam("-user", args); QString newEmail = getParam("-new_email", args); - procArgs(uName, newEmail, false, sharedObjs); + procArgs(uName, newEmail, false); } } -void ChangeEmail::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void ChangeEmail::procIn(const QByteArray &binIn, quint8 dType) { if (dType == TEXT) { QStringList args = parseArgs(binIn, 2); QString newEmail = getParam("-new_email", args); - procArgs(*sharedObjs->userName, newEmail, true, sharedObjs); + procArgs(rdStringFromBlock(userName, BLKSIZE_USER_NAME), newEmail, true); } } -void PasswordChangeRequest::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void PasswordChangeRequest::exec(const QByteArray &uId, bool req) { - Q_UNUSED(sharedObjs); + Query db(this); + db.setType(Query::UPDATE, TABLE_USERS); + db.addColumn(COLUMN_NEED_PASS, req); + db.addCondition(COLUMN_USER_ID, uId); + db.exec(); +} + +void PasswordChangeRequest::procIn(const QByteArray &binIn, quint8 dType) +{ if (dType == TEXT) { QStringList args = parseArgs(binIn, 4); QString uName = getParam("-user", args); QString req = getParam("-req", args); + QByteArray uId; if (uName.isEmpty()) { @@ -523,68 +544,27 @@ void PasswordChangeRequest::procBin(const SharedObjs *sharedObjs, const QByteArr { errTxt("err: Invalid user name.\n"); } - else if (!userExists(uName)) + else if (!userExists(uName, &uId)) { errTxt("err: The requested user account does not exists.\n"); } - else if (!checkRank(*sharedObjs->groupName, getUserGroup(uName))) + else if (!canModifyUser(uId, rd32BitFromBlock(hostRank), false)) { errTxt("err: The target user account out ranks or is equal to your own rank. access denied.\n"); } else { - Query db(this); - - db.setType(Query::UPDATE, TABLE_USERS); - db.addColumn(COLUMN_NEED_PASS, static_cast(req.toInt())); - db.addCondition(COLUMN_USERNAME, uName); - db.exec(); + exec(uId, static_cast(req.toUInt())); } } } -void NameChangeRequest::procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType) +void NameChangeRequest::exec(const QByteArray &uId, bool req) { - Q_UNUSED(sharedObjs); + Query db(this); - if (dType == TEXT) - { - QStringList args = parseArgs(binIn, 4); - QString uName = getParam("-user", args); - QString req = getParam("-req", args); - - if (uName.isEmpty()) - { - errTxt("err: User name (-user) argument was not found or is empty.\n"); - } - else if (req.isEmpty()) - { - errTxt("err: Request bool (-req) argument was not found or is empty.\n"); - } - else if (!isBool(req)) - { - errTxt("err: The request bool value (-req) must be a 0 or 1.\n"); - } - else if (!validUserName(uName)) - { - errTxt("err: Invalid user name.\n"); - } - else if (!userExists(uName)) - { - errTxt("err: The requested user account does not exists.\n"); - } - else if (!checkRank(*sharedObjs->groupName, getUserGroup(uName))) - { - errTxt("err: The target user account out ranks or is equal to your own rank. access denied.\n"); - } - else - { - Query db(this); - - db.setType(Query::UPDATE, TABLE_USERS); - db.addColumn(COLUMN_NEED_NAME, static_cast(req.toInt())); - db.addCondition(COLUMN_USERNAME, uName); - db.exec(); - } - } + db.setType(Query::UPDATE, TABLE_USERS); + db.addColumn(COLUMN_NEED_NAME, req); + db.addCondition(COLUMN_USER_ID, uId); + db.exec(); } diff --git a/src/commands/users.h b/src/commands/users.h index 1b8471a..0678e62 100644 --- a/src/commands/users.h +++ b/src/commands/users.h @@ -18,8 +18,11 @@ // . #include "../common.h" +#include "../cmd_object.h" #include "table_viewer.h" +bool canModifyUser(const QByteArray &uId, quint32 myRank, bool equalAcceptable); + class ListUsers : public TableViewer { Q_OBJECT @@ -33,7 +36,7 @@ public: //------------------------------------ -class LockUser : public InternCommand +class LockUser : public CmdObject { Q_OBJECT @@ -41,14 +44,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit LockUser(QObject *parent = nullptr); }; //------------------------------------ -class CreateUser : public InternCommand +class CreateUser : public CmdObject { Q_OBJECT @@ -62,21 +65,21 @@ public: static QString cmdName(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void clear(); + void procIn(const QByteArray &binIn, quint8 dType); explicit CreateUser(QObject *parent = nullptr); }; //----------------------------------- -class RemoveUser : public InternCommand +class RemoveUser : public CmdObject { Q_OBJECT private: - QString uName; + QByteArray uId; void rm(); void ask(); @@ -85,15 +88,14 @@ public: static QString cmdName(); - void term(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit RemoveUser(QObject *parent = nullptr); }; //----------------------------------- -class ChangeGroup : public InternCommand +class ChangeUserRank : public CmdObject { Q_OBJECT @@ -101,14 +103,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); - explicit ChangeGroup(QObject *parent = nullptr); + explicit ChangeUserRank(QObject *parent = nullptr); }; //--------------------------------- -class ChangePassword : public InternCommand +class ChangePassword : public CmdObject { Q_OBJECT @@ -116,14 +118,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit ChangePassword(QObject *parent = nullptr); }; //------------------------------- -class ChangeUsername : public InternCommand +class ChangeUsername : public CmdObject { Q_OBJECT @@ -131,14 +133,14 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit ChangeUsername(QObject *parent = nullptr); }; //----------------------------- -class ChangeDispName : public InternCommand +class ChangeDispName : public CmdObject { Q_OBJECT @@ -146,26 +148,26 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit ChangeDispName(QObject *parent = nullptr); }; //----------------------------- -class OverWriteEmail : public InternCommand +class OverWriteEmail : public CmdObject { Q_OBJECT protected: - void procArgs(const QString &uName, const QString &newEmail, bool sameRank, const SharedObjs *sharedObjs); + void procArgs(const QString &uName, const QString &newEmail, bool sameRank); public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit OverWriteEmail(QObject *parent = nullptr); }; @@ -180,38 +182,44 @@ public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit ChangeEmail(QObject *parent = nullptr); }; //----------------------------- -class PasswordChangeRequest : public InternCommand +class PasswordChangeRequest : public CmdObject { Q_OBJECT +protected: + + virtual void exec(const QByteArray &uId, bool req); + public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); + void procIn(const QByteArray &binIn, quint8 dType); explicit PasswordChangeRequest(QObject *parent = nullptr); }; //---------------------------- -class NameChangeRequest : public InternCommand +class NameChangeRequest : public PasswordChangeRequest { Q_OBJECT +private: + + void exec(const QByteArray &uId, bool req); + public: static QString cmdName(); - void procBin(const SharedObjs *sharedObjs, const QByteArray &binIn, uchar dType); - explicit NameChangeRequest(QObject *parent = nullptr); }; diff --git a/src/common.cpp b/src/common.cpp index 18a50af..ce9239a 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -16,11 +16,6 @@ // along with MRCI under the LICENSE.md file. If not, see // . -QString sessionCountShareKey() -{ - return QString(APP_NAME) + ".SessionCount"; -} - QString boolStr(bool state) { QString ret; @@ -31,43 +26,11 @@ QString boolStr(bool state) return ret; } -uint rdSessionLoad() -{ - uint ret = 0; - - QSharedMemory mem(sessionCountShareKey()); - - if (mem.attach(QSharedMemory::ReadOnly)) - { - mem.lock(); - - memcpy(&ret, mem.data(), 4); - - mem.unlock(); - mem.detach(); - } - - return ret; -} - -void wrSessionLoad(uint value) -{ - QSharedMemory mem(sessionCountShareKey()); - - if (mem.attach(QSharedMemory::ReadWrite)) - { - mem.lock(); - - memcpy(mem.data(), &value, 4); - - mem.unlock(); - mem.detach(); - } -} - QString genSerialNumber() { - return QDateTime::currentDateTime().toString("yyyyMMddHHmmsszzz"); + Serial::serialIndex++; + + return QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch()) + "-" + QString::number(Serial::serialIndex); } void serializeThread(QThread *thr) @@ -75,144 +38,26 @@ void serializeThread(QThread *thr) thr->setObjectName(genSerialNumber()); } -QByteArray wrFrame(quint16 cmdId, const QByteArray &data, uchar dType) +quint32 toCmdId32(quint16 cmdId, quint16 branchId) { - QByteArray cmdBa = wrInt(cmdId, 16); - QByteArray typeBa = wrInt(dType, 8); - QByteArray sizeBa = wrInt(data.size(), MAX_FRAME_BITS); + quint32 ret = 0; + quint32 *dst = &ret; - return typeBa + cmdBa + sizeBa + data; -} - -QByteArray wrInt(quint64 num, int numOfBits) -{ - QByteArray ret(numOfBits / 8, static_cast(0)); - - num = qToLittleEndian(num); - - memcpy(ret.data(), &num, static_cast(ret.size())); + memcpy(dst, &cmdId, 2); + memcpy(dst + 2, &branchId, 2); return ret; } -QByteArray wrInt(qint64 num, int numOfBits) +quint16 toCmdId16(quint32 id) { - return wrInt(static_cast(num), numOfBits); -} + quint16 ret = 0; -QByteArray wrInt(int num, int numOfBits) -{ - return wrInt(static_cast(num), numOfBits); -} - -QByteArray wrInt(uint num, int numOfBits) -{ - return wrInt(static_cast(num), numOfBits); -} - -QByteArray toFILE_INFO(const QFileInfo &info) -{ - // this function converts some information extracted from a QFileInfo object to - // a FILE_INFO frame. - - // format: [1byte(flags)][8bytes(createTime)][8bytes(modTime)][8bytes(fileSize)] - // [TEXT(fileName)][TEXT(symLinkTarget)] - - // note: the TEXT strings are 16bit NULL terminated meaning 2 bytes of 0x00 - // indicate the end of the string. - - // note: the integer data found in flags, modTime, createTime and fileSize - // are formatted in little endian byte order (unsigned). - - char flags = 0; - - if (info.isFile()) flags |= IS_FILE; - if (info.isDir()) flags |= IS_DIR; - if (info.isSymLink()) flags |= IS_SYMLNK; - if (info.isReadable()) flags |= CAN_READ; - if (info.isWritable()) flags |= CAN_WRITE; - if (info.isExecutable()) flags |= CAN_EXE; - if (info.exists()) flags |= EXISTS; - - QByteArray ret; - QByteArray strTerm(2, 0); - - ret.append(flags); - ret.append(wrInt(info.birthTime().toMSecsSinceEpoch(), 64)); - ret.append(wrInt(info.lastModified().toMSecsSinceEpoch(), 64)); - ret.append(wrInt(info.size(), 64)); - ret.append(toTEXT(info.fileName()) + strTerm); - ret.append(toTEXT(info.symLinkTarget() + strTerm)); + memcpy(&ret, &id, 2); return ret; } -QByteArray toFILE_INFO(const QString &path) -{ - return toFILE_INFO(QFileInfo(path)); -} - -QByteArray toPEER_INFO(const SharedObjs *sharedObjs) -{ - return *sharedObjs->sessionId + - *sharedObjs->userId + - fixedToTEXT(*sharedObjs->userName, 24) + - fixedToTEXT(*sharedObjs->appName, 64) + - fixedToTEXT(*sharedObjs->displayName, 32); -} - -QByteArray toNEW_CMD(quint16 cmdId, const QString &cmdName, ExternCommand *cmdObj) -{ - QByteArray idBa = wrInt(cmdId, 16); - QByteArray genBa = wrInt(0, 8); - - if (cmdObj->handlesGenfile()) - { - genBa = wrInt(1, 8); - } - - return idBa + genBa + fixedToTEXT(cmdName, 64) + fixedToTEXT(cmdObj->libText(), 64); -} - -QByteArray toMY_INFO(const SharedObjs *sharedObjs) -{ - Query db; - - db.setType(Query::PULL, TABLE_USERS); - db.addColumn(COLUMN_EMAIL); - db.addColumn(COLUMN_EMAIL_VERIFIED); - db.addCondition(COLUMN_USERNAME, *sharedObjs->userName); - db.exec(); - - QByteArray confirmed; - - if (db.getData(COLUMN_EMAIL_VERIFIED).toBool()) - { - confirmed.append(static_cast(0x01)); - } - else - { - confirmed.append(static_cast(0x00)); - } - - return toPEER_INFO(sharedObjs) + - fixedToTEXT(db.getData(COLUMN_EMAIL).toString(), 64) + - fixedToTEXT(*sharedObjs->groupName, 12) + - confirmed; -} - -QByteArray toPEER_STAT(const QByteArray &sesId, const QByteArray &chIds, bool isDisconnecting) -{ - if (isDisconnecting) - { - return sesId + chIds + QByteArray(1, 0x01); - } - else - { - return sesId + chIds + QByteArray(1, 0x00); - } -} - QByteArray toTEXT(const QString &txt) { QByteArray ret = QTextCodec::codecForName(TXT_CODEC)->fromUnicode(txt); @@ -222,21 +67,21 @@ QByteArray toTEXT(const QString &txt) QByteArray fixedToTEXT(const QString &txt, int len) { - return toTEXT(txt.leftJustified(len, ' ', true)); + return toTEXT(txt).leftJustified(len, 0, true); +} + +QByteArray nullTermTEXT(const QString &txt) +{ + return toTEXT(txt) + QByteArray(2, 0x00); } QString fromTEXT(const QByteArray &txt) { - return QTextCodec::codecForName(TXT_CODEC)->toUnicode(txt); -} + QByteArray ba = txt; -quint64 rdInt(const QByteArray &bytes) -{ - quint64 ret = 0; + ba.replace(QByteArray(2, 0x00), QByteArray()); - memcpy(&ret, bytes.data(), static_cast(bytes.size())); - - return qFromLittleEndian(ret); + return QTextCodec::codecForName(TXT_CODEC)->toUnicode(ba); } bool noCaseMatch(const QString &strA, const QString &strB) @@ -275,7 +120,7 @@ bool validUserName(const QString &uName) { bool ret = false; - if ((uName.size() >= 2) && (uName.size() <= 24)) + if ((uName.size() >= 2) && ((uName.size() * 2) <= BLKSIZE_USER_NAME)) { ret = !uName.contains(' ') && !containsNewLine(uName); } @@ -287,7 +132,7 @@ bool validCommonName(const QString &name) { bool ret = false; - if ((name.size() >= 1) && (name.size() <= 200)) + if ((name.size() >= 1) && (name.size() <= 136)) { ret = !name.contains(' ') && !containsNewLine(name); } @@ -312,38 +157,13 @@ bool validEmailAddr(const QString &email) return ret; } -bool validGroupName(const QString &grName) +bool validCommandName(const QString &name) { bool ret = false; - if ((grName.size() >= 1) && (grName.size() <= 12)) + if ((name.size() >= 1) && (name.size() <= 64)) { - ret = !grName.contains(' ') && !containsNewLine(grName); - } - - return ret; -} - -bool validCommandName(const QString &name) -{ - bool ret = true; - - if ((name.size() >= 1) && (name.size() <= 64) && !name.contains(' ')) - { - for (auto&& chr : name) - { - if (!chr.isNumber() && - !chr.isLetter() && - !(chr == '_') && - !(chr == '?')) - { - ret = false; break; - } - } - } - else - { - ret = false; + ret = !name.contains(' ') && !containsNewLine(name); } return ret; @@ -351,7 +171,7 @@ bool validCommandName(const QString &name) bool validDispName(const QString &name) { - return (name.size() <= 32) && !containsNewLine(name); + return ((name.size() * 2) <= BLKSIZE_DISP_NAME) && !containsNewLine(name); } bool validChName(const QString &name) @@ -385,6 +205,24 @@ bool validLevel(const QString &num, bool includePub) return ret; } +bool validModPath(const QString &modPath) +{ + bool ret = true; + QString forbidden = "|*:\"?<>"; + + for (auto&& chr : forbidden) + { + if (modPath.contains(chr)) + { + ret = false; + + break; + } + } + + return ret; +} + bool validPassword(const QString &pw) { bool ret = false; @@ -453,66 +291,65 @@ bool matchedVolume(const QString &pathA, const QString &pathB) return storA.device() == storB.device(); } -void mkPathForFile(const QString &path) -{ - mkPath(QFileInfo(path).absolutePath()); -} - -bool userExists(const QString &uName) +bool userExists(const QString &uName, QByteArray *uId, QString *email) { Query db; db.setType(Query::PULL, TABLE_USERS); - db.addColumn(COLUMN_USERNAME); + db.addColumn(COLUMN_USER_ID); + db.addColumn(COLUMN_EMAIL); db.addCondition(COLUMN_USERNAME, uName); db.exec(); + if (db.rows() && (uId != nullptr)) + { + *uId = db.getData(COLUMN_USER_ID).toByteArray(); + } + + if (db.rows() && (email != nullptr)) + { + *email = db.getData(COLUMN_EMAIL).toString(); + } + return db.rows(); } -bool recoverPWExists(const QString &uName) +bool recoverPWExists(const QByteArray &uId) { Query db; db.setType(Query::PULL, TABLE_PW_RECOVERY); - db.addColumn(COLUMN_USERNAME); - db.addCondition(COLUMN_USERNAME, uName); + db.addColumn(COLUMN_USER_ID); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); return db.rows(); } -bool emailExists(const QString &email) +bool emailExists(const QString &email, QByteArray *uId) { Query db; db.setType(Query::PULL, TABLE_USERS); - db.addColumn(COLUMN_EMAIL); + db.addColumn(COLUMN_USER_ID); db.addCondition(COLUMN_EMAIL, email); db.exec(); - return db.rows(); -} - -bool groupExists(const QString &grName) -{ - Query db; - - db.setType(Query::PULL, TABLE_GROUPS); - db.addColumn(COLUMN_GRNAME); - db.addCondition(COLUMN_GRNAME, grName); - db.exec(); + if (db.rows() && (uId != nullptr)) + { + *uId = db.getData(COLUMN_USER_ID).toByteArray(); + } return db.rows(); } -bool modExists(const QString &modName) +bool modExists(const QString &modPath) { Query db; db.setType(Query::PULL, TABLE_MODULES); - db.addColumn(COLUMN_MOD_NAME); - db.addCondition(COLUMN_MOD_NAME, modName); + db.addColumn(COLUMN_MOD_MAIN); + db.addCondition(COLUMN_MOD_MAIN, modPath); db.exec(); return db.rows(); @@ -532,63 +369,18 @@ bool rdOnlyFlagExists(const QString &chName, uchar subId, int level) return db.rows(); } -bool isLocked(const QString &uName) +bool isLocked(const QByteArray &uId) { Query db; db.setType(Query::PULL, TABLE_USERS); db.addColumn(COLUMN_LOCKED); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); return db.getData(COLUMN_LOCKED).toBool(); } -bool commandHasRank(const QString &cmdName) -{ - Query db; - - db.setType(Query::PULL, TABLE_CMD_RANKS); - db.addColumn(COLUMN_COMMAND); - db.addCondition(COLUMN_COMMAND, cmdName); - db.exec(); - - return db.rows(); -} - -bool checkRank(const QString &myGroup, const QString &targetGroup, bool equalAcceptable) -{ - uint myRank = getRankForGroup(myGroup); - uint targetRank = getRankForGroup(targetGroup); - bool ret = false; - - if (equalAcceptable) - { - ret = (myRank <= targetRank); - } - else - { - ret = (myRank < targetRank); - } - - return ret; -} - -bool maxedInstalledMods() -{ - Query db; - - db.setType(Query::PULL, TABLE_MODULES); - db.addColumn(COLUMN_MOD_NAME); - db.exec(); - - int installed = db.rows(); - double max = (qPow(2, 16) - (MAX_CMDS_PER_MOD * 2)) / MAX_CMDS_PER_MOD; - //max commands - (max async commands + max internal commands) / max commands per mod - - return installed >= max; -} - bool isBool(const QString &str) { bool ret = false; @@ -611,15 +403,13 @@ bool isInt(const QString &str) return ret; } -bool containsChId(const QByteArray &chId, const QByteArray &chIds) +bool matchChs(const char *chsA, const char *chsB) { bool ret = false; - for (int i = 0; i < chIds.size(); i += 9) + for (int i = 0; i < MAX_OPEN_SUB_CHANNELS; i += BLKSIZE_SUB_CHANNEL) { - QByteArray id = QByteArray::fromRawData(chIds.data() + i, 9); - - if (id == chId) + if (posOfBlock(chsA + i, chsB, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL) != -1) { ret = true; @@ -630,131 +420,100 @@ bool containsChId(const QByteArray &chId, const QByteArray &chIds) return ret; } -bool matchChs(const QByteArray &chsA, const QByteArray &chsB) +void containsActiveCh(const char *subChs, char *actBlock) { - bool ret = false; - - for (int i = 0; i < chsA.size(); i += 9) + if (globalActiveFlag()) { - QByteArray id = QByteArray::fromRawData(chsA.data() + i, 9); + wr8BitToBlock(1, actBlock); + } + else + { + wr8BitToBlock(0, actBlock); - if (containsChId(id, chsB)) + Query db; + + for (int i = 0; i < MAX_OPEN_SUB_CHANNELS; i += BLKSIZE_SUB_CHANNEL) { - ret = true; + quint64 chId = rd64BitFromBlock(subChs + i); + quint8 subId = rd8BitFromBlock(subChs + (i + 8)); - break; + db.setType(Query::PULL, TABLE_SUB_CHANNELS); + db.addColumn(COLUMN_CHANNEL_ID); + db.addCondition(COLUMN_CHANNEL_ID, chId); + db.addCondition(COLUMN_SUB_CH_ID, subId); + db.addCondition(COLUMN_ACTIVE_UPDATE, true); + db.exec(); + + if (db.rows()) + { + wr8BitToBlock(1, actBlock); + + break; + } } } - - return ret; } -bool containsActiveCh(const QByteArray &chIds) +QString defaultPw() { - bool ret = false; - Query db; - for (int i = 0; i < chIds.size(); i += 9) - { - quint64 chId = rdInt(QByteArray::fromRawData(chIds.data() + i, 8)); - quint64 subId = rdInt(QByteArray::fromRawData(chIds.data() + (i + 8), 1)); + db.setType(Query::PULL, TABLE_SERV_SETTINGS); + db.addColumn(COLUMN_DEFAULT_PASS); + db.exec(); - db.setType(Query::PULL, TABLE_SUB_CHANNELS); - db.addColumn(COLUMN_CHANNEL_ID); - db.addCondition(COLUMN_CHANNEL_ID, chId); - db.addCondition(COLUMN_SUB_CH_ID, subId); - db.addCondition(COLUMN_ACTIVE_UPDATE, true); - db.exec(); - - if (db.rows()) - { - ret = true; - - break; - } - } - - return ret; + return db.getData(COLUMN_DEFAULT_PASS).toString(); } -bool channelExists(quint64 chId) +bool channelExists(const QString &chName, quint64 *chId) { Query db; db.setType(Query::PULL, TABLE_CHANNELS); db.addColumn(COLUMN_CHANNEL_ID); - db.addCondition(COLUMN_CHANNEL_ID, chId); - db.exec(); - - return db.rows(); -} - -bool channelExists(const QString &chName) -{ - Query db; - - db.setType(Query::PULL, TABLE_CHANNELS); - db.addColumn(COLUMN_CHANNEL_NAME); db.addCondition(COLUMN_CHANNEL_NAME, chName); db.exec(); + if (db.rows() && (chId != nullptr)) + { + *chId = db.getData(COLUMN_CHANNEL_ID).toULongLong(); + } + return db.rows(); } -bool channelSubExists(quint64 chId, uchar subId) +bool channelSubExists(quint64 chId, const QString &sub, quint8 *subId) { Query db; db.setType(Query::PULL, TABLE_SUB_CHANNELS); db.addColumn(COLUMN_SUB_CH_ID); db.addCondition(COLUMN_CHANNEL_ID, chId); - db.addCondition(COLUMN_SUB_CH_ID, subId); - db.exec(); - - return db.rows(); -} - -bool channelSubExists(const QString &ch, const QString &sub) -{ - Query db; - - db.setType(Query::PULL, TABLE_SUB_CHANNELS); - db.addColumn(COLUMN_SUB_CH_NAME); - db.addCondition(COLUMN_CHANNEL_NAME, ch); db.addCondition(COLUMN_SUB_CH_NAME, sub); db.exec(); + if (db.rows() && (subId != nullptr)) + { + *subId = static_cast(db.getData(COLUMN_SUB_CH_ID).toUInt()); + } + return db.rows(); } -bool inviteExists(const QString &uName, const QString &chName) +bool inviteExists(const QByteArray &uId, quint64 chId) { Query db; db.setType(Query::PULL, TABLE_CH_MEMBERS); - db.addColumn(COLUMN_CHANNEL_NAME); - db.addCondition(COLUMN_CHANNEL_NAME, chName); - db.addCondition(COLUMN_USERNAME, uName); + db.addColumn(COLUMN_CHANNEL_ID); + db.addCondition(COLUMN_CHANNEL_ID, chId); + db.addCondition(COLUMN_USER_ID, uId); db.addCondition(COLUMN_PENDING_INVITE, true); db.exec(); return db.rows(); } -bool memberExists(const QString &uName, const QString &chName) -{ - Query db; - - db.setType(Query::PULL, TABLE_CH_MEMBERS); - db.addColumn(COLUMN_CHANNEL_NAME); - db.addCondition(COLUMN_CHANNEL_NAME, chName); - db.addCondition(COLUMN_USERNAME, uName); - db.exec(); - - return db.rows(); -} - bool globalActiveFlag() { Query db; @@ -766,48 +525,7 @@ bool globalActiveFlag() return db.getData(COLUMN_ACTIVE_UPDATE).toBool(); } -bool allowMemberDel(const SharedObjs *sharedObjs, const QString &targetUName, const QString &chName) -{ - bool ret = false; - - if (memberExists(targetUName, chName)) - { - int targetLevel = channelAccessLevel(targetUName, chName); - - if (targetLevel != OWNER) - { - if (noCaseMatch(*sharedObjs->userName, targetUName)) - { - ret = true; - } - else if (channelAccessLevel(sharedObjs, chName) < targetLevel) - { - ret = true; - } - } - } - - return ret; -} - -bool allowLevelChange(const SharedObjs *sharedObjs, int newLevel, const QString &chName) -{ - bool ret = false; - int myLevel = channelAccessLevel(*sharedObjs->userName, chName); - - if ((myLevel == OWNER) && (newLevel == OWNER)) - { - ret = true; - } - else if (newLevel > myLevel) - { - ret = true; - } - - return ret; -} - -bool genSubId(const QString &chName, int *newId) +bool genSubId(quint64 chId, quint8 *newId) { bool ret = false; @@ -815,16 +533,16 @@ bool genSubId(const QString &chName, int *newId) db.setType(Query::PULL, TABLE_SUB_CHANNELS); db.addColumn(COLUMN_SUB_CH_ID); - db.addCondition(COLUMN_CHANNEL_NAME, chName); + db.addCondition(COLUMN_CHANNEL_ID, chId); db.exec(); if (db.rows() < maxSubChannels()) { - QList subList; + QList subList; for (int i = 0; i < db.rows(); ++i) { - subList.append(db.getData(COLUMN_SUB_CH_ID, i).toInt()); + subList.append(static_cast(db.getData(COLUMN_SUB_CH_ID, i).toUInt())); } ret = true; @@ -836,13 +554,13 @@ bool genSubId(const QString &chName, int *newId) return ret; } -bool isChOwner(const QString &uName) +bool isChOwner(const QByteArray &uId) { Query db; db.setType(Query::PULL, TABLE_CH_MEMBERS); - db.addColumn(COLUMN_USERNAME); - db.addCondition(COLUMN_USERNAME, uName); + db.addColumn(COLUMN_USER_ID); + db.addCondition(COLUMN_USER_ID, uId); db.addCondition(COLUMN_PENDING_INVITE, false); db.addCondition(COLUMN_ACCESS_LEVEL, OWNER); db.exec(); @@ -850,92 +568,6 @@ bool isChOwner(const QString &uName) return db.rows(); } -int channelAccessLevel(const QString &uName, quint64 chId) -{ - Query db; - - db.setType(Query::PULL, TABLE_CH_MEMBERS); - db.addColumn(COLUMN_ACCESS_LEVEL); - db.addCondition(COLUMN_CHANNEL_ID, chId); - db.addCondition(COLUMN_USERNAME, uName); - db.addCondition(COLUMN_PENDING_INVITE, false); - db.exec(); - - if (db.rows()) - { - return db.getData(COLUMN_ACCESS_LEVEL).toInt(); - } - else - { - return PUBLIC; - } -} - -int channelAccessLevel(const QString &uName, const QString &chName) -{ - Query db; - - db.setType(Query::PULL, TABLE_CH_MEMBERS); - db.addColumn(COLUMN_ACCESS_LEVEL); - db.addCondition(COLUMN_CHANNEL_NAME, chName); - db.addCondition(COLUMN_USERNAME, uName); - db.addCondition(COLUMN_PENDING_INVITE, false); - db.exec(); - - if (db.rows()) - { - return db.getData(COLUMN_ACCESS_LEVEL).toInt(); - } - else - { - return PUBLIC; - } -} - -int channelAccessLevel(const SharedObjs *sharedObjs, const QString &chName) -{ - if (*sharedObjs->chOwnerOverride) - { - return OWNER; - } - else - { - return channelAccessLevel(*sharedObjs->userName, chName); - } -} - -int channelAccessLevel(const SharedObjs *sharedObjs, quint64 chId) -{ - if (*sharedObjs->chOwnerOverride) - { - return OWNER; - } - else - { - return channelAccessLevel(*sharedObjs->userName, chId); - } -} - -int lowestAcessLevel(quint64 chId, uchar subId) -{ - Query db; - - db.setType(Query::PULL, TABLE_SUB_CHANNELS); - db.addColumn(COLUMN_LOWEST_LEVEL); - db.addCondition(COLUMN_CHANNEL_ID, chId); - db.addCondition(COLUMN_SUB_CH_ID, subId); - db.exec(); - - if (db.rows()) - { - return db.getData(COLUMN_LOWEST_LEVEL).toInt(); - } - else - { - return 5000; - } -} - int maxSubChannels() { Query db; @@ -947,103 +579,36 @@ int maxSubChannels() return db.getData(COLUMN_MAX_SUB_CH).toInt(); } -quint64 getChId(const QString &chName) +int channelAccessLevel(const QByteArray &uId, const char *override, quint64 chId) +{ + if (rd8BitFromBlock(override) == 1) + { + return OWNER; + } + else + { + return channelAccessLevel(uId, chId); + } +} + +int channelAccessLevel(const QByteArray &uId, quint64 chId) { Query db; - db.setType(Query::PULL, TABLE_CHANNELS); - db.addColumn(COLUMN_CHANNEL_ID); - db.addCondition(COLUMN_CHANNEL_NAME, chName); + db.setType(Query::PULL, TABLE_CH_MEMBERS); + db.addColumn(COLUMN_ACCESS_LEVEL); + db.addCondition(COLUMN_CHANNEL_ID, chId); + db.addCondition(COLUMN_USER_ID, uId); + db.addCondition(COLUMN_PENDING_INVITE, false); db.exec(); - return db.getData(COLUMN_CHANNEL_ID).toULongLong(); -} - -uchar getSubId(const QString &chName, const QString &subName) -{ - Query db; - - db.setType(Query::PULL, TABLE_SUB_CHANNELS); - db.addColumn(COLUMN_SUB_CH_ID); - db.addCondition(COLUMN_CHANNEL_NAME, chName); - db.addCondition(COLUMN_SUB_CH_NAME, subName); - db.exec(); - - return static_cast(db.getData(COLUMN_SUB_CH_ID).toUInt()); -} - -int chPos(const QByteArray &id, const QByteArray &chIds) -{ - int ret = -1; - - for (int i = 0; i < chIds.size(); i += 9) + if (db.rows()) { - QByteArray chInList = QByteArray::fromRawData(chIds.data() + i, 9); - - if (chInList == id) - { - ret = i; - - break; - } + return db.getData(COLUMN_ACCESS_LEVEL).toInt(); } - - return ret; -} - -int countChs(const QByteArray &chIds) -{ - int ret = 0; - - for (int i = 0; i < chIds.size(); i += 9) + else { - quint64 id = rdInt(QByteArray::fromRawData(chIds.data() + i, 8)); - - if (id != 0) - { - ret++; - } - } - - return ret; -} - -int blankChPos(const QByteArray &chIds) -{ - int ret = -1; - - for (int i = 0; i < chIds.size(); i += 9) - { - quint64 id = rdInt(QByteArray::fromRawData(chIds.data() + i, 8)); - - if (id == 0) - { - ret = i; - - break; - } - } - - return ret; -} - -uint getRankForGroup(const QString &grName) -{ - Query db; - - db.setType(Query::PULL, TABLE_GROUPS); - db.addColumn(COLUMN_HOST_RANK); - db.addCondition(COLUMN_GRNAME, grName); - db.exec(); - - return db.getData(COLUMN_HOST_RANK).toUInt(); -} - -void uniqueAdd(quint16 id, QList &list) -{ - if (!list.contains(id)) - { - list.append(id); + return PUBLIC; } } @@ -1064,18 +629,6 @@ void listDir(QList > &list, const QString &srcPath, cons } } -QString getUserGroup(const QString &uName) -{ - Query db; - - db.setType(Query::PULL, TABLE_USERS); - db.addColumn(COLUMN_GRNAME); - db.addCondition(COLUMN_USERNAME, uName); - db.exec(); - - return db.getData(COLUMN_GRNAME).toString(); -} - QString getUserNameForEmail(const QString &email) { Query db; @@ -1088,13 +641,25 @@ QString getUserNameForEmail(const QString &email) return db.getData(COLUMN_USERNAME).toString(); } -QString getEmailForUser(const QString &uName) +QString getDispName(const QByteArray &uId) +{ + Query db; + + db.setType(Query::PULL, TABLE_USERS); + db.addColumn(COLUMN_DISPLAY_NAME); + db.addCondition(COLUMN_USER_ID, uId); + db.exec(); + + return db.getData(COLUMN_DISPLAY_NAME).toString(); +} + +QString getEmailForUser(const QByteArray &uId) { Query db; db.setType(Query::PULL, TABLE_USERS); db.addColumn(COLUMN_EMAIL); - db.addCondition(COLUMN_USERNAME, uName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); return db.getData(COLUMN_EMAIL).toString(); @@ -1135,157 +700,6 @@ QString escapeChars(const QString &str, const QChar &escapeChr, const QChar &chr return ret; } -QList genSequence(int min, int max, int len) -{ - QList ret; - - for (int i = 0; i < len; ++i) - { - ret.append(QRandomGenerator::global()->bounded(min, max)); - } - - return ret; -} - -QChar genLetter() -{ - // generate random letter from ascii table decimal value 97-122. - - return QChar(static_cast(QRandomGenerator::global()->bounded(97, 122))); -} - -QChar genNum() -{ - // generate random number from ascii table decimal value 48-57. - - return QChar(static_cast(QRandomGenerator::global()->bounded(48, 57))); -} - -QChar genSpecialChar() -{ - static QString specialChars = "`~!@#$%^&*()-_+=[]{}\\|:;\"'<,>.?/"; - - return specialChars[QRandomGenerator::global()->bounded(0, specialChars.size() - 1)]; -} - -int inRange(int pos, int min, int max) -{ - int ret = pos; - - if (pos < min) ret = min; - if (pos > max) ret = max; - - return ret; -} - -void moveCharLeft(int pos, QString &str) -{ - pos = inRange(pos, 0, str.size() - 1); - - QChar chr = str[pos]; - - str.remove(pos, 1); - - if (pos == 0) str.append(chr); - else str.insert(pos - 1, chr); -} - -void moveCharRight(int pos, QString &str) -{ - pos = inRange(pos, 0, str.size() - 1); - - QChar chr = str[pos]; - - str.remove(pos, 1); - - if (pos == str.size() - 1) str.insert(0, chr); - else str.insert(pos + 1, chr); -} - -QString genPw() -{ - QString ret; - - QList seq = genSequence(2, 5, 4); - - for (int i = 0; i < seq[0]; ++i) - { - ret.append(genLetter()); - } - - for (int i = 0; i < seq[1]; ++i) - { - ret.append(genLetter().toUpper()); - } - - for (int i = 0; i < seq[2]; ++i) - { - ret.append(genNum()); - } - - for (int i = 0; i < seq[3]; ++i) - { - ret.append(genSpecialChar()); - } - - seq = genSequence(0, ret.size() - 1, 10); - - bool toggle = false; - - for (int i : seq) - { - if (toggle) moveCharRight(i, ret); - else moveCharLeft(i, ret); - - toggle = !toggle; - } - - return ret; -} - -QString modDataPath() -{ - QString ret = qEnvironmentVariable(ENV_MOD_PATH, DEFAULT_MOD_PATH); - - if (ret.right(1) == '/') ret.chop(1); - else if (ret.right(1) == '\\') ret.chop(1); - - ret = expandEnvVariables(ret); - - mkPath(ret); - - return ret; -} - -QString pipesPath() -{ - QString ret = qEnvironmentVariable(ENV_PIPE_PATH, DEFAULT_PIPE_PATH); - - if (ret.right(1) == '/') ret.chop(1); - else if (ret.right(1) == '\\') ret.chop(1); - - ret = expandEnvVariables(ret); - - mkPath(ret); - - return ret; -} - -void msgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) -{ - Q_UNUSED(type); - Q_UNUSED(context); - - if (!msg.contains("QSslSocket: cannot resolve")) - { - Query db; - - db.setType(Query::PUSH, TABLE_DMESG); - db.addColumn(COLUMN_LOGENTRY, msg); - db.exec(); - } -} - void mkPath(const QString &path) { if (!QDir().exists(path)) @@ -1294,150 +708,6 @@ void mkPath(const QString &path) } } -void mkFile(const QString &path) -{ - mkPathForFile(path); - - QFile file(path); - - file.open(QFile::WriteOnly); - file.write(QByteArray()); - file.close(); -} - -void wrOpenCh(RWSharedObjs *sharedObjs, const QByteArray &id) -{ - quint64 chId = rdInt(QByteArray::fromRawData(id.data(), 8)); - quint64 subId = rdInt(QByteArray::fromRawData(id.data() + 8, 1)); - int rdPos = blankChPos(*sharedObjs->chIds); - int wrPos = blankChPos(*sharedObjs->wrAbleChIds); - int lvl; - - if (*sharedObjs->chOwnerOverride) - { - lvl = OWNER; - } - else - { - lvl = channelAccessLevel(*sharedObjs->userName, chId); - } - - sharedObjs->chIds->replace(rdPos, 9, id); - - Query db; - - db.setType(Query::PULL, TABLE_RDONLY_CAST); - db.addColumn(COLUMN_CHANNEL_ID); - db.addCondition(COLUMN_CHANNEL_ID, chId); - db.addCondition(COLUMN_SUB_CH_ID, subId); - db.addCondition(COLUMN_ACCESS_LEVEL, lvl); - db.exec(); - - if (db.rows() == 0) - { - sharedObjs->wrAbleChIds->replace(wrPos, 9, id); - } - - if (globalActiveFlag()) - { - *sharedObjs->activeUpdate = true; - } - else - { - *sharedObjs->activeUpdate = containsActiveCh(*sharedObjs->chIds); - } -} - -void wrCloseCh(RWSharedObjs *sharedObjs, const QByteArray &id, QByteArray &peerStat) -{ - int rdPos = chPos(id, *sharedObjs->chIds); - int wrPos = chPos(id, *sharedObjs->wrAbleChIds); - bool needPeerStat = false; - QByteArray oldChIds = *sharedObjs->chIds; - - if (*sharedObjs->activeUpdate) - { - needPeerStat = true; - } - - if (rdPos != -1) - { - sharedObjs->chIds->replace(rdPos, 9, QByteArray(9, static_cast(0))); - - if (wrPos != -1) - { - sharedObjs->wrAbleChIds->replace(wrPos, 9, QByteArray(9, static_cast(0))); - } - - *sharedObjs->activeUpdate = containsActiveCh(*sharedObjs->chIds); - } - - if (needPeerStat && (oldChIds != *sharedObjs->chIds)) - { - QByteArray castHeader = oldChIds + wrInt(PEER_STAT, 8); - QByteArray data = toPEER_STAT(*sharedObjs->sessionId, *sharedObjs->chIds, false); - - peerStat = castHeader + data; - } - else - { - peerStat.clear(); - } -} - -void wrCloseCh(RWSharedObjs *sharedObjs, quint64 chId, QByteArray &peerStat) -{ - QByteArray chBa = wrInt(chId, 64); - QByteArray oldChIds = *sharedObjs->chIds; - bool needPeerStat = false; - - if (*sharedObjs->activeUpdate) - { - needPeerStat = true; - } - - for (int i = 0; i < sharedObjs->chIds->size(); i += 9) - { - if (chBa == QByteArray::fromRawData(sharedObjs->chIds->data() + i, 8)) - { - sharedObjs->chIds->replace(i, 9, QByteArray(9, static_cast(0))); - } - - if (chBa == QByteArray::fromRawData(sharedObjs->wrAbleChIds->data() + i, 8)) - { - sharedObjs->wrAbleChIds->replace(i, 9, QByteArray(9, static_cast(0))); - } - } - - *sharedObjs->activeUpdate = containsActiveCh(*sharedObjs->chIds); - - if (needPeerStat && (oldChIds != *sharedObjs->chIds)) - { - QByteArray castHeader = oldChIds + wrInt(PEER_STAT, 8); - QByteArray data = toPEER_STAT(*sharedObjs->sessionId, *sharedObjs->chIds, false); - - peerStat = castHeader + data; - } - else - { - peerStat.clear(); - } -} - -void wrCloseCh(RWSharedObjs *sharedObjs, const QByteArray &id) -{ - QByteArray unused; - - wrCloseCh(sharedObjs, id, unused); -} - -void wrCloseCh(RWSharedObjs *sharedObjs, quint64 chId) -{ - QByteArray unused; - - wrCloseCh(sharedObjs, chId, unused); -} - QStringList parseArgs(const QByteArray &data, int maxArgs, int *pos) { QStringList ret; @@ -1560,203 +830,27 @@ SessionCarrier::SessionCarrier(Session *session) : QObject() sessionObj = session; } -InternCommand::InternCommand(QObject *parent) : ExternCommand(parent) +IdleTimer::IdleTimer(QObject *parent) : QTimer(parent) { - rwSharedObjs = nullptr; + setSingleShot(true); } -void InternCommand::setWritableDataShare(RWSharedObjs *sharedObjs) +void IdleTimer::detectWrite(qint64) { - rwSharedObjs = sharedObjs; - - term(); + start(); } -bool InternCommand::loopEnabled() +void IdleTimer::attach(QIODevice *dev, int msec) { - if (rwSharedObjs == nullptr) - { - return false; - } - else - { - return rwSharedObjs->activeLoopCmds->contains(cmdId); - } -} + setInterval(msec); -bool InternCommand::moreInputEnabled() -{ - if (rwSharedObjs == nullptr) - { - return false; - } - else - { - return rwSharedObjs->moreInputCmds->contains(cmdId); - } -} - -QString InternCommand::libText() -{ - return INTERN_MOD_NAME; -} - -QString InternCommand::parseMd(int offset) -{ - QFile file(":/docs/intern_commands/" + objectName() + ".md", this); - QByteArray data; - - if (file.open(QFile::ReadOnly)) - { - data = file.readAll(); - } - - file.close(); - - int targetTags = offset * 6; - int pos = -1; - int len = 0; - - for (int i = 0, tags = 0; i < data.size(); ++i) - { - if (data[i] == '#') - { - ++tags; - - if (pos != -1) - { - break; - } - } - else if (tags == targetTags) - { - len++; - - if (pos == -1) - { - pos = i; - } - } - } - - QByteArray ret = data.mid(pos, len).trimmed(); - - if (offset == 2) - { - ret.chop(3); - ret.remove(0, 3); - } - - return ret; -} - -QString InternCommand::shortText() -{ - return parseMd(1); -} - -QString InternCommand::ioText() -{ - return parseMd(2); -} - -QString InternCommand::longText() -{ - return parseMd(3); -} - -CommandOutput::CommandOutput(ExternCommand *parent) : QObject(parent) -{ - // this class is used by CmdExecutor to permanently attach a command id to - // the command object's output data and isolates that id from the object - // itself. this prevents objects from impersonating another object's output - // data. - - cmdObj = parent; -} - -void CommandOutput::setCmdId(quint16 id) -{ - cmdId = id; -} - -void CommandOutput::dataFromCmdObj(const QByteArray &data, uchar typeId) -{ - emit dataOut(cmdId, data, typeId); -} - -void CommandOutput::openChIdFromCmdObj(quint64 id, uchar subId) -{ - emit openChById(cmdId, id, subId); -} - -void CommandOutput::openChNameFromCmdObj(const QString &ch, const QString &sub) -{ - emit openChByName(cmdId, ch, sub); -} - -void CommandOutput::closeChIdFromCmdObj(quint64 id, uchar subId) -{ - emit closeChById(cmdId, id, subId); -} - -void CommandOutput::closeChNameFromCmdObj(const QString &ch, const QString &sub) -{ - emit closeChByName(cmdId, ch, sub); -} - -void CommandOutput::enableLoopFromCmdObj(bool state) -{ - cmdObj->inLoopMode = state; - - emit enableLoop(cmdId, state); -} - -void CommandOutput::enableMoreInputFromCmdObj(bool state) -{ - cmdObj->inMoreInputMode = state; - - emit enableMoreInput(cmdId, state); -} - -void CommandOutput::finished() -{ - cmdObj->inMoreInputMode = false; - cmdObj->inLoopMode = false; - - emit cmdFinished(cmdId); -} - -RWSharedObjs::RWSharedObjs(QObject *parent) : QObject(parent) -{ - commands = nullptr; - cmdNames = nullptr; - chList = nullptr; - chIds = nullptr; - wrAbleChIds = nullptr; - sessionAddr = nullptr; - userName = nullptr; - groupName = nullptr; - displayName = nullptr; - appName = nullptr; - clientMajor = nullptr; - clientMinor = nullptr; - clientPatch = nullptr; - sessionId = nullptr; - moreInputCmds = nullptr; - activeLoopCmds = nullptr; - pausedCmds = nullptr; - p2pAccepted = nullptr; - p2pPending = nullptr; - activeUpdate = nullptr; - chOwnerOverride = nullptr; - hostRank = nullptr; + connect(dev, SIGNAL(readyRead()), this, SLOT(start())); + connect(dev, SIGNAL(bytesWritten(qint64)), this, SLOT(detectWrite(qint64))); } ShellIPC::ShellIPC(const QStringList &args, QObject *parent) : QLocalSocket(parent) { arguments = args; - pipeName = pipesPath() + "/" + QString(APP_NAME) + ".TCPServer.Control"; connect(this, SIGNAL(connected()), this, SLOT(hostConnected())); connect(this, SIGNAL(disconnected()), this, SIGNAL(closeInstance())); @@ -1765,7 +859,7 @@ ShellIPC::ShellIPC(const QStringList &args, QObject *parent) : QLocalSocket(pare bool ShellIPC::connectToHost() { - connectToServer(pipeName); + connectToServer(HOST_CONTROL_PIPE); if (!waitForConnected()) { @@ -1786,3 +880,5 @@ void ShellIPC::dataIn() emit closeInstance(); } + +quint64 Serial::serialIndex = 0; diff --git a/src/common.h b/src/common.h index 0bb4e87..f442895 100644 --- a/src/common.h +++ b/src/common.h @@ -70,9 +70,9 @@ #include "db.h" #include "shell.h" -#include "commands/command.h" +#include "mem_share.h" -#define FRAME_HEADER_SIZE 6 +#define FRAME_HEADER_SIZE 8 #define MAX_FRAME_BITS 24 #define IMPORT_REV 3 #define LOCAL_BUFFSIZE 16777215 @@ -84,83 +84,80 @@ #define EXE_CRASH_LIMIT 5 #define EXE_DEBUG_INFO_SIZE 512 #define SERVER_HEADER_TAG "MRCI" -#define MOD_IMPORT_FUNC "hostImport" +#define HOST_CONTROL_PIPE "MRCI_HOST_CONTROL" +#define TXT_CODEC "UTF-16LE" +#define TXT_CODEC_BITS 16 -#define ASYNC_RDY 1 -#define ASYNC_SYS_MSG 2 -#define ASYNC_EXE_CRASH 3 -#define ASYNC_EXIT 4 // internal only -#define ASYNC_CAST 5 // internal only -#define ASYNC_MAXSES 6 // internal only -#define ASYNC_LOGOUT 7 // internal only -#define ASYNC_USER_DELETED 8 -#define ASYNC_GROUP_RENAMED 9 // internal only -#define ASYNC_DISP_RENAMED 10 // internal only -#define ASYNC_GRP_TRANS 11 // internal only -#define ASYNC_USER_GROUP_CHANGED 12 // internal only -#define ASYNC_CMD_RANKS_CHANGED 13 // internal only -#define ASYNC_RESTART 14 // internal only -#define ASYNC_ENABLE_MOD 15 // internal only -#define ASYNC_DISABLE_MOD 16 // internal only -#define ASYNC_GROUP_UPDATED 17 // internal only -#define ASYNC_END_SESSION 18 // internal only -#define ASYNC_USER_LOGIN 19 // internal only -#define ASYNC_RESTORE_AUTH 20 // internal only -#define ASYNC_TO_PEER 21 -#define ASYNC_LIMITED_CAST 22 -#define ASYNC_RW_MY_INFO 23 // internal only -#define ASYNC_P2P 24 -#define ASYNC_CLOSE_P2P 25 // internal only -#define ASYNC_NEW_CH_MEMBER 26 -#define ASYNC_DEL_CH 27 -#define ASYNC_RENAME_CH 28 -#define ASYNC_CH_ACT_FLAG 29 -#define ASYNC_NEW_SUB_CH 30 -#define ASYNC_RM_SUB_CH 31 -#define ASYNC_RENAME_SUB_CH 32 -#define ASYNC_INVITED_TO_CH 33 -#define ASYNC_RM_CH_MEMBER 34 -#define ASYNC_INVITE_ACCEPTED 35 -#define ASYNC_MEM_LEVEL_CHANGED 36 -#define ASYNC_SUB_CH_LEVEL_CHG 37 -#define ASYNC_ADD_RDONLY 38 -#define ASYNC_RM_RDONLY 39 -#define ASYNC_ADD_CMD 40 -#define ASYNC_RM_CMD 41 -#define ASYNC_USER_RENAMED 42 -#define ASYNC_PUBLIC_AUTH 43 // internal only - -enum SalveExitCodes +enum AsyncCommands : quint16 { - GEN_ERR = 1, - FAILED_TO_OPEN_PIPE = 2, - PIPE_CONNECT_TIMEOUT = 3 + ASYNC_RDY = 1, + ASYNC_SYS_MSG = 2, + ASYNC_EXIT = 3, // internal only + ASYNC_CAST = 4, + ASYNC_MAXSES = 5, // internal only + ASYNC_LOGOUT = 6, // internal only + ASYNC_USER_DELETED = 7, + ASYNC_DISP_RENAMED = 8, // internal only + ASYNC_USER_RANK_CHANGED = 9, // internal only + ASYNC_CMD_RANKS_CHANGED = 10, // internal only + ASYNC_RESTART = 11, // internal only + ASYNC_ENABLE_MOD = 12, // internal only + ASYNC_DISABLE_MOD = 13, // internal only + ASYNC_END_SESSION = 14, // internal only + ASYNC_USER_LOGIN = 15, // internal only + ASYNC_TO_PEER = 16, + ASYNC_LIMITED_CAST = 17, + ASYNC_RW_MY_INFO = 18, // internal only + ASYNC_P2P = 19, + ASYNC_CLOSE_P2P = 20, // internal only + ASYNC_NEW_CH_MEMBER = 21, + ASYNC_DEL_CH = 22, + ASYNC_RENAME_CH = 23, + ASYNC_CH_ACT_FLAG = 24, + ASYNC_NEW_SUB_CH = 25, + ASYNC_RM_SUB_CH = 26, + ASYNC_RENAME_SUB_CH = 27, + ASYNC_INVITED_TO_CH = 28, + ASYNC_RM_CH_MEMBER = 29, + ASYNC_INVITE_ACCEPTED = 30, + ASYNC_MEM_LEVEL_CHANGED = 31, + ASYNC_SUB_CH_LEVEL_CHG = 32, + ASYNC_ADD_RDONLY = 33, + ASYNC_RM_RDONLY = 34, + ASYNC_ADD_CMD = 35, + ASYNC_RM_CMD = 36, + ASYNC_USER_RENAMED = 37, + ASYNC_PING_PEERS = 38, // internal only + ASYNC_OPEN_SUBCH = 39, // internal only + ASYNC_CLOSE_SUBCH = 40, // internal only + ASYNC_UPDATE_BANS = 41, // internal only + ASYNC_KEEP_ALIVE = 42, // internal only + ASYNC_SET_DIR = 43, // internal only + ASYNC_DEBUG_TEXT = 44 // internal only }; -enum PrivateTypeID +enum Flags : quint32 { - PRIV_IPC = 1, - PUB_IPC = 2, - PUB_IPC_WITH_FEEDBACK = 3, - PING_PEERS = 4 + FRAME_RDY = 1, + SESSION_RDY = 1 << 1, + ACTIVE_PAYLOAD = 1 << 2, + END_SESSION_EMPTY_PROC = 1 << 3, + END_SESSION_ON_PAYLOAD_DEL = 1 << 4, + RES_ON_EMPTY = 1 << 5, + CLOSE_ON_EMPTY = 1 << 6, + ACCEPTING = 1 << 7, + LOADING_PUB_CMDS = 1 << 8, + LOADING_EXEMPT_CMDS = 1 << 9, + LOADING_USER_CMDS = 1 << 10, + SESSION_PARAMS_SET = 1 << 11, + LOGGED_IN = 1 << 12, + MORE_INPUT = 1 << 13, + LOOPING = 1 << 14, + SINGLE_STEP_MODE = 1 << 15, + HALT_STATE = 1 << 16 }; -enum Flags : uint -{ - IPC_LINK_OK = 1, - IPC_FRAME_RDY = 1 << 1, - TCP_FRAME_RDY = 1 << 2, - SSL_HOLD = 1 << 3, - VER_OK = 1 << 4, - EXPECTED_TERM = 1 << 5, - ACTIVE_PAYLOAD = 1 << 6, - END_SESSION_ON_PAYLOAD_DEL = 1 << 7, - RES_ON_EMPTY = 1 << 8, - CLOSE_ON_EMPTY = 1 << 9, - ACCEPTING = 1 << 10 -}; - -enum FileInfoFlags +enum FileInfoFlags : quint8 { IS_FILE = 1, IS_DIR = 1 << 1, @@ -171,145 +168,102 @@ enum FileInfoFlags EXISTS = 1 << 6 }; -typedef ExternCommand* (*BuildInternCmd)(QObject*); +enum TypeID : quint8 +{ + GEN_FILE = 1, + TEXT = 2, + ERR = 3, + PRIV_TEXT = 4, + IDLE = 5, + HOST_CERT = 6, + FILE_INFO = 7, + PEER_INFO = 8, + MY_INFO = 9, + PEER_STAT = 10, + P2P_REQUEST = 11, + P2P_CLOSE = 12, + P2P_OPEN = 13, + BYTES = 14, + SESSION_ID = 15, + NEW_CMD = 16, + CMD_ID = 17, + BIG_TEXT = 18, + TERM_CMD = 19, + HOST_VER = 20, + PRIV_IPC = 21, + PUB_IPC = 22, + PUB_IPC_WITH_FEEDBACK = 23, + PING_PEERS = 24, + CH_MEMBER_INFO = 25, + CH_ID = 26, + KILL_CMD = 27, + HALT_CMD = 28, + RESUME_CMD = 29 +}; + +enum ChannelMemberLevel : quint8 +{ + OWNER = 1, + ADMIN = 2, + OFFICER = 3, + REGULAR = 4, + PUBLIC = 5 +}; -class RWSharedObjs; class Session; -QByteArray wrFrame(quint16 cmdId, const QByteArray &data, uchar dType); -QByteArray wrInt(quint64 num, int numOfBits); -QByteArray wrInt(qint64 num, int numOfBits); -QByteArray wrInt(int num, int numOfBits); -QByteArray wrInt(uint num, int numOfBits); -QByteArray toFILE_INFO(const QString &path); -QByteArray toFILE_INFO(const QFileInfo &info); QByteArray toTEXT(const QString &txt); QByteArray fixedToTEXT(const QString &txt, int len); -QByteArray toPEER_INFO(const SharedObjs *sharedObjs); -QByteArray toMY_INFO(const SharedObjs *sharedObjs); -QByteArray toPEER_STAT(const QByteArray &sesId, const QByteArray &chIds, bool isDisconnecting); -QByteArray toNEW_CMD(quint16 cmdId, const QString &cmdName, ExternCommand *cmdObj); -quint64 rdInt(const QByteArray &bytes); -quint64 getChId(const QString &chName); -uchar getSubId(const QString &chName, const QString &subName); -uint rdSessionLoad(); -uint getRankForGroup(const QString &grName); -void wrSessionLoad(uint value); +QByteArray nullTermTEXT(const QString &txt); +quint32 toCmdId32(quint16 cmdId, quint16 branchId); +quint16 toCmdId16(quint32 id); void serializeThread(QThread *thr); -void uniqueAdd(quint16 id, QList &list); -void mkPathForFile(const QString &path); -void msgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); void mkPath(const QString &path); -void mkFile(const QString &path); -void moveCharLeft(int pos, QString &str); -void moveCharRight(int pos, QString &str); void listDir(QList > &list, const QString &srcPath, const QString &dstPath); -void wrOpenCh(RWSharedObjs *sharedObjs, const QByteArray &id); -void wrCloseCh(RWSharedObjs *sharedObjs, const QByteArray &id, QByteArray &peerStat); -void wrCloseCh(RWSharedObjs *sharedObjs, quint64 chId, QByteArray &peerStat); -void wrCloseCh(RWSharedObjs *sharedObjs, const QByteArray &id); -void wrCloseCh(RWSharedObjs *sharedObjs, quint64 chId); +void containsActiveCh(const char *subChs, char *actBlock); bool containsNewLine(const QString &str); +bool validModPath(const QString &modPath); bool validUserName(const QString &uName); bool validEmailAddr(const QString &email); bool validPassword(const QString &pw); -bool validGroupName(const QString &grName); bool validCommandName(const QString &name); bool validCommonName(const QString &name); bool validDispName(const QString &name); bool validChName(const QString &name); bool validLevel(const QString &num, bool includePub); bool validSubId(const QString &num); -bool modExists(const QString &modName); -bool userExists(const QString &uName); -bool emailExists(const QString &email); -bool groupExists(const QString &grName); -bool inviteExists(const QString &uName, const QString &chName); -bool memberExists(const QString &uName, const QString &chName); -bool channelExists(quint64 chId); -bool channelExists(const QString &chName); -bool channelSubExists(quint64 chId, uchar subId); -bool channelSubExists(const QString &ch, const QString &sub); -bool recoverPWExists(const QString &uName); +bool modExists(const QString &modPath); +bool userExists(const QString &uName, QByteArray *uId = nullptr, QString *email = nullptr); +bool emailExists(const QString &email, QByteArray *uId = nullptr); +bool inviteExists(const QByteArray &uId, quint64 chId); +bool channelExists(const QString &chName, quint64 *chId = nullptr); +bool channelSubExists(quint64 chId, const QString &sub, quint8 *subId = nullptr); +bool recoverPWExists(const QByteArray &uId); bool rdOnlyFlagExists(const QString &chName, uchar subId, int level); -bool checkRank(const QString &myGroup, const QString &targetGroup, bool equalAcceptable = false); bool isBool(const QString &str); bool isInt(const QString &str); -bool isLocked(const QString &uName); -bool commandHasRank(const QString &cmdName); +bool isLocked(const QByteArray &uId); bool matchedFsObjTypes(const QString &pathA, const QString &pathB); bool matchedVolume(const QString &pathA, const QString &pathB); bool noCaseMatch(const QString &strA, const QString &strB); bool argExists(const QString &key, const QStringList &args); -bool matchChs(const QByteArray &chsA, const QByteArray &chsB); -bool containsChId(const QByteArray &chId, const QByteArray &chIds); -bool containsActiveCh(const QByteArray &chIds); +bool matchChs(const char *chsA, const char *chsB); bool globalActiveFlag(); -bool genSubId(const QString &chName, int *newId); -bool isChOwner(const QString &uName); -bool allowMemberDel(const SharedObjs *sharedObjs, const QString &targetUName, const QString &chName); -bool allowLevelChange(const SharedObjs *sharedObjs, int newLevel, const QString &chName); -bool maxedInstalledMods(); -int channelAccessLevel(const QString &uName, quint64 chId); -int channelAccessLevel(const QString &uName, const QString &chName); -int channelAccessLevel(const SharedObjs *sharedObjs, const QString &chName); -int channelAccessLevel(const SharedObjs *sharedObjs, quint64 chId); -int lowestAcessLevel(quint64 chId, uchar subId); -int chPos(const QByteArray &id, const QByteArray &chIds); -int blankChPos(const QByteArray &chIds); -int countChs(const QByteArray &chIds); -int inRange(int pos, int min, int max); +bool genSubId(quint64 chId, quint8 *newId); +bool isChOwner(const QByteArray &uId); +int channelAccessLevel(const QByteArray &uId, quint64 chId); +int channelAccessLevel(const QByteArray &uId, const char *override, quint64 chId); int maxSubChannels(); QString fromTEXT(const QByteArray &txt); -QString getUserGroup(const QString &uName); QString getUserNameForEmail(const QString &email); -QString getEmailForUser(const QString &uName); +QString getEmailForUser(const QByteArray &uId); +QString getDispName(const QByteArray &uId); QString boolStr(bool state); QString getParam(const QString &key, const QStringList &args); -QString genPw(); QString escapeChars(const QString &str, const QChar &escapeChr, const QChar &chr); QString genSerialNumber(); -QString modDataPath(); -QString pipesPath(); -QString sessionCountShareKey(); +QString defaultPw(); QStringList parseArgs(const QByteArray &data, int maxArgs, int *pos = nullptr); -QList genSequence(int min, int max, int len); -QChar genLetter(); -QChar genNum(); -QChar genSpecialChar(); - -class RWSharedObjs : public QObject -{ - Q_OBJECT - -public: - - QHash *commands; - QHash *cmdNames; - QList *chList; - QList *p2pAccepted; - QList *p2pPending; - QList *moreInputCmds; - QList *activeLoopCmds; - QList *pausedCmds; - QString *sessionAddr; - QString *userName; - QString *groupName; - QString *displayName; - QString *appName; - ushort *clientMajor; - ushort *clientMinor; - ushort *clientPatch; - QByteArray *chIds; - QByteArray *wrAbleChIds; - QByteArray *sessionId; - QByteArray *userId; - bool *activeUpdate; - bool *chOwnerOverride; - uint *hostRank; - - explicit RWSharedObjs(QObject *parent = nullptr); -}; //--------------------------- @@ -324,81 +278,24 @@ public: explicit SessionCarrier(Session *session); }; -//-------------------------- +//---------------------------- -class InternCommand : public ExternCommand +class IdleTimer : public QTimer { Q_OBJECT -protected: +private slots: - RWSharedObjs *rwSharedObjs; - - bool loopEnabled(); - bool moreInputEnabled(); - QString parseMd(int offset); + void detectWrite(qint64); public: - void setWritableDataShare(RWSharedObjs *sharedObjs); - QString libText(); - QString shortText(); - QString ioText(); - QString longText(); + explicit IdleTimer(QObject *parent = nullptr); - explicit InternCommand(QObject *parent = nullptr); - -signals: - - void authOk(); - void termAllCommands(); - void castPeerInfo(); - void reloadCommands(); - void termCommandId(quint16 cmdId); - void backendDataOut(quint16 cmdId, const QByteArray &data, uchar typeId); + void attach(QIODevice *dev, int msec); }; -//---------------------------- - -class CommandOutput : public QObject -{ - Q_OBJECT - -private: - - quint16 cmdId; - ExternCommand *cmdObj; - -public: - - explicit CommandOutput(ExternCommand *parent); - - void setCmdId(quint16 id); - -public slots: - - void dataFromCmdObj(const QByteArray &data, uchar typeId); - void openChIdFromCmdObj(quint64 id, uchar subId); - void closeChIdFromCmdObj(quint64 id, uchar subId); - void openChNameFromCmdObj(const QString &ch, const QString &sub); - void closeChNameFromCmdObj(const QString &ch, const QString &sub); - void enableLoopFromCmdObj(bool state); - void enableMoreInputFromCmdObj(bool state); - void finished(); - -signals: - - void dataOut(quint16 cmdId, const QByteArray &data, uchar typeId); - void openChById(quint16 cmdId, quint64 id, uchar subId); - void closeChById(quint16 cmdId, quint64 id, uchar subId); - void openChByName(quint16 cmdId, const QString &ch, const QString &sub); - void closeChByName(quint16 cmdId, const QString &ch, const QString &sub); - void cmdFinished(quint16 cmdId); - void enableLoop(quint16 cmdId, bool state); - void enableMoreInput(quint16 cmdId, bool state); -}; - -//---------------------------- +//--------------------------- class ShellIPC : public QLocalSocket { @@ -407,7 +304,6 @@ class ShellIPC : public QLocalSocket private: QStringList arguments; - QString pipeName; private slots: @@ -425,4 +321,14 @@ signals: void closeInstance(); }; +//-------------------------- + +class Serial +{ + +public: + + static quint64 serialIndex; +}; + #endif // COMMON_H diff --git a/src/db.cpp b/src/db.cpp index 35516cb..8501862 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -26,9 +26,9 @@ QString columnType(const QString &column) QString ret; if ((column == COLUMN_IPADDR) || (column == COLUMN_LOGENTRY) || (column == COLUMN_USERNAME) || - (column == COLUMN_GRNAME) || (column == COLUMN_EMAIL) || (column == COLUMN_INITGROUP) || + (column == COLUMN_CHANNEL_NAME) || (column == COLUMN_EMAIL) || (column == COLUMN_SUB_CH_NAME) || (column == COLUMN_COMMAND) || (column == COLUMN_CLIENT_VER) || (column == COLUMN_COMMON_NAME) || - (column == COLUMN_DISPLAY_NAME) || (column == COLUMN_CHANNEL_NAME) || (column == COLUMN_SUB_CH_NAME)) + (column == COLUMN_DISPLAY_NAME)) { ret = "TEXT COLLATE NOCASE"; } @@ -43,14 +43,14 @@ QString columnType(const QString &column) else if ((column == COLUMN_TEMP_PW_MSG) || (column == COLUMN_ZIPBIN) || (column == COLUMN_CONFIRM_SUBJECT) || (column == COLUMN_ZIPCOMPRESS) || (column == COLUMN_ZIPEXTRACT) || (column == COLUMN_TEMP_PW_SUBJECT) || (column == COLUMN_MAILERBIN) || (column == COLUMN_MAIL_SEND) || (column == COLUMN_CONFIRM_MSG) || - (column == COLUMN_MOD_NAME) || (column == COLUMN_MOD_MAIN)) + (column == COLUMN_MOD_MAIN)) { ret = "TEXT"; } else if ((column == COLUMN_LOCK_LIMIT) || (column == COLUMN_PORT) || (column == COLUMN_BAN_LIMIT) || (column == COLUMN_HOST_RANK) || (column == COLUMN_MAXSESSIONS) || (column == COLUMN_LOWEST_LEVEL) || (column == COLUMN_ACCESS_LEVEL) || (column == COLUMN_CHANNEL_ID) || (column == COLUMN_SUB_CH_ID) || - (column == COLUMN_MAX_SUB_CH) || (column == COLUMN_CMD_ID_OFFS)) + (column == COLUMN_MAX_SUB_CH) || (column == COLUMN_INITRANK)) { ret = "INTEGER"; } @@ -91,78 +91,185 @@ QString sqlDataPath() return ret; } -QString initGroup() +QList genSequence(int min, int max, int len) +{ + QList ret; + + for (int i = 0; i < len; ++i) + { + ret.append(QRandomGenerator::global()->bounded(min, max)); + } + + return ret; +} + +QChar genLetter() +{ + // generate random letter from ascii table decimal value 97-122. + + return QChar(static_cast(QRandomGenerator::global()->bounded(97, 122))); +} + +QChar genNum() +{ + // generate random number from ascii table decimal value 48-57. + + return QChar(static_cast(QRandomGenerator::global()->bounded(48, 57))); +} + +QChar genSpecialChar() +{ + static QString specialChars = "`~!@#$%^&*()-_+=[]{}\\|:;\"'<,>.?/"; + + return specialChars[QRandomGenerator::global()->bounded(0, specialChars.size() - 1)]; +} + +int inRange(int pos, int min, int max) +{ + int ret = pos; + + if (pos < min) ret = min; + if (pos > max) ret = max; + + return ret; +} + +void moveCharLeft(int pos, QString &str) +{ + pos = inRange(pos, 0, str.size() - 1); + + QChar chr = str[pos]; + + str.remove(pos, 1); + + if (pos == 0) str.append(chr); + else str.insert(pos - 1, chr); +} + +void moveCharRight(int pos, QString &str) +{ + pos = inRange(pos, 0, str.size() - 1); + + QChar chr = str[pos]; + + str.remove(pos, 1); + + if (pos == str.size() - 1) str.insert(0, chr); + else str.insert(pos + 1, chr); +} + +QString genPw() +{ + QString ret; + + QList seq = genSequence(2, 5, 4); + + for (int i = 0; i < seq[0]; ++i) + { + ret.append(genLetter()); + } + + for (int i = 0; i < seq[1]; ++i) + { + ret.append(genLetter().toUpper()); + } + + for (int i = 0; i < seq[2]; ++i) + { + ret.append(genNum()); + } + + for (int i = 0; i < seq[3]; ++i) + { + ret.append(genSpecialChar()); + } + + seq = genSequence(0, ret.size() - 1, 10); + + bool toggle = false; + + for (int i : seq) + { + if (toggle) moveCharRight(i, ret); + else moveCharLeft(i, ret); + + toggle = !toggle; + } + + return ret; +} + +quint32 initHostRank() { Query db; db.setType(Query::PULL, TABLE_SERV_SETTINGS); - db.addColumn(COLUMN_INITGROUP); + db.addColumn(COLUMN_INITRANK); db.exec(); - return db.getData(COLUMN_INITGROUP).toString(); + return db.getData(COLUMN_INITRANK).toUInt(); } -QByteArray getSalt(const QString &userName, const QString &table) +QByteArray getSalt(const QByteArray &uId, const QString &table) { Query db; db.setType(Query::PULL, table); db.addColumn(COLUMN_SALT); - db.addColumn(COLUMN_USER_ID); - db.addCondition(COLUMN_USERNAME, userName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); - return db.getData(COLUMN_SALT).toByteArray() + db.getData(COLUMN_USER_ID).toByteArray(); + return db.getData(COLUMN_SALT).toByteArray(); } bool createUser(const QString &userName, const QString &email, const QString &dispName, const QString &password) { bool ret = false; - Query db; + Query db; + QByteArray newUId = genUniqueHash(); db.setType(Query::PUSH, TABLE_USERS); db.addColumn(COLUMN_USERNAME, userName); db.addColumn(COLUMN_EMAIL, email); - db.addColumn(COLUMN_GRNAME, initGroup()); + db.addColumn(COLUMN_HOST_RANK, initHostRank()); db.addColumn(COLUMN_DISPLAY_NAME, dispName); db.addColumn(COLUMN_EMAIL_VERIFIED, false); db.addColumn(COLUMN_NEED_PASS, false); db.addColumn(COLUMN_NEED_NAME, false); - db.addColumn(COLUMN_USER_ID, genUniqueHash()); + db.addColumn(COLUMN_USER_ID, newUId); db.addRandBlob(COLUMN_SALT, 128); if (db.exec()) { - ret = updatePassword(userName, password, TABLE_USERS); + ret = updatePassword(newUId, password, TABLE_USERS); } return ret; } -bool createTempPw(const QString &userName, const QString &email, const QString &password) +bool createTempPw(const QByteArray &uId, const QString &password) { bool ret = false; Query db; db.setType(Query::PUSH, TABLE_PW_RECOVERY); - db.addColumn(COLUMN_USERNAME, userName); - db.addColumn(COLUMN_EMAIL, email); + db.addColumn(COLUMN_USER_ID, uId); db.addRandBlob(COLUMN_SALT, 128); if (db.exec()) { - ret = updatePassword(userName, password, TABLE_PW_RECOVERY); + ret = updatePassword(uId, password, TABLE_PW_RECOVERY); } return ret; } -bool updatePassword(const QString &userName, const QString &password, const QString &table, bool requireNewPass) +bool updatePassword(const QByteArray &uId, const QString &password, const QString &table, bool requireNewPass) { bool ret = false; - QByteArray salt = getSalt(userName, table); + QByteArray salt = getSalt(uId, table); if (!salt.isEmpty()) { @@ -175,7 +282,7 @@ bool updatePassword(const QString &userName, const QString &password, const QStr db.setType(Query::UPDATE, table); db.addColumn(COLUMN_HASH, hasher.result()); db.addColumn(COLUMN_NEED_PASS, requireNewPass); - db.addCondition(COLUMN_USERNAME, userName); + db.addCondition(COLUMN_USER_ID, uId); ret = db.exec(); } @@ -183,10 +290,10 @@ bool updatePassword(const QString &userName, const QString &password, const QStr return ret; } -bool auth(const QString &userName, const QString &password, const QString &table) +bool auth(const QByteArray &uId, const QString &password, const QString &table) { bool ret = false; - QByteArray salt = getSalt(userName, table); + QByteArray salt = getSalt(uId, table); if (!salt.isEmpty()) { @@ -198,7 +305,7 @@ bool auth(const QString &userName, const QString &password, const QString &table db.setType(Query::PULL, table); db.addColumn(COLUMN_HASH); - db.addCondition(COLUMN_USERNAME, userName); + db.addCondition(COLUMN_USER_ID, uId); db.exec(); if (db.rows()) @@ -214,7 +321,7 @@ Query::Query(QObject *parent) : QObject(parent) { // this class is an SQL database interface that will be used to store // all persistent data for this application. it works by building - // a query string (qStr + wStr + limit) to be executed by the + // a query string (qStr + jStr + wStr + limit) to be executed by the // QSqlQuery object in the exec() function. // QT's QSqlQuery in a multi-threaded app will only work if the @@ -254,11 +361,17 @@ Query::Query(QObject *parent) : QObject(parent) QString Query::errDetail() { QString ret; + QString errTxt = "none"; + + if (!lastErr.isEmpty()) + { + errTxt = lastErr; + } QTextStream txtOut(&ret); - txtOut << " driver error: " << lastErr << endl; - txtOut << " query: " << qStr << endl; + txtOut << " driver error: " << errTxt << endl; + txtOut << " query: " << qStr << jStr << wStr << limit << endl; txtOut << " db path: " << sqlDataPath() << endl; QFileInfo info = QFileInfo(QFileInfo(sqlDataPath()).path()); @@ -335,6 +448,7 @@ void Query::setType(QueryType qType, const QString &tbl) qStr.clear(); wStr.clear(); + jStr.clear(); limit.clear(); columnList.clear(); bindValues.clear(); @@ -354,7 +468,7 @@ void Query::setType(QueryType qType, const QString &tbl) break; } - case PULL: + case PULL: case INNER_JOIN_PULL: { txt << "SELECT %columns% FROM " << tbl; @@ -392,7 +506,7 @@ void Query::setType(QueryType qType, const QString &tbl) void Query::setQueryLimit(uint value, uint offset) { - if (type == PULL) + if ((type == PULL) || (type == INNER_JOIN_PULL)) { limit = " LIMIT " + QString::number(value) + " OFFSET " + QString::number(offset); } @@ -454,6 +568,16 @@ void Query::addColumn(const QString &column, const QVariant &dataIn) columnsAsPassed.append(column); } +void Query::addTableColumn(const QString &table, const QString &column) +{ + if ((type == INNER_JOIN_PULL) || (type == PULL)) + { + columnList.append(table + "." + column); + } + + columnsAsPassed.append(column); +} + void Query::addRandBlob(const QString &column, int len) { if ((type == PUSH) || (type == UPDATE)) @@ -476,9 +600,41 @@ void Query::addRandBlob(const QString &column, int len) columnsAsPassed.append(column); } -void Query::addCondition(const QString &column, const QVariant &data, Condition cond) +void Query::addJoinCondition(const QString &column, const QString &joinTable, Condition cond) { - if ((type == PULL) || (type == UPDATE) || (type == DEL)) + if (type == INNER_JOIN_PULL) + { + QTextStream txt(&jStr); + + if (jStr.contains(joinTable)) + { + txt << " AND "; + } + else + { + txt << " INNER JOIN " << joinTable << " ON "; + } + + if (cond == NOT_EQUAL) + { + txt << table << "." << column << " != " << joinTable << "." << column; + } + else if (cond == EQUAL) + { + txt << table << "." << column << " = " << joinTable << "." << column; + } + } +} + +void Query::clearConditions() +{ + wStr.clear(); + whereBinds.clear(); +} + +void Query::addCondition(const QString &column, const QVariant &data, Condition cond, const QString &tbl) +{ + if ((type == PULL) || (type == UPDATE) || (type == DEL) || (type == INNER_JOIN_PULL)) { QTextStream txt(&wStr); @@ -487,13 +643,27 @@ void Query::addCondition(const QString &column, const QVariant &data, Condition if (cond == NOT_EQUAL) { - txt << column << " != :where" << whereBinds.size(); + if (tbl.isEmpty()) + { + txt << column << " != :where" << whereBinds.size(); + } + else + { + txt << tbl << "." << column << " != :where" << whereBinds.size(); + } whereBinds.append(data); } else if (cond == EQUAL) { - txt << column << " = :where" << whereBinds.size(); + if (tbl.isEmpty()) + { + txt << column << " = :where" << whereBinds.size(); + } + else + { + txt << tbl << "." << column << " = :where" << whereBinds.size(); + } whereBinds.append(data); } @@ -520,21 +690,18 @@ void Query::addCondition(const QString &column, const QVariant &data, Condition escapedData.insert(0, "%"); } - txt << "LIKE('" << escapedData << "', " << column << ", '" << escape << "') > 0"; + if (tbl.isEmpty()) + { + txt << "LIKE('" << escapedData << "', " << column << ", '" << escape << "') > 0"; + } + else + { + txt << "LIKE('" << escapedData << "', " << tbl << "." << column << ", '" << escape << "') > 0"; + } } } } -void Query::addComma(QString &str, bool *toggle) -{ - if (!*toggle) - { - str.append(","); - - *toggle = true; - } -} - void Query::addUnique(const QString &column) { if ((columnsAsPassed.contains(column)) && (type == CREATE_TABLE)) @@ -600,6 +767,7 @@ void Query::preExec() QString columnsStr = QStringList(columnList).join(", "); qStr.replace("%columns%", columnsStr); + data.clear(); if (type == PUSH) { @@ -660,7 +828,7 @@ bool Query::exec() QSqlQuery query(getDatabase()); - query.prepare(qStr + wStr + limit + ";"); + query.prepare(qStr + jStr + wStr + limit + ";"); for (int i = 0; i < bindValues.size(); ++i) { @@ -702,6 +870,11 @@ bool Query::exec() return queryOk; } +QList > &Query::allData() +{ + return data; +} + QVariant Query::getData(const QString &column, int row) { QVariant ret; @@ -723,8 +896,14 @@ int Query::rows() { int ret = 0; - if (type == PULL) ret = data.size(); - else ret = rowsAffected; + if ((type == PULL) || (type == INNER_JOIN_PULL)) + { + ret = data.size(); + } + else + { + ret = rowsAffected; + } return ret; } diff --git a/src/db.h b/src/db.h index d106654..4814483 100644 --- a/src/db.h +++ b/src/db.h @@ -37,38 +37,26 @@ #include "shell.h" #define APP_NAME "MRCI" -#define APP_VER "1.1.3" +#define APP_VER "2.0.0" #define APP_TARGET "mrci" #ifdef Q_OS_WIN -#define DEFAULT_ZIPBIN "%ProgramW6432%\\7-Zip\\7z.exe" #define DEFAULT_MAILBIN "%COMSPEC%" #define DEFAULT_MAIL_SEND "echo %message_body% | mutt -s %subject% %target_email%" #define DEFAULT_DB_PATH "%LOCALAPPDATA%\\%EXENAME%\\data.db" -#define DEFAULT_MOD_PATH "%LOCALAPPDATA%\\%EXENAME%\\modules" -#define DEFAULT_PIPE_PATH "%LOCALAPPDATA%\\%EXENAME%\\pipes" #else -#define DEFAULT_ZIPBIN "/usr/bin/7z" #define DEFAULT_MAILBIN "/bin/sh" #define DEFAULT_MAIL_SEND "-c \"echo %message_body% | mutt -s %subject% %target_email%\"" #define DEFAULT_DB_PATH "$HOME/.$EXENAME/data.db" -#define DEFAULT_MOD_PATH "$HOME/.$EXENAME/modules" -#define DEFAULT_PIPE_PATH "$HOME/.$EXENAME/pipes" #endif #define ENV_DB_PATH "MRCI_DB_PATH" -#define ENV_MOD_PATH "MRCI_MOD_PATH" -#define ENV_PIPE_PATH "MRCI_PIPE_PATH" #define ENV_EXENAME "EXENAME" #define ROOT_USER "root" -#define DEFAULT_UGROUP "users" -#define DEFAULT_PASSWRD "R00tPa$$w0rd" -#define OUTPUT_DIR_SUB "%output_dir%" -#define INPUT_DIR_SUB "%input_dir%" #define SUBJECT_SUB "%subject%" #define MSG_SUB "%message_body%" #define TARGET_EMAIL_SUB "%target_email%" @@ -77,8 +65,6 @@ #define USERNAME_SUB "%user_name%" #define DATE_SUB "%date%" #define INTERN_MOD_NAME ":internal_mod" -#define DEFAULT_ZIPEXTRACT "x -y %input_dir% -o%output_dir%" -#define DEFAULT_ZIPCOMPRESS "a -y %output_dir% %input_dir%" #define DEFAULT_CONFIRM_SUBJECT "Email Verification" #define DEFAULT_TEMP_PW_SUBJECT "Password Reset" #define DEFAULT_LISTEN_ADDRESS "127.0.0.1" @@ -87,6 +73,7 @@ #define DEFAULT_LOCK_LIMIT 20 #define DEFAULT_MAXSESSIONS 100 #define DEFAULT_MAX_SUBS 50 +#define DEFAULT_INIT_RANK 2 #define MAX_LS_ENTRIES 50 #define MAX_CMDS_PER_MOD 256 @@ -106,7 +93,6 @@ Date requested: %date%." #define TABLE_IPHIST "ip_history" #define TABLE_IPBANS "ban_list" #define TABLE_USERS "users" -#define TABLE_GROUPS "groups" #define TABLE_SERV_SETTINGS "host_settings" #define TABLE_CMD_RANKS "command_ranks" #define TABLE_AUTH_LOG "auth_log" @@ -125,17 +111,14 @@ Date requested: %date%." #define COLUMN_SESSION_ID "session_id" #define COLUMN_TIME "time_stamp" #define COLUMN_USERNAME "user_name" -#define COLUMN_GRNAME "group_name" #define COLUMN_PORT "port" #define COLUMN_BAN_LIMIT "auto_ban_threshold" #define COLUMN_LOCK_LIMIT "auto_lock_threshold" #define COLUMN_MAXSESSIONS "max_sessions" #define COLUMN_HOST_RANK "host_rank" -#define COLUMN_COMMAND "command_obj_name" -#define COLUMN_MOD_NAME "mod_name" -#define COLUMN_MOD_MAIN "mod_main_lib" -#define COLUMN_CMD_ID_OFFS "command_id_offset" -#define COLUMN_INITGROUP "initial_group" +#define COLUMN_COMMAND "command_name" +#define COLUMN_MOD_MAIN "module_executable" +#define COLUMN_INITRANK "initial_host_rank" #define COLUMN_PUB_USERS "allow_public_registrations" #define COLUMN_SALT "salt" #define COLUMN_HASH "hash" @@ -173,18 +156,27 @@ Date requested: %date%." #define COLUMN_LOWEST_LEVEL "lowest_access_level" #define COLUMN_ACCESS_LEVEL "access_level" #define COLUMN_MAX_SUB_CH "max_sub_channels" +#define COLUMN_APP_NAME "client_app" +#define COLUMN_DEFAULT_PASS "default_password" +QString genPw(); +QList genSequence(int min, int max, int len); +QChar genLetter(); +QChar genNum(); +QChar genSpecialChar(); +int inRange(int pos, int min, int max); QString sqlDataPath(); QString columnType(const QString &column); -QString columnDefault(const QString &column, const QString &table); -QString initGroup(); -QByteArray getSalt(const QString &userName, const QString &table); +quint32 initHostRank(); +QByteArray getSalt(const QByteArray &uId, const QString &table); QByteArray genUniqueHash(); bool createUser(const QString &userName, const QString &email, const QString &dispName, const QString &password); -bool createTempPw(const QString &userName, const QString &email, const QString &password); -bool updatePassword(const QString &userName, const QString &password, const QString &table, bool requireNewPass = false); -bool auth(const QString &userName, const QString &password, const QString &table); +bool createTempPw(const QByteArray &uId, const QString &password); +bool updatePassword(const QByteArray &uId, const QString &password, const QString &table, bool requireNewPass = false); +bool auth(const QByteArray &uId, const QString &password, const QString &table); void cleanupDbConnection(); +void moveCharLeft(int pos, QString &str); +void moveCharRight(int pos, QString &str); class Query : public QObject { @@ -198,6 +190,7 @@ public: PUSH, PULL, DEL, + INNER_JOIN_PULL, CREATE_TABLE, ALTER_TABLE }; @@ -225,30 +218,34 @@ public: explicit Query(QObject *parent = nullptr); - void addRandBlob(const QString &column, int len); - void addCondition(const QString &column, const QVariant &data, Condition cond = EQUAL); - void addUnique(const QString &column); - void setPrimary(const QString &column); - void setPrimaryAsc(const QString &column); - void setPrimaryDesc(const QString &column); - void setQueryLimit(uint value, uint offset = 0); - void increment(const QString &column, double value); - void decrement(const QString &column, double value); - void addForeign(const QString &column, const QString &refTable, const QString &refColum, FKAction onDel = RESTRICT, FKAction onUpdate = RESTRICT); - void addColumn(const QString &column); - void addColumn(const QString &column, const QVariant &dataIn); - void setType(QueryType qType, const QString &tbl); - void enableForeignKeys(bool state); - void setTextEncoding(const QString &encoding); - int rows(); - int columns(); - bool exec(); - bool createExecuted(); - bool inErrorstate(); - QStringList tables(); - QStringList columnsInTable(const QString &tbl); - QVariant getData(const QString &column, int row = 0); - QString errDetail(); + void addRandBlob(const QString &column, int len); + void addCondition(const QString &column, const QVariant &data, Condition cond = EQUAL, const QString &tbl = QString()); + void addJoinCondition(const QString &column, const QString &joinTable, Condition cond = EQUAL); + void addUnique(const QString &column); + void setPrimary(const QString &column); + void setPrimaryAsc(const QString &column); + void setPrimaryDesc(const QString &column); + void setQueryLimit(uint value, uint offset = 0); + void increment(const QString &column, double value); + void decrement(const QString &column, double value); + void addForeign(const QString &column, const QString &refTable, const QString &refColum, FKAction onDel = RESTRICT, FKAction onUpdate = RESTRICT); + void addColumn(const QString &column); + void addColumn(const QString &column, const QVariant &dataIn); + void addTableColumn(const QString &table, const QString &column); + void setType(QueryType qType, const QString &tbl); + void enableForeignKeys(bool state); + void setTextEncoding(const QString &encoding); + void clearConditions(); + int rows(); + int columns(); + bool exec(); + bool createExecuted(); + bool inErrorstate(); + QStringList tables(); + QStringList columnsInTable(const QString &tbl); + QVariant getData(const QString &column, int row = 0); + QString errDetail(); + QList > &allData(); private: @@ -261,6 +258,7 @@ private: QString limit; QString qStr; QString wStr; + QString jStr; QueryType type; QList directBind; QStringList columnsAsPassed; @@ -272,7 +270,6 @@ private: bool createRedirect(); void postUpdate(); void preExec(); - void addComma(QString &str, bool *toggle); void changeValue(const QString &column, double value, const QString &sign); }; diff --git a/src/db_setup.cpp b/src/db_setup.cpp index 6cc9d2a..99eb583 100644 --- a/src/db_setup.cpp +++ b/src/db_setup.cpp @@ -25,6 +25,8 @@ bool setupDb(QString *errMsg) Query query(QThread::currentThread()); Query defaults(QThread::currentThread()); + QString randPw; + if (query.inErrorstate()) { ret = false; @@ -38,8 +40,8 @@ bool setupDb(QString *errMsg) query.setType(Query::CREATE_TABLE, TABLE_IPHIST); query.addColumn(COLUMN_IPADDR); query.addColumn(COLUMN_TIME); - query.addColumn(COLUMN_CLIENT_VER); query.addColumn(COLUMN_SESSION_ID); + query.addColumn(COLUMN_APP_NAME); query.addColumn(COLUMN_LOGENTRY); ret = query.exec(); @@ -48,12 +50,7 @@ bool setupDb(QString *errMsg) if (ret) { query.setType(Query::CREATE_TABLE, TABLE_MODULES); - query.addColumn(COLUMN_MOD_NAME); query.addColumn(COLUMN_MOD_MAIN); - query.addColumn(COLUMN_LOCKED); - query.addColumn(COLUMN_CMD_ID_OFFS); - query.setPrimary(COLUMN_MOD_NAME); - query.addUnique(COLUMN_MOD_NAME); query.addUnique(COLUMN_MOD_MAIN); ret = query.exec(); @@ -75,9 +72,7 @@ bool setupDb(QString *errMsg) query.setType(Query::CREATE_TABLE, TABLE_CMD_RANKS); query.addColumn(COLUMN_COMMAND); query.addColumn(COLUMN_HOST_RANK); - query.addColumn(COLUMN_MOD_NAME); - query.setPrimary(COLUMN_COMMAND); - query.addUnique(COLUMN_COMMAND); + query.addColumn(COLUMN_MOD_MAIN); ret = query.exec(); } @@ -114,41 +109,11 @@ bool setupDb(QString *errMsg) ret = query.exec(); } - if (ret) - { - query.setType(Query::CREATE_TABLE, TABLE_GROUPS); - query.addColumn(COLUMN_GRNAME); - query.addColumn(COLUMN_HOST_RANK); - query.setPrimary(COLUMN_GRNAME); - query.addUnique(COLUMN_GRNAME); - - ret = query.exec(); - - if (query.createExecuted()) - { - query.setType(Query::PUSH, TABLE_GROUPS); - query.addColumn(COLUMN_GRNAME, ROOT_USER); - query.addColumn(COLUMN_HOST_RANK, 1); - - ret = query.exec(); - - if (ret) - { - query.setType(Query::PUSH, TABLE_GROUPS); - query.addColumn(COLUMN_GRNAME, DEFAULT_UGROUP); - query.addColumn(COLUMN_HOST_RANK, 2); - - ret = query.exec(); - } - } - } - if (ret) { query.setType(Query::CREATE_TABLE, TABLE_USERS); query.addColumn(COLUMN_USERNAME); query.addColumn(COLUMN_USER_ID); - query.addColumn(COLUMN_GRNAME); query.addColumn(COLUMN_EMAIL); query.addColumn(COLUMN_HASH); query.addColumn(COLUMN_SALT); @@ -158,31 +123,34 @@ bool setupDb(QString *errMsg) query.addColumn(COLUMN_LOCKED); query.addColumn(COLUMN_EMAIL_VERIFIED); query.addColumn(COLUMN_DISPLAY_NAME); - query.setPrimary(COLUMN_USERNAME); + query.addColumn(COLUMN_HOST_RANK); + query.setPrimary(COLUMN_USER_ID); query.addUnique(COLUMN_USERNAME); query.addUnique(COLUMN_EMAIL); query.addUnique(COLUMN_USER_ID); - query.addForeign(COLUMN_GRNAME, TABLE_GROUPS, COLUMN_GRNAME, Query::RESTRICT, Query::CASCADE); ret = query.exec(); if (query.createExecuted()) { + QByteArray uId = genUniqueHash(); + query.setType(Query::PUSH, TABLE_USERS); query.addColumn(COLUMN_USERNAME, ROOT_USER); - query.addColumn(COLUMN_GRNAME, ROOT_USER); + query.addColumn(COLUMN_HOST_RANK, 1); query.addColumn(COLUMN_NEED_PASS, true); query.addColumn(COLUMN_NEED_NAME, false); query.addColumn(COLUMN_LOCKED, false); query.addColumn(COLUMN_EMAIL_VERIFIED, false); - query.addColumn(COLUMN_USER_ID, genUniqueHash()); + query.addColumn(COLUMN_USER_ID, uId); query.addRandBlob(COLUMN_SALT, 128); ret = query.exec(); if (ret) { - ret = updatePassword(ROOT_USER, DEFAULT_PASSWRD, TABLE_USERS, true); + randPw = genPw(); + ret = updatePassword(uId, randPw, TABLE_USERS, true); } } } @@ -192,12 +160,11 @@ bool setupDb(QString *errMsg) query.setType(Query::CREATE_TABLE, TABLE_AUTH_LOG); query.addColumn(COLUMN_IPADDR); query.addColumn(COLUMN_TIME); - query.addColumn(COLUMN_USERNAME); + query.addColumn(COLUMN_USER_ID); query.addColumn(COLUMN_AUTH_ATTEMPT); query.addColumn(COLUMN_RECOVER_ATTEMPT); query.addColumn(COLUMN_ACCEPTED); query.addColumn(COLUMN_COUNT); - query.addForeign(COLUMN_USERNAME, TABLE_USERS, COLUMN_USERNAME, Query::CASCADE, Query::CASCADE); ret = query.exec(); } @@ -206,16 +173,10 @@ bool setupDb(QString *errMsg) { query.setType(Query::CREATE_TABLE, TABLE_PW_RECOVERY); query.addColumn(COLUMN_TIME); - query.addColumn(COLUMN_USERNAME); - query.addColumn(COLUMN_EMAIL); query.addColumn(COLUMN_HASH); query.addColumn(COLUMN_SALT); query.addColumn(COLUMN_USER_ID); - query.addUnique(COLUMN_USERNAME); - query.addUnique(COLUMN_EMAIL); - query.addForeign(COLUMN_USER_ID, TABLE_USERS, COLUMN_USER_ID, Query::CASCADE, Query::RESTRICT); - query.addForeign(COLUMN_USERNAME, TABLE_USERS, COLUMN_USERNAME, Query::CASCADE, Query::CASCADE); - query.addForeign(COLUMN_EMAIL, TABLE_USERS, COLUMN_EMAIL, Query::CASCADE, Query::CASCADE); + query.addForeign(COLUMN_USER_ID, TABLE_USERS, COLUMN_USER_ID, Query::CASCADE, Query::CASCADE); ret = query.exec(); } @@ -224,13 +185,11 @@ bool setupDb(QString *errMsg) { query.setType(Query::CREATE_TABLE, TABLE_CH_MEMBERS); query.addColumn(COLUMN_CHANNEL_ID); - query.addColumn(COLUMN_CHANNEL_NAME); - query.addColumn(COLUMN_USERNAME); + query.addColumn(COLUMN_USER_ID); query.addColumn(COLUMN_PENDING_INVITE); query.addColumn(COLUMN_ACCESS_LEVEL); query.addForeign(COLUMN_CHANNEL_ID, TABLE_CHANNELS, COLUMN_CHANNEL_ID, Query::CASCADE, Query::CASCADE); - query.addForeign(COLUMN_CHANNEL_NAME, TABLE_CHANNELS, COLUMN_CHANNEL_NAME, Query::CASCADE, Query::CASCADE); - query.addForeign(COLUMN_USERNAME, TABLE_USERS, COLUMN_USERNAME, Query::CASCADE, Query::CASCADE); + query.addForeign(COLUMN_USER_ID, TABLE_USERS, COLUMN_USER_ID, Query::CASCADE, Query::CASCADE); ret = query.exec(); } @@ -241,11 +200,9 @@ bool setupDb(QString *errMsg) query.addColumn(COLUMN_CHANNEL_ID); query.addColumn(COLUMN_SUB_CH_NAME); query.addColumn(COLUMN_SUB_CH_ID); - query.addColumn(COLUMN_CHANNEL_NAME); query.addColumn(COLUMN_LOWEST_LEVEL); query.addColumn(COLUMN_ACTIVE_UPDATE); query.addForeign(COLUMN_CHANNEL_ID, TABLE_CHANNELS, COLUMN_CHANNEL_ID, Query::CASCADE, Query::CASCADE); - query.addForeign(COLUMN_CHANNEL_NAME, TABLE_CHANNELS, COLUMN_CHANNEL_NAME, Query::CASCADE, Query::CASCADE); ret = query.exec(); } @@ -254,11 +211,9 @@ bool setupDb(QString *errMsg) { query.setType(Query::CREATE_TABLE, TABLE_RDONLY_CAST); query.addColumn(COLUMN_CHANNEL_ID); - query.addColumn(COLUMN_CHANNEL_NAME); query.addColumn(COLUMN_SUB_CH_ID); query.addColumn(COLUMN_ACCESS_LEVEL); query.addForeign(COLUMN_CHANNEL_ID, TABLE_CHANNELS, COLUMN_CHANNEL_ID, Query::CASCADE, Query::CASCADE); - query.addForeign(COLUMN_CHANNEL_NAME, TABLE_CHANNELS, COLUMN_CHANNEL_NAME, Query::CASCADE, Query::CASCADE); } if (ret) @@ -273,7 +228,7 @@ bool setupDb(QString *errMsg) query.addColumn(COLUMN_ZIPBIN); query.addColumn(COLUMN_ZIPCOMPRESS); query.addColumn(COLUMN_ZIPEXTRACT); - query.addColumn(COLUMN_INITGROUP); + query.addColumn(COLUMN_INITRANK); query.addColumn(COLUMN_MAILERBIN); query.addColumn(COLUMN_MAIL_SEND); query.addColumn(COLUMN_CONFIRM_SUBJECT); @@ -284,10 +239,15 @@ bool setupDb(QString *errMsg) query.addColumn(COLUMN_ENABLE_PW_RESET); query.addColumn(COLUMN_ACTIVE_UPDATE); query.addColumn(COLUMN_MAX_SUB_CH); - query.addForeign(COLUMN_INITGROUP, TABLE_GROUPS, COLUMN_GRNAME, Query::RESTRICT, Query::CASCADE); + query.addColumn(COLUMN_DEFAULT_PASS); ret = query.exec(); + if (randPw.isEmpty()) + { + randPw = genPw(); + } + if (query.createExecuted()) { query.setType(Query::PUSH, TABLE_SERV_SETTINGS); @@ -297,10 +257,7 @@ bool setupDb(QString *errMsg) query.addColumn(COLUMN_LOCK_LIMIT, DEFAULT_LOCK_LIMIT); query.addColumn(COLUMN_MAXSESSIONS, DEFAULT_MAXSESSIONS); query.addColumn(COLUMN_PUB_USERS, false); - query.addColumn(COLUMN_ZIPBIN, DEFAULT_ZIPBIN); - query.addColumn(COLUMN_ZIPCOMPRESS, DEFAULT_ZIPCOMPRESS); - query.addColumn(COLUMN_ZIPEXTRACT, DEFAULT_ZIPEXTRACT); - query.addColumn(COLUMN_INITGROUP, DEFAULT_UGROUP); + query.addColumn(COLUMN_INITRANK, DEFAULT_INIT_RANK); query.addColumn(COLUMN_MAILERBIN, DEFAULT_MAILBIN); query.addColumn(COLUMN_MAIL_SEND, DEFAULT_MAIL_SEND); query.addColumn(COLUMN_CONFIRM_SUBJECT, DEFAULT_CONFIRM_SUBJECT); @@ -311,6 +268,7 @@ bool setupDb(QString *errMsg) query.addColumn(COLUMN_ENABLE_PW_RESET, true); query.addColumn(COLUMN_ACTIVE_UPDATE, true); query.addColumn(COLUMN_MAX_SUB_CH, DEFAULT_MAX_SUBS); + query.addColumn(COLUMN_DEFAULT_PASS, randPw); ret = query.exec(); } @@ -326,7 +284,7 @@ bool setupDb(QString *errMsg) query.addColumn(COLUMN_ZIPBIN); query.addColumn(COLUMN_ZIPCOMPRESS); query.addColumn(COLUMN_ZIPEXTRACT); - query.addColumn(COLUMN_INITGROUP); + query.addColumn(COLUMN_INITRANK); query.addColumn(COLUMN_MAILERBIN); query.addColumn(COLUMN_MAIL_SEND); query.addColumn(COLUMN_CONFIRM_SUBJECT); @@ -337,6 +295,7 @@ bool setupDb(QString *errMsg) query.addColumn(COLUMN_ENABLE_PW_RESET); query.addColumn(COLUMN_ACTIVE_UPDATE); query.addColumn(COLUMN_MAX_SUB_CH); + query.addColumn(COLUMN_DEFAULT_PASS); ret = query.exec(); @@ -350,10 +309,7 @@ bool setupDb(QString *errMsg) if (query.getData(COLUMN_LOCK_LIMIT).isNull()) defaults.addColumn(COLUMN_LOCK_LIMIT, DEFAULT_LOCK_LIMIT); if (query.getData(COLUMN_MAXSESSIONS).isNull()) defaults.addColumn(COLUMN_MAXSESSIONS, DEFAULT_MAXSESSIONS); if (query.getData(COLUMN_PUB_USERS).isNull()) defaults.addColumn(COLUMN_PUB_USERS, false); - if (query.getData(COLUMN_ZIPBIN).isNull()) defaults.addColumn(COLUMN_ZIPBIN, DEFAULT_ZIPBIN); - if (query.getData(COLUMN_ZIPCOMPRESS).isNull()) defaults.addColumn(COLUMN_ZIPCOMPRESS, DEFAULT_ZIPCOMPRESS); - if (query.getData(COLUMN_ZIPEXTRACT).isNull()) defaults.addColumn(COLUMN_ZIPEXTRACT, DEFAULT_ZIPEXTRACT); - if (query.getData(COLUMN_INITGROUP).isNull()) defaults.addColumn(COLUMN_INITGROUP, DEFAULT_UGROUP); + if (query.getData(COLUMN_INITRANK).isNull()) defaults.addColumn(COLUMN_INITRANK, DEFAULT_INIT_RANK); if (query.getData(COLUMN_MAILERBIN).isNull()) defaults.addColumn(COLUMN_MAILERBIN, DEFAULT_MAILBIN); if (query.getData(COLUMN_MAIL_SEND).isNull()) defaults.addColumn(COLUMN_MAIL_SEND, DEFAULT_MAIL_SEND); if (query.getData(COLUMN_CONFIRM_SUBJECT).isNull()) defaults.addColumn(COLUMN_CONFIRM_SUBJECT, DEFAULT_CONFIRM_SUBJECT); @@ -364,6 +320,7 @@ bool setupDb(QString *errMsg) if (query.getData(COLUMN_ENABLE_PW_RESET).isNull()) defaults.addColumn(COLUMN_ENABLE_PW_RESET, true); if (query.getData(COLUMN_ACTIVE_UPDATE).isNull()) defaults.addColumn(COLUMN_ACTIVE_UPDATE, true); if (query.getData(COLUMN_MAX_SUB_CH).isNull()) defaults.addColumn(COLUMN_MAX_SUB_CH, DEFAULT_MAX_SUBS); + if (query.getData(COLUMN_DEFAULT_PASS).isNull()) defaults.addColumn(COLUMN_DEFAULT_PASS, randPw); if (defaults.columns()) { diff --git a/src/int_loader.cpp b/src/int_loader.cpp deleted file mode 100644 index 4ae300a..0000000 --- a/src/int_loader.cpp +++ /dev/null @@ -1,347 +0,0 @@ -#include "int_loader.h" - -// This file is part of MRCI. - -// MRCI is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// MRCI is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with MRCI under the LICENSE.md file. If not, see -// . - -InternalCommandLoader::InternalCommandLoader(RWSharedObjs *sharedData, QObject *parent) : CommandLoader(parent) -{ - rwShared = sharedData; - pubReg = false; - emailConfirmation = false; - passwrdResets = false; - - // This is used as an automated way to update the Internal_Commands doc for the source code documentation - // keep it commented for release code. - - //makeDocHeader("/docs/Internal_Commands.md"); - - objNames << CloseHost::cmdName(); - objNames << RestartHost::cmdName(); - objNames << Auth::cmdName(); - objNames << ListBans::cmdName(); - objNames << BanIP::cmdName(); - objNames << UnBanIP::cmdName(); - objNames << Cast::cmdName(); - objNames << OpenSubChannel::cmdName(); - objNames << CloseSubChannel::cmdName(); - objNames << LsOpenChannels::cmdName(); - objNames << ListGroups::cmdName(); - objNames << CreateGroup::cmdName(); - objNames << TransGroup::cmdName(); - objNames << ListCommands::cmdName(); - objNames << HostInfo::cmdName(); - objNames << IPHist::cmdName(); - objNames << ListMods::cmdName(); - objNames << DelMod::cmdName(); - objNames << UploadMod::cmdName(); - objNames << ListUsers::cmdName(); - objNames << CreateUser::cmdName(); - objNames << RecoverAcct::cmdName(); - objNames << ResetPwRequest::cmdName(); - objNames << VerifyEmail::cmdName(); - objNames << Resume::cmdName(); - objNames << Pause::cmdName(); - objNames << Term::cmdName(); - objNames << AuthLog::cmdName(); - objNames << LsCmdRanks::cmdName(); - objNames << RemoveCmdRank::cmdName(); - objNames << AssignCmdRank::cmdName(); - objNames << ServSettings::cmdName(); - objNames << LockUser::cmdName(); - objNames << NameChangeRequest::cmdName(); - objNames << PasswordChangeRequest::cmdName(); - objNames << ChangeEmail::cmdName(); - objNames << OverWriteEmail::cmdName(); - objNames << ChangeDispName::cmdName(); - objNames << ChangeUsername::cmdName(); - objNames << ChangePassword::cmdName(); - objNames << RemoveUser::cmdName(); - objNames << ChangeGroup::cmdName(); - objNames << IsEmailVerified::cmdName(); - objNames << SetEmailTemplate::cmdName(); - objNames << PreviewEmail::cmdName(); - objNames << MyInfo::cmdName(); - objNames << DownloadFile::cmdName(); - objNames << UploadFile::cmdName(); - objNames << Delete::cmdName(); - objNames << Copy::cmdName(); - objNames << Move::cmdName(); - objNames << ListFiles::cmdName(); - objNames << FileInfo::cmdName(); - objNames << MakePath::cmdName(); - objNames << ChangeDir::cmdName(); - objNames << ListDBG::cmdName(); - objNames << ListCerts::cmdName(); - objNames << CertInfo::cmdName(); - objNames << AddCert::cmdName(); - objNames << RemoveCert::cmdName(); - objNames << ToPeer::cmdName(); - objNames << LsP2P::cmdName(); - objNames << P2POpen::cmdName(); - objNames << P2PClose::cmdName(); - objNames << P2PRequest::cmdName(); - objNames << PingPeers::cmdName(); - objNames << CreateChannel::cmdName(); - objNames << RemoveChannel::cmdName(); - objNames << RenameChannel::cmdName(); - objNames << SetActiveState::cmdName(); - objNames << CreateSubCh::cmdName(); - objNames << RemoveSubCh::cmdName(); - objNames << RenameSubCh::cmdName(); - objNames << ListChannels::cmdName(); - objNames << ListSubCh::cmdName(); - objNames << SearchChannels::cmdName(); - objNames << InviteToCh::cmdName(); - objNames << DeclineChInvite::cmdName(); - objNames << AcceptChInvite::cmdName(); - objNames << RemoveChMember::cmdName(); - objNames << SetMemberLevel::cmdName(); - objNames << SetSubAcessLevel::cmdName(); - objNames << ListMembers::cmdName(); - objNames << AddRDOnlyFlag::cmdName(); - objNames << RemoveRDOnlyFlag::cmdName(); - objNames << ListRDonlyFlags::cmdName(); - objNames << SetGroupRank::cmdName(); - objNames << CmdInfo::cmdName(); - objNames << OwnerOverride::cmdName(); - objNames << Tree::cmdName(); -} - -void InternalCommandLoader::loadSettings() -{ - Query db(this); - - db.setType(Query::PULL, TABLE_SERV_SETTINGS); - db.addColumn(COLUMN_PUB_USERS); - db.addColumn(COLUMN_ENABLE_CONFIRM); - db.addColumn(COLUMN_ENABLE_PW_RESET); - db.exec(); - - pubReg = db.getData(COLUMN_PUB_USERS).toBool(); - emailConfirmation = db.getData(COLUMN_ENABLE_CONFIRM).toBool(); - passwrdResets = db.getData(COLUMN_ENABLE_PW_RESET).toBool(); -} - -bool InternalCommandLoader::exists(const QString &cmdName) -{ - return objNames.contains(cmdName, Qt::CaseInsensitive); -} - -QStringList InternalCommandLoader::cmdList() -{ - loadSettings(); - - if (emailConfirmation && !exists(VerifyEmail::cmdName())) - { - objNames.append(VerifyEmail::cmdName()); - } - else if (!emailConfirmation && exists(VerifyEmail::cmdName())) - { - objNames.removeOne(VerifyEmail::cmdName()); - } - - return objNames; -} - -QStringList InternalCommandLoader::pubCmdList() -{ - loadSettings(); - - QStringList ret; - - ret << ListCommands::cmdName() << Auth::cmdName() << MyInfo::cmdName(); - - if (pubReg) - { - ret << CreateUser::cmdName(); - } - - if (passwrdResets) - { - ret << ResetPwRequest::cmdName() << RecoverAcct::cmdName(); - } - - return ret; -} - -QStringList InternalCommandLoader::rankExemptList() -{ - QStringList ret; - - ret << Auth::cmdName(); - ret << MyInfo::cmdName(); - ret << ChangeDispName::cmdName(); - ret << ChangeUsername::cmdName(); - ret << ChangePassword::cmdName(); - ret << ChangeEmail::cmdName(); - ret << ListCommands::cmdName(); - ret << VerifyEmail::cmdName(); - ret << IsEmailVerified::cmdName(); - ret << Resume::cmdName(); - ret << Pause::cmdName(); - ret << Term::cmdName(); - ret << CmdInfo::cmdName(); - - return ret; -} - -InternCommand *InternalCommandLoader::cmdObj(const QString &name) -{ - InternCommand *ret = nullptr; - - if (objNames.contains(name, Qt::CaseInsensitive)) - { - if (noCaseMatch(name, CloseHost::cmdName())) ret = new CloseHost(this); - else if (noCaseMatch(name, RestartHost::cmdName())) ret = new RestartHost(this); - else if (noCaseMatch(name, Auth::cmdName())) ret = new Auth(this); - else if (noCaseMatch(name, ListBans::cmdName())) ret = new ListBans(this); - else if (noCaseMatch(name, BanIP::cmdName())) ret = new BanIP(this); - else if (noCaseMatch(name, UnBanIP::cmdName())) ret = new UnBanIP(this); - else if (noCaseMatch(name, Cast::cmdName())) ret = new Cast(this); - else if (noCaseMatch(name, OpenSubChannel::cmdName())) ret = new OpenSubChannel(this); - else if (noCaseMatch(name, CloseSubChannel::cmdName())) ret = new CloseSubChannel(this); - else if (noCaseMatch(name, LsOpenChannels::cmdName())) ret = new LsOpenChannels(this); - else if (noCaseMatch(name, ListGroups::cmdName())) ret = new ListGroups(this); - else if (noCaseMatch(name, CreateGroup::cmdName())) ret = new CreateGroup(this); - else if (noCaseMatch(name, TransGroup::cmdName())) ret = new TransGroup(this); - else if (noCaseMatch(name, ListCommands::cmdName())) ret = new ListCommands(this); - else if (noCaseMatch(name, HostInfo::cmdName())) ret = new HostInfo(this); - else if (noCaseMatch(name, IPHist::cmdName())) ret = new IPHist(this); - else if (noCaseMatch(name, ListMods::cmdName())) ret = new ListMods(this); - else if (noCaseMatch(name, DelMod::cmdName())) ret = new DelMod(this); - else if (noCaseMatch(name, UploadMod::cmdName())) ret = new UploadMod(this); - else if (noCaseMatch(name, ListUsers::cmdName())) ret = new ListUsers(this); - else if (noCaseMatch(name, CreateUser::cmdName())) ret = new CreateUser(this); - else if (noCaseMatch(name, RecoverAcct::cmdName())) ret = new RecoverAcct(this); - else if (noCaseMatch(name, ResetPwRequest::cmdName())) ret = new ResetPwRequest(this); - else if (noCaseMatch(name, VerifyEmail::cmdName())) ret = new VerifyEmail(this); - else if (noCaseMatch(name, Resume::cmdName())) ret = new Resume(this); - else if (noCaseMatch(name, Pause::cmdName())) ret = new Pause(this); - else if (noCaseMatch(name, Term::cmdName())) ret = new Term(this); - else if (noCaseMatch(name, AuthLog::cmdName())) ret = new AuthLog(this); - else if (noCaseMatch(name, LsCmdRanks::cmdName())) ret = new LsCmdRanks(this); - else if (noCaseMatch(name, RemoveCmdRank::cmdName())) ret = new RemoveCmdRank(this); - else if (noCaseMatch(name, AssignCmdRank::cmdName())) ret = new AssignCmdRank(this); - else if (noCaseMatch(name, ServSettings::cmdName())) ret = new ServSettings(this); - else if (noCaseMatch(name, LockUser::cmdName())) ret = new LockUser(this); - else if (noCaseMatch(name, NameChangeRequest::cmdName())) ret = new NameChangeRequest(this); - else if (noCaseMatch(name, PasswordChangeRequest::cmdName())) ret = new PasswordChangeRequest(this); - else if (noCaseMatch(name, ChangeEmail::cmdName())) ret = new ChangeEmail(this); - else if (noCaseMatch(name, OverWriteEmail::cmdName())) ret = new OverWriteEmail(this); - else if (noCaseMatch(name, ChangeDispName::cmdName())) ret = new ChangeDispName(this); - else if (noCaseMatch(name, ChangeUsername::cmdName())) ret = new ChangeUsername(this); - else if (noCaseMatch(name, ChangePassword::cmdName())) ret = new ChangePassword(this); - else if (noCaseMatch(name, RemoveUser::cmdName())) ret = new RemoveUser(this); - else if (noCaseMatch(name, ChangeGroup::cmdName())) ret = new ChangeGroup(this); - else if (noCaseMatch(name, IsEmailVerified::cmdName())) ret = new IsEmailVerified(this); - else if (noCaseMatch(name, SetEmailTemplate::cmdName())) ret = new SetEmailTemplate(this); - else if (noCaseMatch(name, PreviewEmail::cmdName())) ret = new PreviewEmail(this); - else if (noCaseMatch(name, MyInfo::cmdName())) ret = new MyInfo(this); - else if (noCaseMatch(name, DownloadFile::cmdName())) ret = new DownloadFile(this); - else if (noCaseMatch(name, UploadFile::cmdName())) ret = new UploadFile(this); - else if (noCaseMatch(name, Delete::cmdName())) ret = new Delete(this); - else if (noCaseMatch(name, Copy::cmdName())) ret = new Copy(this); - else if (noCaseMatch(name, Move::cmdName())) ret = new Move(this); - else if (noCaseMatch(name, ListFiles::cmdName())) ret = new ListFiles(this); - else if (noCaseMatch(name, FileInfo::cmdName())) ret = new FileInfo(this); - else if (noCaseMatch(name, MakePath::cmdName())) ret = new MakePath(this); - else if (noCaseMatch(name, ChangeDir::cmdName())) ret = new ChangeDir(this); - else if (noCaseMatch(name, ListDBG::cmdName())) ret = new ListDBG(this); - else if (noCaseMatch(name, ListCerts::cmdName())) ret = new ListCerts(this); - else if (noCaseMatch(name, CertInfo::cmdName())) ret = new CertInfo(this); - else if (noCaseMatch(name, AddCert::cmdName())) ret = new AddCert(this); - else if (noCaseMatch(name, RemoveCert::cmdName())) ret = new RemoveCert(this); - else if (noCaseMatch(name, ToPeer::cmdName())) ret = new ToPeer(this); - else if (noCaseMatch(name, LsP2P::cmdName())) ret = new LsP2P(this); - else if (noCaseMatch(name, P2POpen::cmdName())) ret = new P2POpen(this); - else if (noCaseMatch(name, P2PClose::cmdName())) ret = new P2PClose(this); - else if (noCaseMatch(name, P2PRequest::cmdName())) ret = new P2PRequest(this); - else if (noCaseMatch(name, PingPeers::cmdName())) ret = new PingPeers(this); - else if (noCaseMatch(name, CreateChannel::cmdName())) ret = new CreateChannel(this); - else if (noCaseMatch(name, RemoveChannel::cmdName())) ret = new RemoveChannel(this); - else if (noCaseMatch(name, RenameChannel::cmdName())) ret = new RenameChannel(this); - else if (noCaseMatch(name, SetActiveState::cmdName())) ret = new SetActiveState(this); - else if (noCaseMatch(name, CreateSubCh::cmdName())) ret = new CreateSubCh(this); - else if (noCaseMatch(name, RemoveSubCh::cmdName())) ret = new RemoveSubCh(this); - else if (noCaseMatch(name, RenameSubCh::cmdName())) ret = new RenameSubCh(this); - else if (noCaseMatch(name, ListChannels::cmdName())) ret = new ListChannels(this); - else if (noCaseMatch(name, ListSubCh::cmdName())) ret = new ListSubCh(this); - else if (noCaseMatch(name, SearchChannels::cmdName())) ret = new SearchChannels(this); - else if (noCaseMatch(name, InviteToCh::cmdName())) ret = new InviteToCh(this); - else if (noCaseMatch(name, DeclineChInvite::cmdName())) ret = new DeclineChInvite(this); - else if (noCaseMatch(name, AcceptChInvite::cmdName())) ret = new AcceptChInvite(this); - else if (noCaseMatch(name, RemoveChMember::cmdName())) ret = new RemoveChMember(this); - else if (noCaseMatch(name, SetMemberLevel::cmdName())) ret = new SetMemberLevel(this); - else if (noCaseMatch(name, SetSubAcessLevel::cmdName())) ret = new SetSubAcessLevel(this); - else if (noCaseMatch(name, ListMembers::cmdName())) ret = new ListMembers(this); - else if (noCaseMatch(name, AddRDOnlyFlag::cmdName())) ret = new AddRDOnlyFlag(this); - else if (noCaseMatch(name, RemoveRDOnlyFlag::cmdName())) ret = new RemoveRDOnlyFlag(this); - else if (noCaseMatch(name, ListRDonlyFlags::cmdName())) ret = new ListRDonlyFlags(this); - else if (noCaseMatch(name, SetGroupRank::cmdName())) ret = new SetGroupRank(this); - else if (noCaseMatch(name, CmdInfo::cmdName())) ret = new CmdInfo(this); - else if (noCaseMatch(name, OwnerOverride::cmdName())) ret = new OwnerOverride(this); - else if (noCaseMatch(name, Tree::cmdName())) ret = new Tree(this); - - if (ret == nullptr) - { - qDebug() << "InternalCommandLoader Error: the loader claims command name '" << name << "' exists but no command object was actually matched/built."; - } - else - { - ret->setObjectName(name); - ret->setWritableDataShare(rwShared); - - // This is used as an automated way to update the Internal_Commands doc for the source code documentation - // keep it commented for release code. - - //appendToDoc("/docs/Internal_Commands.md", name, ret); - } - } - else - { - qDebug() << "InternalCommandLoader Error: command name '" << name << "' not found."; - } - - return ret; -} - -void InternalCommandLoader::makeDocHeader(const QString &path) -{ - QFile file(path); - - if (file.open(QFile::WriteOnly | QFile::Text)) - { - file.write("### 7.1 Internal Commands ###\n\n"); - file.write("The host is extendable via 3rd party modules but the host itself have it's own internal module that load "); - file.write("commands with direct access to the host database and several internal power functions that external "); - file.write("commands would otherwise not have direct access to. \n\n"); - } - - file.close(); -} - -void InternalCommandLoader::appendToDoc(const QString &path, const QString &cmdName, InternCommand *obj) -{ - QFile file(path); - QFileInfo info(path); - - if (file.open(QFile::Append | QFile::Text)) - { - file.write("* [" + cmdName.toUtf8() + "](intern_commands/" + cmdName.toUtf8() + ".md) - " + obj->shortText().toUtf8() + "\n\n"); - } - - file.close(); -} diff --git a/src/main.cpp b/src/main.cpp index 083f730..f60509c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,8 +7,6 @@ #include "db.h" #include "common.h" #include "tcp_server.h" -#include "unix_signal.h" -#include "cmd_executor.h" #include "db_setup.h" // This file is part of MRCI. @@ -27,6 +25,33 @@ // along with MRCI under the LICENSE.md file. If not, see // . +void msgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + Q_UNUSED(type) + Q_UNUSED(context) + + if (!msg.contains("QSslSocket: cannot resolve")) + { + Query db; + + db.setType(Query::PUSH, TABLE_DMESG); + db.addColumn(COLUMN_LOGENTRY, msg); + db.exec(); + } +} + +QByteArray rootUserId() +{ + Query db; + + db.setType(Query::PULL, TABLE_USERS); + db.addColumn(COLUMN_USER_ID); + db.addCondition(COLUMN_USERNAME, ROOT_USER); + db.exec(); + + return db.getData(COLUMN_USER_ID).toByteArray(); +} + void showHelp() { QTextStream txtOut(stdout); @@ -35,14 +60,22 @@ void showHelp() txtOut << "Usage: " << APP_TARGET << " " << endl << endl; txtOut << "" << endl << endl; txtOut << " -help : display usage information about this application." << endl; - txtOut << " -start : start a new host instance in the background." << endl; + txtOut << " -start : start a new host instance in the background. (non-blocking)" << endl; txtOut << " -stop : stop the current host instance if one is currently running." << endl; txtOut << " -about : display versioning/warranty information about this application." << endl; txtOut << " -addr {ip_address:port} : set the listening address and port for TCP clients." << endl; txtOut << " -status : display status information about the host instance if it is currently running." << endl; - txtOut << " -reset_root : reset the root account password to default." << endl; - txtOut << " -executor : this starts a command executor instance. this is normally for internal use only." << endl; - txtOut << " -host : this starts a blocking host instance. this is normally for internal use only." << endl << endl; + txtOut << " -reset_root : reset the root account password to the default password shown below." << endl; + txtOut << " -host : this starts a blocking host instance. for internal use only." << endl; + txtOut << " -public_cmds : run the internal module to list it's public commands. for internal use only." << endl; + txtOut << " -exempt_cmds : run the internal module to list it's rank exempt commands. for internal use only." << endl; + txtOut << " -user_cmds : run the internal module to list it's user commands. for internal use only." << endl; + txtOut << " -run_cmd {command_name} : run an internal module command. for internal use only." << endl << endl; + txtOut << "Internal module | -public_cmds, -user_cmds, -exempt_cmds, -run_cmd |:" << endl << endl; + txtOut << " -pipe {pipe_name/path} : the named pipe used to establish a data connection with the session." << endl; + txtOut << " -mem_ses {key_name} : the shared memory key for the session." << endl; + txtOut << " -mem_host {key_name} : the shared memory key for the host main process." << endl << endl; + txtOut << " default password: " << defaultPw() << endl << endl; } void soeDueToDbErr(int *retCode) @@ -98,6 +131,8 @@ int main(int argc, char *argv[]) qInstallMessageHandler(msgHandler); + //args.append("-host"); // debug + if (args.contains("-help", Qt::CaseInsensitive)) { showHelp(); @@ -108,6 +143,7 @@ int main(int argc, char *argv[]) QTextStream(stdout) << "Based on QT " << QT_VERSION_STR << " " << 8 * QT_POINTER_SIZE << "bit" << endl << endl; QTextStream(stdout) << "The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE" << endl; QTextStream(stdout) << "WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE." << endl << endl; + QTextStream(stdout) << " default password: " << defaultPw() << endl << endl; } else if (args.contains("-addr", Qt::CaseInsensitive)) { @@ -154,7 +190,10 @@ int main(int argc, char *argv[]) } } } - else if (args.contains("-executor", Qt::CaseInsensitive)) + else if (args.contains("-run_cmd", Qt::CaseInsensitive) || + args.contains("-public_cmds", Qt::CaseInsensitive) || + args.contains("-exempt_cmds", Qt::CaseInsensitive) || + args.contains("-user_cmds", Qt::CaseInsensitive)) { if (dbFail) { @@ -162,11 +201,12 @@ int main(int argc, char *argv[]) } else { - auto *session = new Session(&app); + auto *mod = new Module(&app); - session->startAsSlave(args); - - ret = QCoreApplication::exec(); + if (mod->start(args)) + { + ret = QCoreApplication::exec(); + } } } else if (args.contains("-host", Qt::CaseInsensitive)) @@ -179,16 +219,6 @@ int main(int argc, char *argv[]) { auto *serv = new TCPServer(&app); - #ifdef Q_OS_LINUX - - setupUnixSignalHandlers(); - - auto *signalHandler = new UnixSignalHandler(&app); - - QObject::connect(signalHandler, SIGNAL(closeServer()), serv, SLOT(closeServer())); - - #endif - if (serv->start()) { ret = QCoreApplication::exec(); @@ -218,7 +248,7 @@ int main(int argc, char *argv[]) } else { - updatePassword(ROOT_USER, DEFAULT_PASSWRD, TABLE_USERS, true); + updatePassword(rootUserId(), defaultPw(), TABLE_USERS, true); } } else diff --git a/src/mem_share.cpp b/src/mem_share.cpp new file mode 100644 index 0000000..c4c5149 --- /dev/null +++ b/src/mem_share.cpp @@ -0,0 +1,444 @@ +#include "mem_share.h" + +// This file is part of MRCI. + +// MRCI is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// MRCI is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with MRCI under the LICENSE.md file. If not, see +// . + +QString createHostSharedMem(QSharedMemory *mem) +{ + int len = 0; + QString ret; + + len += BLKSIZE_HOST_LOAD; // hostLoad + + mem->setKey(HOST_NON_NATIVE_KEY); + + if (mem->create(len)) + { + ret = mem->nativeKey(); + } + else if (mem->attach()) + { + ret = mem->nativeKey(); + } + + return ret; +} + +int posOfLikeBlock(const QByteArray &block, const char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock) +{ + int ret = -1; + quint32 cmpLen = static_cast(block.size()); + + if (cmpLen > bytesPerBlock) + { + cmpLen = bytesPerBlock; + } + + for (uint i = 0; i < numOfBlocks; i += bytesPerBlock) + { + if (memcmp(block.data(), blocks + i, cmpLen) == 0) + { + ret = static_cast(i); + + break; + } + } + + return ret; +} + +int posOfBlock(const char *block, const char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock) +{ + int ret = -1; + + for (uint i = 0; i < numOfBlocks; i += bytesPerBlock) + { + if (memcmp(block, blocks + i, bytesPerBlock) == 0) + { + ret = static_cast(i); + + break; + } + } + + return ret; +} + +int countNonEmptyBlocks(const char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock) +{ + int ret = 0; + char *blank = new char[bytesPerBlock]; + + memset(blank, 0, bytesPerBlock); + + for (uint i = 0; i < numOfBlocks; i += bytesPerBlock) + { + if (memcmp(blank, blocks + i, bytesPerBlock) != 0) + { + ret++; + } + } + + delete[] blank; + return ret; +} + +int posOfEmptyBlock(const char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock) +{ + char *blank = new char[bytesPerBlock]; + + memset(blank, 0, bytesPerBlock); + + int ret = posOfBlock(blank, blocks, numOfBlocks, bytesPerBlock); + + delete[] blank; + return ret; +} + +bool addBlockToBlockset(const char *block, char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock) +{ + bool ret = false; + + if (posOfBlock(block, blocks, numOfBlocks, bytesPerBlock) == -1) + { + int pos = posOfEmptyBlock(blocks, numOfBlocks, bytesPerBlock); + + if (pos != -1) + { + memcpy(blocks + pos, block, bytesPerBlock); + + ret = true; + } + } + + return ret; +} + +bool addStringToBlockset(const QString &str, char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock) +{ + char *block = new char[bytesPerBlock]; + + wrStringToBlock(str, block, bytesPerBlock); + + bool ret = addBlockToBlockset(block, blocks, numOfBlocks, bytesPerBlock); + + delete[] block; + return ret; +} + +bool rmBlockFromBlockset(const char *block, char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock) +{ + bool ret = false; + int pos = posOfBlock(block, blocks, numOfBlocks, bytesPerBlock); + + if (pos != -1) + { + memset(blocks + pos, 0, bytesPerBlock); + + ret = true; + } + + return ret; +} + +bool rmLikeBlkFromBlkset(const QByteArray &block, char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock) +{ + bool ret = false; + int pos = posOfLikeBlock(block, blocks, numOfBlocks, bytesPerBlock); + + if (pos != -1) + { + memset(blocks + pos, 0, bytesPerBlock); + + ret = true; + } + + return ret; +} + +bool isEmptyBlock(const char *block, quint32 blockSize) +{ + char *blank = new char[blockSize]; + + memset(blank, 0, blockSize); + + return memcmp(block, blank, blockSize) == 0; +} + +void wrStringToBlock(const QString &str, char *block, quint32 blockSize) +{ + quint32 strByteSize = static_cast(str.size()) * 2; + + if (strByteSize > blockSize) + { + strByteSize = blockSize; + } + else if (strByteSize != blockSize) + { + memset(block, 0, blockSize); + } + + memcpy(block, reinterpret_cast(str.utf16()), strByteSize); +} + +void wr8BitToBlock(quint8 num, char *block) +{ + memcpy(block, &num, 1); +} + +void wr16BitToBlock(quint16 num, char *block) +{ + memcpy(block, &num, 2); +} + +void wr32BitToBlock(quint32 num, char *block) +{ + memcpy(block, &num, 4); +} + +void wr64BitToBlock(quint64 num, char *block) +{ + memcpy(block, &num, 8); +} + +void wrToBlock(const QByteArray &data, char *block, quint32 blockSize) +{ + quint32 actualSize = static_cast(data.size()); + + if (actualSize > blockSize) + { + actualSize = blockSize; + } + else if (actualSize != blockSize) + { + memset(block, 0, blockSize); + } + + memcpy(block, data.data(), actualSize); +} + +QByteArray wrInt(quint64 num, int numOfBits) +{ + QByteArray ret(numOfBits / 8, static_cast(0)); + + num = qToLittleEndian(num); + + memcpy(ret.data(), &num, static_cast(ret.size())); + + return ret; +} + +QByteArray wrInt(qint64 num, int numOfBits) +{ + return wrInt(static_cast(num), numOfBits); +} + +QByteArray wrInt(int num, int numOfBits) +{ + return wrInt(static_cast(num), numOfBits); +} + +QByteArray wrInt(uint num, int numOfBits) +{ + return wrInt(static_cast(num), numOfBits); +} + +quint8 rd8BitFromBlock(const char *block) +{ + return static_cast(block[0]); +} + +quint16 rd16BitFromBlock(const char *block) +{ + quint16 ret; + + memcpy(&ret, block, 2); + + return ret; +} + +quint32 rd32BitFromBlock(const char *block) +{ + quint32 ret; + + memcpy(&ret, block, 4); + + return ret; +} + +quint64 rd64BitFromBlock(const char *block) +{ + quint64 ret; + + memcpy(&ret, block, 8); + + return ret; +} + +QString rdStringFromBlock(const char *block, quint32 blockSize) +{ + QString ret; + quint16 chr; + + for (quint32 i = 0; i < blockSize; i += 2) + { + memcpy(&chr, block + i, 2); + + if (chr == 0) + { + break; + } + else + { + ret.append(QChar(chr)); + } + } + + return ret; +} + +QByteArray rdFromBlock(const char *block, quint32 blockSize) +{ + if (blockSize == 0) + { + return QByteArray(); + } + else + { + return QByteArray::fromRawData(block, static_cast(blockSize)); + } +} + +quint64 rdInt(const QByteArray &bytes) +{ + quint64 ret = 0; + + memcpy(&ret, bytes.data(), static_cast(bytes.size())); + + return qFromLittleEndian(ret); +} + +MemShare::MemShare(QObject *parent) : QObject(parent) +{ + sharedMem = new QSharedMemory(this); + hostSharedMem = new QSharedMemory(this); +} + +bool MemShare::createSharedMem(const QByteArray &sesId, const QString &hostKey) +{ + int len = 0; + bool ret = false; + + sharedMem->setKey(sesId.toHex()); + hostSharedMem->setNativeKey(hostKey); + + len += BLKSIZE_SESSION_ID; // sessionId + len += BLKSIZE_USER_ID; // userId + len += BLKSIZE_CLIENT_IP; // clientIp + len += BLKSIZE_APP_NAME; // appName + len += BLKSIZE_USER_NAME; // userName + len += BLKSIZE_DISP_NAME; // displayName + len += BLKSIZE_GROUP_NAME; // groupName + len += BLKSIZE_HOST_RANK; // hostRank + len += BLKSIZE_ACT_UPDATE; // activeUpdate + len += BLKSIZE_CH_OVERRIDE; // chOwnerOverride + len += (BLKSIZE_CHANNEL_ID * MAX_CHANNELS_PER_USER); // chList + len += (BLKSIZE_SUB_CHANNEL * MAX_OPEN_SUB_CHANNELS); // openSubChs + len += (BLKSIZE_SUB_CHANNEL * MAX_OPEN_SUB_CHANNELS); // openWritableSubChs + len += (BLKSIZE_SESSION_ID * MAX_P2P_LINKS); // p2pPending + len += (BLKSIZE_SESSION_ID * MAX_P2P_LINKS); // p2pAccepted + + if (!sharedMem->create(len)) + { + qDebug() << "err: Failed to create a shared memory master block. reason: " + sharedMem->errorString(); + } + else if (!hostSharedMem->attach()) + { + qDebug() << "err: Failed to attach to the host shared memory master block. reason: " + hostSharedMem->errorString(); + } + else + { + memset(sharedMem->data(), 0, static_cast(len)); + memcpy(sharedMem->data(), sesId.data(), BLKSIZE_SESSION_ID); + + ret = true; + } + + return ret; +} + +bool MemShare::attachSharedMem(const QString &sKey, const QString &hKey) +{ + bool ret = false; + + sharedMem->setNativeKey(sKey); + hostSharedMem->setNativeKey(hKey); + + if (!sharedMem->attach()) + { + qDebug() << "err: Failed to attach to the session shared memory master block. reason: " + sharedMem->errorString(); + } + else if (!hostSharedMem->attach()) + { + qDebug() << "err: Failed to attach to the host shared memory master block. reason: " + hostSharedMem->errorString(); + } + else + { + ret = true; + } + + return ret; +} + +void MemShare::setupDataBlocks() +{ + if (sharedMem->isAttached() && hostSharedMem->isAttached()) + { + char *sesMasterBlock = static_cast(sharedMem->data()); + char *hosMasterBlock = static_cast(hostSharedMem->data()); + int sesOffs = 0; + int hosOffs = 0; + + sessionId = sesMasterBlock + sesOffs; sesOffs += BLKSIZE_SESSION_ID; + userId = sesMasterBlock + sesOffs; sesOffs += BLKSIZE_USER_ID; + clientIp = sesMasterBlock + sesOffs; sesOffs += BLKSIZE_CLIENT_IP; + appName = sesMasterBlock + sesOffs; sesOffs += BLKSIZE_APP_NAME; + userName = sesMasterBlock + sesOffs; sesOffs += BLKSIZE_USER_NAME; + displayName = sesMasterBlock + sesOffs; sesOffs += BLKSIZE_DISP_NAME; + hostRank = sesMasterBlock + sesOffs; sesOffs += BLKSIZE_HOST_RANK; + activeUpdate = sesMasterBlock + sesOffs; sesOffs += BLKSIZE_ACT_UPDATE; + chOwnerOverride = sesMasterBlock + sesOffs; sesOffs += BLKSIZE_CH_OVERRIDE; + chList = sesMasterBlock + sesOffs; sesOffs += (BLKSIZE_CHANNEL_ID * MAX_CHANNELS_PER_USER); + openSubChs = sesMasterBlock + sesOffs; sesOffs += (BLKSIZE_SUB_CHANNEL * MAX_OPEN_SUB_CHANNELS); + openWritableSubChs = sesMasterBlock + sesOffs; sesOffs += (BLKSIZE_SUB_CHANNEL * MAX_OPEN_SUB_CHANNELS); + p2pPending = sesMasterBlock + sesOffs; sesOffs += (BLKSIZE_SESSION_ID * MAX_P2P_LINKS); + p2pAccepted = sesMasterBlock + sesOffs; sesOffs += (BLKSIZE_SESSION_ID * MAX_P2P_LINKS); + hostLoad = hosMasterBlock + hosOffs; hosOffs += BLKSIZE_HOST_LOAD; + sesMemKey = sharedMem->nativeKey(); + hostMemKey = hostSharedMem->nativeKey(); + } +} + +QByteArray MemShare::createPeerInfoFrame() +{ + QByteArray sesId = rdFromBlock(sessionId, BLKSIZE_SESSION_ID); + QByteArray usrId = rdFromBlock(userId, BLKSIZE_USER_ID); + QByteArray uName = rdFromBlock(userName, BLKSIZE_USER_NAME); + QByteArray aName = rdFromBlock(appName, BLKSIZE_APP_NAME); + QByteArray dName = rdFromBlock(displayName, BLKSIZE_DISP_NAME); + + return sesId + usrId + uName + aName + dName; +} diff --git a/src/mem_share.h b/src/mem_share.h new file mode 100644 index 0000000..738a6c7 --- /dev/null +++ b/src/mem_share.h @@ -0,0 +1,112 @@ +#ifndef MEM_SHARE_H +#define MEM_SHARE_H + +// This file is part of MRCI. + +// MRCI is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// MRCI is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with MRCI under the LICENSE.md file. If not, see +// . + +#include +#include +#include +#include + +#define MAX_OPEN_SUB_CHANNELS 6 +#define MAX_CHANNELS_PER_USER 200 +#define MAX_P2P_LINKS 100 + +#define BLKSIZE_CLIENT_IP 78 +#define BLKSIZE_SESSION_ID 28 +#define BLKSIZE_SUB_CHANNEL 9 +#define BLKSIZE_APP_NAME 134 +#define BLKSIZE_USER_ID 32 +#define BLKSIZE_USER_NAME 48 +#define BLKSIZE_GROUP_NAME 24 +#define BLKSIZE_DISP_NAME 64 +#define BLKSIZE_CHANNEL_ID 8 +#define BLKSIZE_HOST_RANK 4 +#define BLKSIZE_ACT_UPDATE 1 +#define BLKSIZE_CH_OVERRIDE 1 +#define BLKSIZE_HOST_LOAD 4 +#define BLKSIZE_CMD_NAME 128 +#define BLKSIZE_LIB_NAME 128 +#define BLKSIZE_EMAIL_ADDR 128 + +#define HOST_NON_NATIVE_KEY "MRCI_Host_Shared_Mem_Key" + +int posOfLikeBlock(const QByteArray &block, const char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock); +int posOfBlock(const char *block, const char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock); +int posOfEmptyBlock(const char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock); +int countNonEmptyBlocks(const char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock); +bool rmLikeBlkFromBlkset(const QByteArray &block, char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock); +bool rmBlockFromBlockset(const char *block, char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock); +bool addStringToBlockset(const QString &str, char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock); +bool addBlockToBlockset(const char *block, char *blocks, quint32 numOfBlocks, quint32 bytesPerBlock); +bool isEmptyBlock(const char *block, quint32 blockSize); +void wrStringToBlock(const QString &str, char *block, quint32 blockSize); +void wr8BitToBlock(quint8 num, char *block); +void wr16BitToBlock(quint16 num, char *block); +void wr32BitToBlock(quint32 num, char *block); +void wr64BitToBlock(quint64 num, char *block); +void wrToBlock(const QByteArray &data, char *block, quint32 blockSize); +QByteArray rdFromBlock(const char *block, quint32 blockSize); +QByteArray wrInt(quint64 num, int numOfBits); +QByteArray wrInt(qint64 num, int numOfBits); +QByteArray wrInt(int num, int numOfBits); +QByteArray wrInt(uint num, int numOfBits); +QString rdStringFromBlock(const char *block, quint32 blockSize); +QString createHostSharedMem(QSharedMemory *mem); +quint8 rd8BitFromBlock(const char *block); +quint16 rd16BitFromBlock(const char *block); +quint32 rd32BitFromBlock(const char *block); +quint64 rd64BitFromBlock(const char *block); +quint64 rdInt(const QByteArray &bytes); + +class MemShare : public QObject +{ + Q_OBJECT + +protected: + + QString sesMemKey; + QString hostMemKey; + QSharedMemory *hostSharedMem; + QSharedMemory *sharedMem; + char *p2pAccepted; + char *p2pPending; + char *chList; + char *clientIp; + char *userName; + char *displayName; + char *appName; + char *openSubChs; + char *openWritableSubChs; + char *sessionId; + char *userId; + char *hostRank; + char *activeUpdate; + char *chOwnerOverride; + char *hostLoad; + + bool createSharedMem(const QByteArray &sesId, const QString &hostKey); + bool attachSharedMem(const QString &sKey, const QString &hKey); + void setupDataBlocks(); + QByteArray createPeerInfoFrame(); + +public: + + explicit MemShare(QObject *parent = nullptr); +}; + +#endif // MEM_SHARE_H diff --git a/src/module.cpp b/src/module.cpp new file mode 100644 index 0000000..e0d6a1e --- /dev/null +++ b/src/module.cpp @@ -0,0 +1,311 @@ +#include "module.h" + +// This file is part of MRCI. + +// MRCI is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// MRCI is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with MRCI under the LICENSE.md file. If not, see +// . + +Module::Module(QObject *parent) : QObject(parent) +{ + pubReg = false; + emailConfirmation = false; + passwrdResets = false; + + loadSettings(); +} + +void Module::loadSettings() +{ + Query db(this); + + db.setType(Query::PULL, TABLE_SERV_SETTINGS); + db.addColumn(COLUMN_PUB_USERS); + db.addColumn(COLUMN_ENABLE_CONFIRM); + db.addColumn(COLUMN_ENABLE_PW_RESET); + db.exec(); + + pubReg = db.getData(COLUMN_PUB_USERS).toBool(); + emailConfirmation = db.getData(COLUMN_ENABLE_CONFIRM).toBool(); + passwrdResets = db.getData(COLUMN_ENABLE_PW_RESET).toBool(); +} + +QStringList Module::userCmdList() +{ + QStringList ret; + + ret << CloseHost::cmdName(); + ret << RestartHost::cmdName(); + ret << ListBans::cmdName(); + ret << BanIP::cmdName(); + ret << UnBanIP::cmdName(); + ret << Cast::cmdName(); + ret << OpenSubChannel::cmdName(); + ret << CloseSubChannel::cmdName(); + ret << LsOpenChannels::cmdName(); + ret << HostInfo::cmdName(); + ret << IPHist::cmdName(); + ret << ListMods::cmdName(); + ret << DelMod::cmdName(); + ret << UploadMod::cmdName(); + ret << ListUsers::cmdName(); + ret << CreateUser::cmdName(); + ret << RecoverAcct::cmdName(); + ret << ResetPwRequest::cmdName(); + ret << AuthLog::cmdName(); + ret << LsCmdRanks::cmdName(); + ret << RemoveCmdRank::cmdName(); + ret << AssignCmdRank::cmdName(); + ret << ServSettings::cmdName(); + ret << LockUser::cmdName(); + ret << NameChangeRequest::cmdName(); + ret << PasswordChangeRequest::cmdName(); + ret << OverWriteEmail::cmdName(); + ret << RemoveUser::cmdName(); + ret << ChangeUserRank::cmdName(); + ret << SetEmailTemplate::cmdName(); + ret << PreviewEmail::cmdName(); + ret << DownloadFile::cmdName(); + ret << UploadFile::cmdName(); + ret << Delete::cmdName(); + ret << Copy::cmdName(); + ret << Move::cmdName(); + ret << ListFiles::cmdName(); + ret << FileInfo::cmdName(); + ret << MakePath::cmdName(); + ret << ChangeDir::cmdName(); + ret << ListDBG::cmdName(); + ret << ListCerts::cmdName(); + ret << CertInfo::cmdName(); + ret << AddCert::cmdName(); + ret << RemoveCert::cmdName(); + ret << ToPeer::cmdName(); + ret << LsP2P::cmdName(); + ret << P2POpen::cmdName(); + ret << P2PClose::cmdName(); + ret << P2PRequest::cmdName(); + ret << PingPeers::cmdName(); + ret << CreateChannel::cmdName(); + ret << RemoveChannel::cmdName(); + ret << RenameChannel::cmdName(); + ret << SetActiveState::cmdName(); + ret << CreateSubCh::cmdName(); + ret << RemoveSubCh::cmdName(); + ret << RenameSubCh::cmdName(); + ret << ListChannels::cmdName(); + ret << ListSubCh::cmdName(); + ret << SearchChannels::cmdName(); + ret << InviteToCh::cmdName(); + ret << DeclineChInvite::cmdName(); + ret << AcceptChInvite::cmdName(); + ret << RemoveChMember::cmdName(); + ret << SetMemberLevel::cmdName(); + ret << SetSubAcessLevel::cmdName(); + ret << ListMembers::cmdName(); + ret << AddRDOnlyFlag::cmdName(); + ret << RemoveRDOnlyFlag::cmdName(); + ret << ListRDonlyFlags::cmdName(); + ret << OwnerOverride::cmdName(); + ret << Tree::cmdName(); + + return ret + rankExemptList(); +} + +QStringList Module::pubCmdList() +{ + QStringList ret; + + ret << Auth::cmdName(); + ret << MyInfo::cmdName(); + + if (pubReg) + { + ret << CreateUser::cmdName(); + } + + if (passwrdResets) + { + ret << ResetPwRequest::cmdName(); + ret << RecoverAcct::cmdName(); + } + + return ret; +} + +QStringList Module::genfileList() +{ + QStringList ret; + + ret << DownloadFile::cmdName(); + ret << UploadFile::cmdName(); + ret << SetEmailTemplate::cmdName(); + + return ret; +} + +QStringList Module::rankExemptList() +{ + QStringList ret; + + ret << Auth::cmdName(); + ret << MyInfo::cmdName(); + ret << ChangeDispName::cmdName(); + ret << ChangeUsername::cmdName(); + ret << ChangePassword::cmdName(); + ret << ChangeEmail::cmdName(); + ret << IsEmailVerified::cmdName(); + + if (emailConfirmation) + { + ret << VerifyEmail::cmdName(); + } + + return ret; +} + +bool Module::runCmd(const QString &name) +{ + bool ret = true; + + if (userCmdList().contains(name, Qt::CaseInsensitive)) + { + if (noCaseMatch(name, CloseHost::cmdName())) new CloseHost(this); + else if (noCaseMatch(name, RestartHost::cmdName())) new RestartHost(this); + else if (noCaseMatch(name, Auth::cmdName())) new Auth(this); + else if (noCaseMatch(name, ListBans::cmdName())) new ListBans(this); + else if (noCaseMatch(name, BanIP::cmdName())) new BanIP(this); + else if (noCaseMatch(name, UnBanIP::cmdName())) new UnBanIP(this); + else if (noCaseMatch(name, Cast::cmdName())) new Cast(this); + else if (noCaseMatch(name, OpenSubChannel::cmdName())) new OpenSubChannel(this); + else if (noCaseMatch(name, CloseSubChannel::cmdName())) new CloseSubChannel(this); + else if (noCaseMatch(name, LsOpenChannels::cmdName())) new LsOpenChannels(this); + else if (noCaseMatch(name, HostInfo::cmdName())) new HostInfo(this); + else if (noCaseMatch(name, IPHist::cmdName())) new IPHist(this); + else if (noCaseMatch(name, ListMods::cmdName())) new ListMods(this); + else if (noCaseMatch(name, DelMod::cmdName())) new DelMod(this); + else if (noCaseMatch(name, UploadMod::cmdName())) new UploadMod(this); + else if (noCaseMatch(name, ListUsers::cmdName())) new ListUsers(this); + else if (noCaseMatch(name, CreateUser::cmdName())) new CreateUser(this); + else if (noCaseMatch(name, RecoverAcct::cmdName())) new RecoverAcct(this); + else if (noCaseMatch(name, ResetPwRequest::cmdName())) new ResetPwRequest(this); + else if (noCaseMatch(name, VerifyEmail::cmdName())) new VerifyEmail(this); + else if (noCaseMatch(name, AuthLog::cmdName())) new AuthLog(this); + else if (noCaseMatch(name, LsCmdRanks::cmdName())) new LsCmdRanks(this); + else if (noCaseMatch(name, RemoveCmdRank::cmdName())) new RemoveCmdRank(this); + else if (noCaseMatch(name, AssignCmdRank::cmdName())) new AssignCmdRank(this); + else if (noCaseMatch(name, ServSettings::cmdName())) new ServSettings(this); + else if (noCaseMatch(name, LockUser::cmdName())) new LockUser(this); + else if (noCaseMatch(name, NameChangeRequest::cmdName())) new NameChangeRequest(this); + else if (noCaseMatch(name, PasswordChangeRequest::cmdName())) new PasswordChangeRequest(this); + else if (noCaseMatch(name, ChangeEmail::cmdName())) new ChangeEmail(this); + else if (noCaseMatch(name, OverWriteEmail::cmdName())) new OverWriteEmail(this); + else if (noCaseMatch(name, ChangeDispName::cmdName())) new ChangeDispName(this); + else if (noCaseMatch(name, ChangeUsername::cmdName())) new ChangeUsername(this); + else if (noCaseMatch(name, ChangePassword::cmdName())) new ChangePassword(this); + else if (noCaseMatch(name, RemoveUser::cmdName())) new RemoveUser(this); + else if (noCaseMatch(name, ChangeUserRank::cmdName())) new ChangeUserRank(this); + else if (noCaseMatch(name, IsEmailVerified::cmdName())) new IsEmailVerified(this); + else if (noCaseMatch(name, SetEmailTemplate::cmdName())) new SetEmailTemplate(this); + else if (noCaseMatch(name, PreviewEmail::cmdName())) new PreviewEmail(this); + else if (noCaseMatch(name, MyInfo::cmdName())) new MyInfo(this); + else if (noCaseMatch(name, DownloadFile::cmdName())) new DownloadFile(this); + else if (noCaseMatch(name, UploadFile::cmdName())) new UploadFile(this); + else if (noCaseMatch(name, Delete::cmdName())) new Delete(this); + else if (noCaseMatch(name, Copy::cmdName())) new Copy(this); + else if (noCaseMatch(name, Move::cmdName())) new Move(this); + else if (noCaseMatch(name, ListFiles::cmdName())) new ListFiles(this); + else if (noCaseMatch(name, FileInfo::cmdName())) new FileInfo(this); + else if (noCaseMatch(name, MakePath::cmdName())) new MakePath(this); + else if (noCaseMatch(name, ChangeDir::cmdName())) new ChangeDir(this); + else if (noCaseMatch(name, ListDBG::cmdName())) new ListDBG(this); + else if (noCaseMatch(name, ListCerts::cmdName())) new ListCerts(this); + else if (noCaseMatch(name, CertInfo::cmdName())) new CertInfo(this); + else if (noCaseMatch(name, AddCert::cmdName())) new AddCert(this); + else if (noCaseMatch(name, RemoveCert::cmdName())) new RemoveCert(this); + else if (noCaseMatch(name, ToPeer::cmdName())) new ToPeer(this); + else if (noCaseMatch(name, LsP2P::cmdName())) new LsP2P(this); + else if (noCaseMatch(name, P2POpen::cmdName())) new P2POpen(this); + else if (noCaseMatch(name, P2PClose::cmdName())) new P2PClose(this); + else if (noCaseMatch(name, P2PRequest::cmdName())) new P2PRequest(this); + else if (noCaseMatch(name, PingPeers::cmdName())) new PingPeers(this); + else if (noCaseMatch(name, CreateChannel::cmdName())) new CreateChannel(this); + else if (noCaseMatch(name, RemoveChannel::cmdName())) new RemoveChannel(this); + else if (noCaseMatch(name, RenameChannel::cmdName())) new RenameChannel(this); + else if (noCaseMatch(name, SetActiveState::cmdName())) new SetActiveState(this); + else if (noCaseMatch(name, CreateSubCh::cmdName())) new CreateSubCh(this); + else if (noCaseMatch(name, RemoveSubCh::cmdName())) new RemoveSubCh(this); + else if (noCaseMatch(name, RenameSubCh::cmdName())) new RenameSubCh(this); + else if (noCaseMatch(name, ListChannels::cmdName())) new ListChannels(this); + else if (noCaseMatch(name, ListSubCh::cmdName())) new ListSubCh(this); + else if (noCaseMatch(name, SearchChannels::cmdName())) new SearchChannels(this); + else if (noCaseMatch(name, InviteToCh::cmdName())) new InviteToCh(this); + else if (noCaseMatch(name, DeclineChInvite::cmdName())) new DeclineChInvite(this); + else if (noCaseMatch(name, AcceptChInvite::cmdName())) new AcceptChInvite(this); + else if (noCaseMatch(name, RemoveChMember::cmdName())) new RemoveChMember(this); + else if (noCaseMatch(name, SetMemberLevel::cmdName())) new SetMemberLevel(this); + else if (noCaseMatch(name, SetSubAcessLevel::cmdName())) new SetSubAcessLevel(this); + else if (noCaseMatch(name, ListMembers::cmdName())) new ListMembers(this); + else if (noCaseMatch(name, AddRDOnlyFlag::cmdName())) new AddRDOnlyFlag(this); + else if (noCaseMatch(name, RemoveRDOnlyFlag::cmdName())) new RemoveRDOnlyFlag(this); + else if (noCaseMatch(name, ListRDonlyFlags::cmdName())) new ListRDonlyFlags(this); + else if (noCaseMatch(name, OwnerOverride::cmdName())) new OwnerOverride(this); + else if (noCaseMatch(name, Tree::cmdName())) new Tree(this); + else + { + qDebug() << "Module err: the loader claims command name '" << name << "' exists but no command object was actually matched/built."; + + ret = false; + } + } + else + { + qDebug() << "Module err: command name '" << name << "' not found."; + + ret = false; + } + + return ret; +} + +void Module::listCmds(const QStringList &list) +{ + new ListCommands(list, genfileList(), this); +} + +bool Module::start(const QStringList &args) +{ + bool ret = true; + + if (args.contains("-run_cmd")) + { + ret = runCmd(getParam("-run_cmd", args)); + } + else if (args.contains("-public_cmds")) + { + listCmds(pubCmdList()); + } + else if (args.contains("-exempt_cmds")) + { + listCmds(rankExemptList()); + } + else if (args.contains("-user_cmds")) + { + listCmds(userCmdList()); + } + else + { + ret = false; + } + + return ret; +} diff --git a/src/int_loader.h b/src/module.h similarity index 54% rename from src/int_loader.h rename to src/module.h index 46d7c64..b8fe029 100644 --- a/src/int_loader.h +++ b/src/module.h @@ -1,5 +1,5 @@ -#ifndef INT_LOADER_H -#define INT_LOADER_H +#ifndef MODULE_H +#define MODULE_H // This file is part of MRCI. @@ -17,56 +17,45 @@ // along with MRCI under the LICENSE.md file. If not, see // . -#include -#include -#include -#include - -#include "db.h" #include "common.h" -#include "commands/command.h" +#include "cmd_object.h" #include "commands/admin.h" #include "commands/bans.h" #include "commands/cast.h" -#include "commands/groups.h" #include "commands/info.h" #include "commands/mods.h" #include "commands/users.h" #include "commands/auth.h" #include "commands/cmd_ranks.h" -#include "commands/cmd_state.h" #include "commands/acct_recovery.h" #include "commands/fs.h" #include "commands/certs.h" #include "commands/p2p.h" #include "commands/channels.h" -class InternalCommandLoader : public CommandLoader +class Module : public QObject { Q_OBJECT private: - QStringList objNames; - bool pubReg; - bool emailConfirmation; - bool passwrdResets; + bool pubReg; + bool emailConfirmation; + bool passwrdResets; - void loadSettings(); - void makeDocHeader(const QString &path); - void appendToDoc(const QString &path, const QString &cmdName, InternCommand *obj); + bool runCmd(const QString &name); + void listCmds(const QStringList &list); + void loadSettings(); + QStringList userCmdList(); + QStringList pubCmdList(); + QStringList rankExemptList(); + QStringList genfileList(); public: - RWSharedObjs *rwShared; + explicit Module(QObject *parent = nullptr); - bool exists(const QString &cmdName); - QStringList cmdList(); - QStringList pubCmdList(); - QStringList rankExemptList(); - InternCommand *cmdObj(const QString &name); - - explicit InternalCommandLoader(RWSharedObjs *sharedData, QObject *parent = nullptr); + bool start(const QStringList &args); }; -#endif // INT_LOADER_H +#endif // MODULE_H diff --git a/src/session.cpp b/src/session.cpp index 20d9b5d..1d18ae1 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -16,71 +16,44 @@ // along with MRCI under the LICENSE.md file. If not, see // . -Session::Session(QObject *parent) : QObject(parent) +QByteArray wrFrame(quint32 cmdId, const QByteArray &data, uchar dType) { - chIds = QByteArray(54, static_cast(0)); - rwShared = new RWSharedObjs(this); - shared = new SharedObjs(this); - exeDebugInfo = new QSharedMemory(this); - ipcServ = nullptr; - slaveProc = nullptr; - ipcLink = nullptr; - tcpSocket = nullptr; - activeUpdate = false; - chOwnerOverride = false; - exeCrashCount = 0; - clientMajor = 0; - clientMinor = 0; - clientPatch = 0; - ipcFrameCmdId = 0; - ipcFrameSize = 0; - ipcFrameType = 0; - tcpFrameCmdId = 0; - tcpFrameSize = 0; - tcpFrameType = 0; - hostRank = 0; - flags = 0; + QByteArray typeBa = wrInt(dType, 8); + QByteArray cmdBa = wrInt(cmdId, 32); + QByteArray sizeBa = wrInt(data.size(), MAX_FRAME_BITS); - shared->clientMajor = &clientMajor; - shared->clientMinor = &clientMinor; - shared->clientPatch = &clientPatch; - shared->groupName = &groupName; - shared->userName = &userName; - shared->displayName = &displayName; - shared->appName = &appName; - shared->sessionAddr = &peerIp; - shared->sessionId = &sessionId; - shared->userId = &userId; - shared->chIds = &chIds; - shared->wrAbleChIds = &wrAbleChIds; - shared->p2pAccepted = &p2pAccepted; - shared->p2pPending = &p2pPending; - shared->activeUpdate = &activeUpdate; - shared->chList = &chList; - shared->chOwnerOverride = &chOwnerOverride; - shared->hostRank = &hostRank; - - rwShared->clientMajor = &clientMajor; - rwShared->clientMinor = &clientMinor; - rwShared->clientPatch = &clientPatch; - rwShared->groupName = &groupName; - rwShared->userName = &userName; - rwShared->displayName = &displayName; - rwShared->appName = &appName; - rwShared->sessionAddr = &peerIp; - rwShared->sessionId = &sessionId; - rwShared->userId = &userId; - rwShared->chIds = &chIds; - rwShared->wrAbleChIds = &wrAbleChIds; - rwShared->p2pAccepted = &p2pAccepted; - rwShared->p2pPending = &p2pPending; - rwShared->activeUpdate = &activeUpdate; - rwShared->chList = &chList; - rwShared->chOwnerOverride = &chOwnerOverride; - rwShared->hostRank = &hostRank; + return typeBa + cmdBa + sizeBa + data; } -void Session::genSessionId() +Session::Session(const QString &hostKey, QSslSocket *tcp, QObject *parent) : MemShare(parent) +{ + currentDir = QDir::currentPath(); + hostMemKey = hostKey; + tcpSocket = tcp; + tcpFrameCmdId = 0; + tcpPayloadSize = 0; + tcpFrameType = 0; + flags = 0; +} + +void Session::init() +{ + if (createSharedMem(genSessionId(), hostMemKey)) + { + setupDataBlocks(); + wrStringToBlock(tcpSocket->peerAddress().toString(), clientIp, BLKSIZE_CLIENT_IP); + + connect(tcpSocket, &QSslSocket::disconnected, this, &Session::endSession); + connect(tcpSocket, &QSslSocket::readyRead, this, &Session::dataFromClient); + connect(tcpSocket, &QSslSocket::encrypted, this, &Session::sesRdy); + } + else + { + endSession(); + } +} + +QByteArray Session::genSessionId() { QByteArray serial = genSerialNumber().toUtf8(); QByteArray sysId = QSysInfo::machineUniqueId(); @@ -89,592 +62,258 @@ void Session::genSessionId() hasher.addData(serial + sysId); - sessionId = hasher.result(); -} - -void Session::initAsMain(QSslSocket *tcp) -{ - genSessionId(); - - tcpSocket = tcp; - slaveProc = new QProcess(this); - ipcServ = new QLocalServer(this); - peerIp = tcp->peerAddress().toString(); - pipeName = pipesPath() + "/" + sessionId.toHex(); - - if (QFile::exists(pipeName)) - { - QFile::remove(pipeName); - } - - exeDebugInfo->setKey(sessionId.toHex()); - exeDebugInfo->create(EXE_DEBUG_INFO_SIZE); - - auto buffSize = static_cast(qPow(2, MAX_FRAME_BITS) - 1) + (MAX_FRAME_BITS / 8) + 2; - // max_data_size_per_frame + size_of_size_bytes + size_of_cmd_id - - tcp->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, buffSize); - tcp->setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, buffSize); - - connect(slaveProc, &QProcess::readyReadStandardError, this, &Session::sendStderr); - connect(slaveProc, &QProcess::readyReadStandardOutput, this, &Session::sendStdout); - connect(slaveProc, &QProcess::errorOccurred, this, &Session::exeError); - connect(slaveProc, &QProcess::started, this, &Session::exeStarted); - connect(slaveProc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(exeFinished(int,QProcess::ExitStatus))); - - connect(tcpSocket, &QSslSocket::disconnected, this, &Session::endSession); - connect(tcpSocket, &QSslSocket::readyRead, this, &Session::dataFromClient); - connect(tcpSocket, &QSslSocket::encrypted, this, &Session::run); - - connect(ipcServ, &QLocalServer::newConnection, this, &Session::newIPCLink); -} - -void Session::startAsSlave(const QStringList &args) -{ - sessionId = QByteArray::fromHex(getParam("-session_id", args).toUtf8()); - clientMajor = getParam("-client_major", args).toUShort(); - clientMinor = getParam("-client_minor", args).toUShort(); - clientPatch = getParam("-client_patch", args).toUShort(); - peerIp = getParam("-session_ipaddr", args); - appName = getParam("-app_name", args); - pipeName = pipesPath() + "/" + sessionId.toHex(); - ipcLink = new QLocalSocket(this); - executor = new CmdExecutor(rwShared, shared, exeDebugInfo); - - exeDebugInfo->setKey(sessionId.toHex()); - exeDebugInfo->attach(); - - auto *exeThr = new QThread(); - - connect(exeThr, &QThread::finished, executor, &CmdExecutor::deleteLater); - connect(exeThr, &QThread::finished, exeThr, &QThread::deleteLater); - connect(exeThr, &QThread::started, executor, &CmdExecutor::buildCmdLoaders); - - connect(executor, &CmdExecutor::okToDelete, this, &Session::closeInstance); - connect(executor, &CmdExecutor::okToDelete, exeThr, &QThread::quit); - connect(executor, &CmdExecutor::logout, this, &Session::logout); - connect(executor, &CmdExecutor::authOk, this, &Session::authOk); - connect(executor, &CmdExecutor::endSession, this, &Session::endSession); - connect(executor, &CmdExecutor::dataToSession, this, &Session::dataToClient); - - connect(this, &Session::closeExe, executor, &CmdExecutor::close); - connect(this, &Session::loadCommands, executor, &CmdExecutor::buildCommands); - connect(this, &Session::dataToCommand, executor, &CmdExecutor::exeCmd); - connect(this, &Session::unloadModFile, executor, &CmdExecutor::unloadModFile); - connect(this, &Session::loadModFile, executor, &CmdExecutor::loadModFile); - - connect(ipcLink, &QLocalSocket::connected, this, &Session::ipcConnected); - connect(ipcLink, &QLocalSocket::disconnected, this, &Session::ipcDisconnected); - connect(ipcLink, &QLocalSocket::readyRead, this, &Session::dataFromIPC); - connect(ipcLink, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(ipcError(QLocalSocket::LocalSocketError))); - - serializeThread(exeThr); - - executor->moveToThread(exeThr); - exeThr->start(); - - QTimer::singleShot(100, this, SLOT(run())); -} - -void Session::modLoadCrashCheck(const QString &crashInfo) -{ - if (crashInfo.contains("loadModLib()")) - { - int pos = crashInfo.indexOf("path: "); - - if (pos != -1) - { - QString path = crashInfo.mid(pos + 7).trimmed(); - - Query db(this); - - db.setType(Query::UPDATE, TABLE_MODULES); - db.addColumn(COLUMN_LOCKED, true); - db.addCondition(COLUMN_MOD_MAIN, path); - db.exec(); - - qDebug() << "Module: '" << path << "' auto locked due to a crash while loading."; - } - } -} - -void Session::rdExeDebug() -{ - if (exeDebugInfo->isAttached()) - { - exeDebugInfo->lock(); - - QByteArray data = QByteArray(static_cast(exeDebugInfo->data()), EXE_DEBUG_INFO_SIZE); - - data.replace(QByteArray(2, static_cast(0)), QByteArray()); - - emit dataToClient(ASYNC_SYS_MSG, toTEXT("\ndebug info:\n"), TEXT); - emit dataToClient(ASYNC_SYS_MSG, data + toTEXT("\n"), TEXT); - - qDebug() << "Debug info generated: " + fromTEXT(data); - - modLoadCrashCheck(fromTEXT(data)); - - exeDebugInfo->unlock(); - } -} - -bool Session::isSlave() -{ - return (tcpSocket == nullptr); -} - -bool Session::isMain() -{ - return (tcpSocket != nullptr); + return hasher.result(); } void Session::payloadDeleted() { - if (isSlave()) - { - qDebug() << "Session::payloadDeleted() called while running in slave mode."; - } - else - { - flags &= ~ACTIVE_PAYLOAD; + flags &= ~ACTIVE_PAYLOAD; - if (flags & END_SESSION_ON_PAYLOAD_DEL) - { - endSession(); - } + if (flags & END_SESSION_ON_PAYLOAD_DEL) + { + endSession(); } } void Session::connectToPeer(const QSharedPointer &peer) { - if (isSlave()) - { - qDebug() << "Session::connectToPeer() called while running in slave mode."; - } - else if (peer->sessionObj == nullptr) + if (peer->sessionObj == nullptr) { qDebug() << "Session::connectToPeer() the peer session object is null."; } - else if ((peer->sessionObj != this) && (flags & IPC_LINK_OK)) + else if ((peer->sessionObj != this) && (flags & SESSION_RDY)) { - connect(peer->sessionObj, &Session::backendToPeers, this, &Session::peersDataIn); - connect(this, &Session::backendToPeers, peer->sessionObj, &Session::peersDataIn); + connect(peer->sessionObj, &Session::asyncToPeers, this, &Session::pubAsyncDataIn); + connect(this, &Session::asyncToPeers, peer->sessionObj, &Session::pubAsyncDataIn); } } -void Session::run() +void Session::sesRdy() { - flags &= ~SSL_HOLD; + flags |= SESSION_RDY; - if (isSlave()) - { - ipcLink->connectToServer(pipeName); + auto *payload = new SessionCarrier(this); - QTimer::singleShot(IPC_PREP_TIME, this, SLOT(newIPCTimeout())); - } - else if (!ipcServ->listen(pipeName)) - { - qDebug() << "Session::run() unable to listen on pipe name: " + pipeName + " reason: " + ipcServ->errorString(); + connect(payload, &SessionCarrier::destroyed, this, &Session::payloadDeleted); - dataToClient(ASYNC_SYS_MSG, toTEXT("\nsystem err: Unable to create an IPC pipe for the command executor, ending the session.\n"), ERR); - endSession(); - } - else - { - QStringList args; + emit connectPeers(QSharedPointer(payload)); - args.append("-executor"); - args.append("-session_id"); - args.append(sessionId.toHex()); - args.append("-client_major"); - args.append(QString::number(clientMajor)); - args.append("-client_minor"); - args.append(QString::number(clientMinor)); - args.append("-client_patch"); - args.append(QString::number(clientPatch)); - args.append("-session_ipaddr"); - args.append(peerIp); - args.append("-app_name"); - args.append(appName); - - slaveProc->start(QCoreApplication::applicationFilePath(), args); - - dataToClient(ASYNC_SYS_MSG, toTEXT("Attempting to start a new command executor.\n"), TEXT); - - QTimer::singleShot(IPC_PREP_TIME, this, SLOT(newIPCTimeout())); - } -} - -void Session::ipcOk() -{ - if (isSlave()) - { - qDebug() << "Session::ipcOk() called while running in slave mode."; - } - else - { - flags |= IPC_LINK_OK; - flags |= ACTIVE_PAYLOAD; - - auto *payload = new SessionCarrier(this); - - connect(ipcLink, &QLocalSocket::readyRead, this, &Session::dataFromIPC); - connect(ipcLink, &QLocalSocket::disconnected, this, &Session::ipcDisconnected); - connect(ipcLink, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(ipcError(QLocalSocket::LocalSocketError))); - - connect(payload, &SessionCarrier::destroyed, this, &Session::payloadDeleted); - - emit connectPeers(QSharedPointer(payload)); - - dataToClient(ASYNC_SYS_MSG, toTEXT("IPC connection successfully established.\n"), TEXT); - - if (!userId.isEmpty()) - { - ipcLink->write(wrFrame(ASYNC_RESTORE_AUTH, userId, PRIV_IPC)); - } - else - { - ipcLink->write(wrFrame(ASYNC_PUBLIC_AUTH, QByteArray(), PRIV_IPC)); - } - } -} - -void Session::newIPCTimeout() -{ - if (isSlave()) - { - if (ipcLink->state() != QLocalSocket::ConnectedState) - { - QCoreApplication::exit(PIPE_CONNECT_TIMEOUT); - } - } - else if (ipcLink == nullptr) - { - dataToClient(ASYNC_SYS_MSG, toTEXT("\nsystem err: Timed out waiting for the command executor to request an IPC link with the session.\n"), ERR); - endSession(); - } -} - -void Session::newIPCLink() -{ - if (isSlave()) - { - qDebug() << "Session::newIPCLink() called while running in slave mode."; - } - else - { - if (ipcLink != nullptr) - { - qDebug() << "Session::newIPCLink() another local socket tried to connect to the session while there is already an active connection. session id: " + sessionId.toHex(); - - ipcServ->nextPendingConnection()->deleteLater(); - } - else - { - ipcLink = ipcServ->nextPendingConnection(); - - QTimer::singleShot(IPC_PREP_TIME, this, SLOT(ipcOk())); - } - } -} - -void Session::exeStarted() -{ - if (isSlave()) - { - qDebug() << "Session::exeStarted() called while running in slave mode."; - } - else - { - dataToClient(ASYNC_SYS_MSG, toTEXT("Command executor successfully started, awaiting an IPC request.\n"), TEXT); - } -} - -void Session::ipcConnected() -{ - if (isSlave()) - { - flags |= IPC_LINK_OK; - } -} - -void Session::ipcDisconnected() -{ - flags &= ~IPC_LINK_OK; - - if (isMain()) - { - ipcLink = nullptr; - } -} - -void Session::exeFinished(int ret, QProcess::ExitStatus status) -{ - if (isSlave()) - { - qDebug() << "Session::exeFinished() called while running in slave mode."; - } - else if (flags & EXPECTED_TERM) - { - endSession(); - } - else if ((status == QProcess::NormalExit) && (ret == FAILED_TO_OPEN_PIPE)) - { - dataToClient(ASYNC_EXE_CRASH, toTEXT("\nsystem err: The command executor could not open a new pipe for listening.\n"), ERR); - endSession(); - } - else if ((status == QProcess::NormalExit) && (ret == PIPE_CONNECT_TIMEOUT)) - { - dataToClient(ASYNC_EXE_CRASH, toTEXT("\nsystem err: The command executor timed out waiting for the session to acknowledge the IPC request.\n"), ERR); - endSession(); - } - else - { - int msecSinceLastCrash = lastExeCrash.msecsTo(QTime::currentTime()); - - if ((msecSinceLastCrash <= 5000) || lastExeCrash.isNull()) - { - // only count executor crashes within 5sec of each other. any longer than that - // can be considered a recoverable session. - - exeCrashCount++; - } - else - { - lastExeCrash = QTime(); - exeCrashCount = 0; - } - - if (exeCrashCount <= EXE_CRASH_LIMIT) - { - ipcServ->close(); - - dataToClient(ASYNC_EXE_CRASH, toTEXT("\nsystem err: The command executor has stopped unexpectedly.\n"), ERR); - rdExeDebug(); - dataToClient(ASYNC_SYS_MSG, toTEXT("\nAttempting to restart the executor...\n\n"), TEXT); - run(); - } - else - { - // if there is the amount of executor crashes defined in EXE_CRASH_LIMIT - // within 5sec of each other then the session can no longer be considered - // recoverable so it will be killed to prevent an infinite loop. - - dataToClient(ASYNC_EXE_CRASH, toTEXT("\nsystem err: The command executor has crashed too many times, ending the session.\n"), ERR); - endSession(); - } - } -} - -void Session::exeError(QProcess::ProcessError err) -{ - if (isSlave()) - { - qDebug() << "Session::exeError() called while running in slave mode."; - } - else if (err == QProcess::FailedToStart) - { - qDebug() << "\nsystem err: Could not start the command executor. reason: " + slaveProc->errorString() + ".\n"; - - dataToClient(ASYNC_SYS_MSG, toTEXT("\nsystem err: Could not start the command executor. details are logged for the host admin.\n"), ERR); - endSession(); - } - else - { - qDebug() << "Session:: exeError() " << slaveProc->errorString(); - } -} - -void Session::ipcError(QLocalSocket::LocalSocketError socketError) -{ - if (socketError != QLocalSocket::PeerClosedError) - { - if (isSlave()) - { - qDebug() << "Session::ipcError() slave mode. socketError: " + QString::number(socketError) + " session id: " + sessionId.toHex(); - } - else - { - qDebug() << "Session::ipcError() main mode. socketError: " + QString::number(socketError) + " session id: " + sessionId.toHex(); - } - } + loadCmds(); + asyncToClient(ASYNC_RDY, toTEXT("\nReady!\n\n"), TEXT); } void Session::addIpAction(const QString &action) { Query db(this); + QString ip = rdStringFromBlock(clientIp, BLKSIZE_CLIENT_IP); + QString app = rdStringFromBlock(appName, BLKSIZE_APP_NAME); + QByteArray id = rdFromBlock(sessionId, BLKSIZE_SESSION_ID); + db.setType(Query::PUSH, TABLE_IPHIST); - db.addColumn(COLUMN_IPADDR, peerIp); + db.addColumn(COLUMN_IPADDR, ip); db.addColumn(COLUMN_LOGENTRY, action); - db.addColumn(COLUMN_SESSION_ID, sessionId); - db.addColumn(COLUMN_CLIENT_VER, QString::number(clientMajor) + "." + - QString::number(clientMinor) + "." + - QString::number(clientPatch)); + db.addColumn(COLUMN_SESSION_ID, id); + db.addColumn(COLUMN_APP_NAME, app); db.exec(); } -void Session::closeInstance() +void Session::cmdProcFinished(quint32 cmdId) { - if (isMain()) + cmdProcesses.remove(cmdId); + frameQueue.remove(cmdId); + + if (flags & END_SESSION_EMPTY_PROC) { - qDebug() << "Session::closeInstance() called while running on the main process."; + endSession(); } - else - { - QCoreApplication::exit(0); +} + +void Session::cmdProcStarted(quint32 cmdId, CmdProcess *obj) +{ + cmdProcesses.insert(cmdId, obj); + + if (frameQueue.contains(cmdId)) + { + for (auto&& frame : frameQueue[cmdId]) + { + dataToCmd(cmdId, frame.mid(1), static_cast(frame[0])); + } + + frameQueue.remove(cmdId); } } void Session::endSession() { - if (isSlave()) + emit killMods(); + + logout("", false); + + if (flags & ACTIVE_PAYLOAD) { - ipcLink->write(wrFrame(ASYNC_END_SESSION, QByteArray(), PRIV_IPC)); + flags |= END_SESSION_ON_PAYLOAD_DEL; } else - { - if (activeUpdate) + { + if (cmdProcesses.isEmpty()) { - QByteArray castHeader = chIds + wrInt(PEER_STAT, 8); - QByteArray data = toPEER_STAT(sessionId, chIds, true); + addIpAction("Session Ended"); + cleanupDbConnection(); - emit backendToPeers(ASYNC_LIMITED_CAST, castHeader + data); - } - - if (flags & ACTIVE_PAYLOAD) - { - flags |= END_SESSION_ON_PAYLOAD_DEL; + emit ended(); } else { - if (flags & IPC_LINK_OK) - { - flags |= EXPECTED_TERM; + flags |= END_SESSION_EMPTY_PROC; - ipcLink->write(wrFrame(ASYNC_END_SESSION, QByteArray(), PRIV_IPC)); - } - else + for (auto id : cmdProcesses.keys()) { - addIpAction("Session Ended"); - cleanupDbConnection(); - - emit ended(); + emit killCmd32(id); } } } } -void Session::dataFromIPC() -{ - if (flags & IPC_FRAME_RDY) +void Session::startCmdProc(quint32 cmdId) +{ + quint16 cmdId16 = toCmdId16(cmdId); + QString modApp = cmdAppById[cmdId16]; + QString pipe = rdFromBlock(sessionId, BLKSIZE_USER_ID).toHex() + "-cmd-" + QString::number(cmdId); + + auto *proc = new CmdProcess(cmdId, cmdRealNames[cmdId16], modApp, sesMemKey, hostMemKey, pipe, this); + + proc->setWorkingDirectory(currentDir); + proc->setSessionParams(sharedMem, sessionId, openWritableSubChs); + + connect(proc, &CmdProcess::cmdProcFinished, this, &Session::cmdProcFinished); + connect(proc, &CmdProcess::cmdProcReady, this, &Session::cmdProcStarted); + connect(proc, &CmdProcess::pubIPC, this, &Session::asyncToPeers); + connect(proc, &CmdProcess::privIPC, this, &Session::privAsyncDataIn); + connect(proc, &CmdProcess::pubIPCWithFeedBack, this, &Session::asyncToPeers); + connect(proc, &CmdProcess::pubIPCWithFeedBack, this, &Session::pubAsyncDataIn); + connect(proc, &CmdProcess::dataToClient, this, &Session::dataToClient); + + connect(this, &Session::killCmd16, proc, &CmdProcess::killCmd16); + connect(this, &Session::killCmd32, proc, &CmdProcess::killCmd32); + + proc->startCmdProc(); +} + +ModProcess *Session::initModProc(const QString &modApp) +{ + QString pipe = rdFromBlock(sessionId, BLKSIZE_USER_ID).toHex() + "-mod-" + genSerialNumber(); + quint32 rnk = rd32BitFromBlock(hostRank); + + auto *proc = new ModProcess(modApp, sesMemKey, hostMemKey, pipe, this); + + proc->setWorkingDirectory(currentDir); + proc->setSessionParams(&cmdUniqueNames, &cmdRealNames, &cmdAppById, &modCmdNames, &cmdIds, rnk); + + connect(proc, &ModProcess::dataToClient, this, &Session::dataToClient); + connect(proc, &ModProcess::cmdUnloaded, this, &Session::killCmd16); + + connect(this, &Session::killMods, proc, &ModProcess::kill); + + return proc; +} + +void Session::startModProc(const QString &modApp) +{ + if (flags & LOGGED_IN) { - if (ipcLink->bytesAvailable() >= ipcFrameSize) + initModProc(modApp)->loadExemptCmds(); + initModProc(modApp)->loadUserCmds(); + } + else + { + initModProc(modApp)->loadPublicCmds(); + } +} + +void Session::loadCmds() +{ + startModProc(QCoreApplication::applicationFilePath()); + + Query db(this); + + db.setType(Query::PULL, TABLE_MODULES); + db.addColumn(COLUMN_MOD_MAIN); + db.exec(); + + for (int i = 0; i < db.rows(); ++i) + { + startModProc(db.getData(COLUMN_MOD_MAIN, i).toString()); + } +} + +void Session::dataToCmd(quint32 cmdId, const QByteArray &data, quint8 typeId) +{ + quint16 cmdId16 = toCmdId16(cmdId); + + if (cmdIds.contains(cmdId16)) + { + if (cmdProcesses.contains(cmdId)) { - QByteArray data = ipcLink->read(ipcFrameSize); - - if (isSlave()) + cmdProcesses[cmdId]->dataFromSession(cmdId, data, typeId); + } + else + { + if (frameQueue.contains(cmdId)) { - // data from Session object in main process. - - if ((ipcFrameType == PRIV_IPC) || (ipcFrameType == PUB_IPC)) - { - backendDataIn(ipcFrameCmdId, data); - } - else - { - emit dataToCommand(ipcFrameCmdId, data, ipcFrameType); - } + frameQueue[cmdId].append(wrInt(typeId, 8) + data); } - else if (isMain()) + else { - // data from Session object in slave process. + QList frames; - if (ipcFrameType == PRIV_IPC) - { - backendDataIn(ipcFrameCmdId, data); - } - else if ((ipcFrameType == PUB_IPC) || (ipcFrameType == PUB_IPC_WITH_FEEDBACK)) - { - emit backendToPeers(ipcFrameCmdId, data); - } - else - { - dataToClient(ipcFrameCmdId, data, ipcFrameType); - } + frames.append(wrInt(typeId, 8) + data); + frameQueue.insert(cmdId, frames); } - flags ^= IPC_FRAME_RDY; - - dataFromIPC(); + startCmdProc(cmdId); } } - else if (ipcLink->bytesAvailable() >= FRAME_HEADER_SIZE) + else { - QByteArray header = ipcLink->read(FRAME_HEADER_SIZE); - - ipcFrameType = static_cast(header[0]); - ipcFrameCmdId = static_cast(rdInt(header.mid(1, 2))); - ipcFrameSize = static_cast(rdInt(header.mid(3))); - flags |= IPC_FRAME_RDY; - - dataFromIPC(); + dataToClient(cmdId, toTEXT("err: No such command id: " + QString::number(cmdId16) + "."), ERR); } } void Session::dataFromClient() { - if ((flags & IPC_LINK_OK) && !(flags & SSL_HOLD)) + if (flags & SESSION_RDY) { - if (flags & TCP_FRAME_RDY) + if (flags & FRAME_RDY) { - if (tcpSocket->bytesAvailable() >= tcpFrameSize) - { - ipcLink->write(tcpSocket->read(tcpFrameSize)); + if (tcpSocket->bytesAvailable() >= tcpPayloadSize) + { + dataToCmd(tcpFrameCmdId, tcpSocket->read(tcpPayloadSize), tcpFrameType); - flags ^= TCP_FRAME_RDY; + flags ^= FRAME_RDY; dataFromClient(); } } else if (tcpSocket->bytesAvailable() >= FRAME_HEADER_SIZE) { - QByteArray header = tcpSocket->peek(FRAME_HEADER_SIZE); + QByteArray header = tcpSocket->read(FRAME_HEADER_SIZE); - tcpFrameType = static_cast(header[0]); - tcpFrameCmdId = static_cast(rdInt(header.mid(1, 2))); - tcpFrameSize = static_cast(rdInt(header.mid(3)) + FRAME_HEADER_SIZE); - - if ((tcpFrameType == PUB_IPC) || (tcpFrameType == PRIV_IPC) || - (tcpFrameType == PING_PEERS) || (tcpFrameType == PUB_IPC_WITH_FEEDBACK)) - { - // for obvious security reasons, TCP clients should not be allowed - // to send any of the PrivateTypeID data frames. infact, it is logged - // as suspicious behaviour that admins can monitor for. - - addIpAction("Suspicious action: client attempted to send a private TypeID: " + QString::number(tcpFrameType)); - - tcpSocket->readAll(); // kill the frame. - } - else - { - flags |= TCP_FRAME_RDY; - } + tcpFrameType = static_cast(header[0]); + tcpFrameCmdId = static_cast(rdInt(header.mid(1, 4))); + tcpPayloadSize = static_cast(rdInt(header.mid(5))); + flags |= FRAME_RDY; dataFromClient(); } } - else if (!(flags & VER_OK)) + else { if (tcpSocket->bytesAvailable() >= CLIENT_HEADER_LEN) { if (tcpSocket->read(4) == QByteArray(SERVER_HEADER_TAG)) { - clientMajor = static_cast(rdInt(tcpSocket->read(2))); - clientMinor = static_cast(rdInt(tcpSocket->read(2))); - clientPatch = static_cast(rdInt(tcpSocket->read(2))); - appName = fromTEXT(tcpSocket->read(128)).trimmed(); + wrStringToBlock(fromTEXT(tcpSocket->read(BLKSIZE_APP_NAME)).trimmed(), appName, BLKSIZE_APP_NAME); QString coName = fromTEXT(tcpSocket->read(272)).trimmed(); QStringList ver = QCoreApplication::applicationVersion().split('.'); @@ -684,86 +323,64 @@ void Session::dataFromClient() servHeader.append(wrInt(ver[0].toULongLong(), 16)); servHeader.append(wrInt(ver[1].toULongLong(), 16)); servHeader.append(wrInt(ver[2].toULongLong(), 16)); - servHeader.append(sessionId); + servHeader.append(sessionId, BLKSIZE_SESSION_ID); - if (clientMajor == 1) + addIpAction("Session Active"); + + if (tcpSocket->peerAddress().isLoopback()) { - flags |= VER_OK; + // SSL encryption is optional for locally connected clients + // so sesOk() can be called right away instead of starting + // an SSL handshake. - addIpAction("Session Active"); + // reply value 1 means the client needs to take no further + // action, just await a message from the ASYNC_RDY async + // command id. - if (tcpSocket->peerAddress().isLoopback()) - { - // SSL encryption is optional for locally connected clients - // so run() can be called right away instead of starting - // an SSL handshake. - - // reply value 1 means the client version is acceptable - // and the client needs to take no further action, just - // await an IDLE mrci frame from the host to indicate - // that it is ready to take commands. - - servHeader[0] = 1; - - tcpSocket->write(servHeader); - - run(); - } - else - { - flags |= SSL_HOLD; - - QByteArray certBa; - QByteArray privBa; - - if (getCertAndKey(coName, certBa, privBa)) - { - servHeader[0] = 2; - - // reply value 2 means the client version is acceptable - // but the host will now send it's Pem formatted SSL cert - // data in a HOST_CERT mrci frame just after sending it's - // header. - - // the client must use this cert and send a STARTTLS - // signal when ready. - - tcpSocket->setLocalCertificate(toSSLCert(certBa)); - tcpSocket->setPrivateKey(toSSLKey(privBa)); - tcpSocket->write(servHeader); - - dataToClient(ASYNC_SYS_MSG, certBa, HOST_CERT); - - tcpSocket->startServerEncryption(); - } - else - { - servHeader[0] = 4; - - // reply value 4 means the host was unable to load the - // SSL cert associated with the common name sent by the - // client. the session will lock out and auto close at - // this point. - - tcpSocket->write(servHeader); - - endSession(); - } - } - } - else - { - // replay value 3 means the client version is not supported - // by the host and the session will end after sending the - // header. - - servHeader[0] = 3; - - addIpAction("Client Rejected"); + servHeader[0] = 1; tcpSocket->write(servHeader); - endSession(); + sesRdy(); + } + else + { + QByteArray certBa; + QByteArray privBa; + + if (getCertAndKey(coName, certBa, privBa)) + { + servHeader[0] = 2; + + // reply value 2 means the client version is acceptable + // but the host will now send it's Pem formatted SSL cert + // data in a HOST_CERT mrci frame just after sending it's + // header. + + // the client must use this cert and send a STARTTLS + // signal when ready. + + tcpSocket->setLocalCertificate(toSSLCert(certBa)); + tcpSocket->setPrivateKey(toSSLKey(privBa)); + tcpSocket->write(servHeader); + + dataToClient(ASYNC_SYS_MSG, certBa, HOST_CERT); + + tcpSocket->startServerEncryption(); + } + else + { + servHeader[0] = 4; + + // reply value 4 means the host was unable to load the + // SSL cert associated with the common name sent by the + // client. the session will lock out and auto close at + // this point. + + tcpSocket->write(servHeader); + + endSession(); + } } } else @@ -774,558 +391,327 @@ void Session::dataFromClient() } } -void Session::dataToClient(quint16 cmdId, const QByteArray &data, uchar typeId) +void Session::dataToClient(quint32 cmdId, const QByteArray &data, quint8 typeId) { - if (isSlave() && (flags & IPC_LINK_OK)) + tcpSocket->write(wrFrame(cmdId, data, typeId)); +} + +void Session::asyncToClient(quint16 cmdId, const QByteArray &data, quint8 typeId) +{ + dataToClient(toCmdId32(cmdId, 0), data, typeId); +} + +void Session::logout(const QByteArray &uId, bool reload) +{ + if (rd8BitFromBlock(activeUpdate)) { - if (typeId == PUB_IPC_WITH_FEEDBACK) + castPeerStat(rdFromBlock(openSubChs, MAX_OPEN_SUB_CHANNELS * BLKSIZE_SUB_CHANNEL), true); + } + + emit asyncToPeers(ASYNC_CLOSE_P2P, rdFromBlock(sessionId, BLKSIZE_SESSION_ID)); + + memset(userId, 0, BLKSIZE_USER_ID); + memset(userName, 0, BLKSIZE_USER_NAME); + memset(displayName, 0, BLKSIZE_DISP_NAME); + memset(openSubChs, 0, MAX_OPEN_SUB_CHANNELS * BLKSIZE_SUB_CHANNEL); + memset(openWritableSubChs, 0, MAX_OPEN_SUB_CHANNELS * BLKSIZE_SUB_CHANNEL); + memset(chList, 0, MAX_CHANNELS_PER_USER * BLKSIZE_CHANNEL_ID); + + wr32BitToBlock(0, hostRank); + wr8BitToBlock(0, activeUpdate); + wr8BitToBlock(0, chOwnerOverride); + + flags &= ~LOGGED_IN; + + if (uId.isEmpty()) + { + if (reload) { - backendDataIn(cmdId, data); + loadCmds(); } - - ipcLink->write(wrFrame(cmdId, data, typeId)); - } - else if (isMain()) - { - tcpSocket->write(wrFrame(cmdId, data, typeId)); - } -} - -void Session::peersDataIn(quint16 cmdId, const QByteArray &data) -{ - if (isSlave()) - { - qDebug() << "Session::peersDataIn() called while running in slave mode."; - } - else if (flags & IPC_LINK_OK) - { - ipcLink->write(wrFrame(cmdId, data, PRIV_IPC)); - } -} - -void Session::logout() -{ - if (isMain()) - { - qDebug() << "Session::logout() called while running on the main process."; } else { - userName.clear(); - groupName.clear(); - displayName.clear(); - userId.clear(); - chIds.clear(); - chList.clear(); - - hostRank = 0; - - dataToClient(ASYNC_LOGOUT, QByteArray(), PRIV_IPC); - - emit loadCommands(); + login(uId); } } -void Session::authOk() +void Session::login(const QByteArray &uId) { - if (isMain()) + if (flags & LOGGED_IN) { - qDebug() << "Session:authOk() called while running on the main process."; + logout(uId, true); } else { - if (!userName.isEmpty()) + Query db(this); + + db.setType(Query::PULL, TABLE_USERS); + db.addColumn(COLUMN_USERNAME); + db.addColumn(COLUMN_HOST_RANK); + db.addColumn(COLUMN_DISPLAY_NAME); + db.addCondition(COLUMN_USER_ID, uId); + db.exec(); + + wrToBlock(uId, userId, BLKSIZE_USER_ID); + wrStringToBlock(db.getData(COLUMN_USERNAME).toString(), userName, BLKSIZE_USER_NAME); + wrStringToBlock(db.getData(COLUMN_DISPLAY_NAME).toString(), displayName, BLKSIZE_DISP_NAME); + wr32BitToBlock(db.getData(COLUMN_HOST_RANK).toUInt(), hostRank); + + db.setType(Query::PULL, TABLE_CH_MEMBERS); + db.addColumn(COLUMN_CHANNEL_ID); + db.addCondition(COLUMN_USER_ID, uId); + db.exec(); + + memset(chList, 0, MAX_CHANNELS_PER_USER * BLKSIZE_CHANNEL_ID); + + for (int i = 0; i < db.rows(); ++i) { - Query db(this); + QByteArray chId = wrInt(db.getData(COLUMN_CHANNEL_ID, i).toULongLong(), 64); - db.setType(Query::PULL, TABLE_CH_MEMBERS); - db.addColumn(COLUMN_CHANNEL_NAME); - db.addCondition(COLUMN_USERNAME, userName); - db.exec(); - - chList.clear(); - - for (int i = 0; i < db.rows(); ++i) - { - chList.append(db.getData(COLUMN_CHANNEL_NAME, i).toString().toLower()); - } - - dataToClient(ASYNC_USER_LOGIN, userId, PRIV_IPC); - sendLocalInfo(); + addBlockToBlockset(chId.data(), chList, MAX_CHANNELS_PER_USER, BLKSIZE_CHANNEL_ID); } - emit loadCommands(); - } -} + flags |= LOGGED_IN; -void Session::castPeerInfo() -{ - if (isMain()) - { - qDebug() << "Session::castPeerInfo() called while running on the main process."; - } - else - { - if (activeUpdate) - { - // format: [54bytes(chIds)][1byte(typeId)][rest-of-bytes(PEER_INFO)] - - QByteArray castHeader = chIds + wrInt(PEER_INFO, 8); - QByteArray data = toPEER_INFO(shared); - - dataToClient(ASYNC_LIMITED_CAST, castHeader + data, PUB_IPC); - } + sendLocalInfo(); + loadCmds(); } } void Session::sendLocalInfo() { - if (isMain()) + QByteArray frame = createPeerInfoFrame(); + + Query db; + + db.setType(Query::PULL, TABLE_USERS); + db.addColumn(COLUMN_EMAIL); + db.addColumn(COLUMN_EMAIL_VERIFIED); + db.addCondition(COLUMN_USER_ID, rdFromBlock(userId, BLKSIZE_USER_ID)); + db.exec(); + + frame.append(fixedToTEXT(db.getData(COLUMN_EMAIL).toString(), BLKSIZE_EMAIL_ADDR)); + frame.append(rdFromBlock(hostRank, BLKSIZE_HOST_RANK)); + + if (db.getData(COLUMN_EMAIL_VERIFIED).toBool()) { - qDebug() << "Session::sendLocalInfo() called while runnning on the main process."; + frame.append(static_cast(0x01)); } else { - dataToClient(ASYNC_SYS_MSG, toMY_INFO(shared), MY_INFO); + frame.append(static_cast(0x00)); } + + dataToClient(ASYNC_SYS_MSG, frame, MY_INFO); } -void Session::backendDataIn(quint16 cmdId, const QByteArray &data) +void Session::castPeerStat(const QByteArray &oldSubIds, bool isDisconnecting) { - if (flags & IPC_LINK_OK) + if (rd8BitFromBlock(activeUpdate)) { - if (isMain()) + // format: [54bytes(chIds)][1byte(typeId)][rest-of-bytes(PEER_STAT)] + + QByteArray typeId = wrInt(PEER_STAT, 8); + QByteArray sesId = rdFromBlock(sessionId, BLKSIZE_SESSION_ID); + QByteArray openSubs = rdFromBlock(openSubChs, MAX_OPEN_SUB_CHANNELS * BLKSIZE_SUB_CHANNEL); + QByteArray dc; + + if (isDisconnecting) { - if (cmdId == ASYNC_END_SESSION) - { - endSession(); - } - else if (cmdId == ASYNC_USER_LOGIN) - { - userId = data; - } - else if (cmdId == ASYNC_LOGOUT) - { - userId.clear(); - } - else if (cmdId == ASYNC_EXIT) - { - emit closeServer(); - } - else if (cmdId == ASYNC_RESTART) - { - emit resServer(); - } - else if (cmdId == ASYNC_MAXSES) - { - emit setMaxSessions(static_cast(rdInt(data))); - } - else if (cmdId == ASYNC_DISABLE_MOD) - { - emit delayedModDel(fromTEXT(data)); - } + dc = QByteArray(1, 0x01); } else { - if (cmdId == ASYNC_END_SESSION) - { - p2pAccepted.clear(); - p2pPending.clear(); - - dataToClient(ASYNC_CLOSE_P2P, sessionId, PUB_IPC); - - emit closeExe(); - } - else if (cmdId == ASYNC_USER_DELETED) - { - if (!userName.isEmpty()) - { - if (noCaseMatch(userName, fromTEXT(data))) - { - logout(); - dataToClient(ASYNC_SYS_MSG, toTEXT("\nsystem: your session was forced to logout because your account was deleted.\n"), TEXT); - dataToClient(ASYNC_USER_DELETED, data, TEXT); - } - } - } - else if (cmdId == ASYNC_CAST) - { - // format: [54bytes(wrAbleChIds)][1byte(typeId)][rest-of-bytes(payload)] - - if (matchChs(chIds, QByteArray::fromRawData(data.data(), 54))) - { - dataToClient(cmdId, data.mid(55), static_cast(data[54])); - } - } - else if (cmdId == ASYNC_TO_PEER) - { - // format: [28bytes(sessionId)][1byte(typeId)][rest-of-bytes(payload)] - - if (QByteArray::fromRawData(data.data(), 28) == sessionId) - { - dataToClient(cmdId, data.mid(29), static_cast(data[28])); - } - } - else if (cmdId == ASYNC_P2P) - { - // format: [28bytes(dst_sessionId)][28bytes(src_sessionId)][1byte(typeId)][rest-of-bytes(payload)] - - if (QByteArray::fromRawData(data.data(), 28) == sessionId) - { - QByteArray src = data.mid(28, 28); - - if (data[56] == P2P_REQUEST) - { - if (!p2pPending.contains(src) && !p2pAccepted.contains(src)) - { - p2pPending.append(src); - - dataToClient(cmdId, data.mid(57), P2P_REQUEST); - } - } - else if (data[56] == P2P_OPEN) - { - if (p2pPending.contains(src) && !p2pAccepted.contains(src)) - { - p2pPending.removeAll(src); - p2pAccepted.append(src); - - dataToClient(cmdId, data.mid(57), P2P_OPEN); - } - } - else if (data[56] == P2P_CLOSE) - { - if (p2pPending.contains(src)) - { - p2pPending.removeAll(src); - - dataToClient(cmdId, data.mid(57), P2P_CLOSE); - } - else if (p2pAccepted.contains(src)) - { - p2pAccepted.removeAll(src); - - dataToClient(cmdId, data.mid(57), P2P_CLOSE); - } - } - else if (p2pAccepted.contains(src)) - { - dataToClient(cmdId, src + data.mid(57), static_cast(data[56])); - } - } - } - else if (cmdId == ASYNC_CLOSE_P2P) - { - // format: [28bytes(src_sessionId)] - - if (p2pAccepted.contains(data) || p2pPending.contains(data)) - { - p2pAccepted.removeAll(data); - p2pPending.removeAll(data); - - dataToClient(ASYNC_P2P, data, P2P_CLOSE); - } - } - else if (cmdId == ASYNC_LIMITED_CAST) - { - // format: [54bytes(chIds)][1byte(typeId)][rest-of-bytes(payload)] - - if (activeUpdate && matchChs(chIds, QByteArray::fromRawData(data.data(), 54))) - { - if (data[54] == PING_PEERS) - { - // PING_PEERS is formatted exactly like PEER_INFO. it only tells this - // async command to also send PEER_INFO of this session to the session - // that requested the ping using ASYNC_TO_PEER. - - QByteArray peerId = data.mid(55, 28); - QByteArray typeId = wrInt(PEER_INFO, 8); - QByteArray info = toPEER_INFO(shared); - - dataToClient(ASYNC_TO_PEER, peerId + typeId + info, PUB_IPC); - dataToClient(cmdId, data.mid(55), PEER_INFO); - } - else - { - dataToClient(cmdId, data.mid(55), static_cast(data[54])); - } - } - } - else if ((cmdId == ASYNC_GROUP_RENAMED) || (cmdId == ASYNC_GRP_TRANS)) - { - QStringList args = parseArgs(data, -1); - QString name = getParam("-src", args); - - if (noCaseMatch(groupName, name)) - { - groupName = getParam("-dst", args); - - if (cmdId == ASYNC_GRP_TRANS) - { - hostRank = getRankForGroup(groupName); - - emit loadCommands(); - - sendLocalInfo(); - } - } - } - else if (cmdId == ASYNC_RW_MY_INFO) - { - if (noCaseMatch(userName, fromTEXT(data))) - { - sendLocalInfo(); - } - } - else if (cmdId == ASYNC_USER_RENAMED) - { - QStringList args = parseArgs(data, -1); - QString name = getParam("-old", args); - - if (noCaseMatch(userName, name)) - { - userName = getParam("-new_name", args); - - castPeerInfo(); - sendLocalInfo(); - } - } - else if (cmdId == ASYNC_DISP_RENAMED) - { - QStringList args = parseArgs(data, -1); - QString uName = getParam("-user", args); - - if (noCaseMatch(userName, uName)) - { - displayName = getParam("-name", args); - - castPeerInfo(); - sendLocalInfo(); - } - } - else if (cmdId == ASYNC_USER_GROUP_CHANGED) - { - QStringList args = parseArgs(data, -1); - QString uName = getParam("-user", args); - - if (noCaseMatch(userName, uName)) - { - groupName = getParam("-group", args); - hostRank = getRankForGroup(groupName); - - emit loadCommands(); - - sendLocalInfo(); - } - } - else if (cmdId == ASYNC_CMD_RANKS_CHANGED) - { - emit loadCommands(); - } - else if (cmdId == ASYNC_GROUP_UPDATED) - { - QStringList args = parseArgs(data, -1); - QString name = getParam("-name", args); - - if (noCaseMatch(groupName, name)) - { - hostRank = getParam("-rank", args).toUInt(); - - emit loadCommands(); - } - } - else if (cmdId == ASYNC_RESTORE_AUTH) - { - Query db(this); - - db.setType(Query::PULL, TABLE_USERS); - db.addColumn(COLUMN_USERNAME); - db.addColumn(COLUMN_GRNAME); - db.addColumn(COLUMN_DISPLAY_NAME); - db.addCondition(COLUMN_USER_ID, data); - db.exec(); - - userId = data; - userName = db.getData(COLUMN_USERNAME).toString(); - groupName = db.getData(COLUMN_GRNAME).toString(); - displayName = db.getData(COLUMN_DISPLAY_NAME).toString(); - hostRank = getRankForGroup(groupName); - - authOk(); - dataToClient(ASYNC_RDY, toTEXT("\nReady!\n\n"), TEXT); - } - else if (cmdId == ASYNC_PUBLIC_AUTH) - { - userName.clear(); - groupName.clear(); - displayName.clear(); - userId.clear(); - chIds.clear(); - chList.clear(); - - hostRank = 0; - - authOk(); - dataToClient(ASYNC_RDY, toTEXT("\nReady!\n\n"), TEXT); - } - else if (cmdId == ASYNC_ENABLE_MOD) - { - emit loadModFile(fromTEXT(data)); - emit loadCommands(); - } - else if (cmdId == ASYNC_DISABLE_MOD) - { - emit unloadModFile(fromTEXT(data)); - } - else if ((cmdId == ASYNC_NEW_CH_MEMBER) || (cmdId == ASYNC_INVITED_TO_CH) || - (cmdId == ASYNC_INVITE_ACCEPTED)) - { - QStringList args = parseArgs(data, -1); - QString user = getParam("-user", args); - QString chName = getParam("-ch_name", args).toLower(); - - if (noCaseMatch(user, userName)) - { - if ((cmdId == ASYNC_NEW_CH_MEMBER) || (cmdId == ASYNC_INVITE_ACCEPTED)) - { - chList.append(chName); - } - - dataToClient(cmdId, data, TEXT); - } - } - else if (cmdId == ASYNC_DEL_CH) - { - QStringList args = parseArgs(data, -1); - QString chName = getParam("-ch_name", args).toLower(); - - if (chList.contains(chName)) - { - chList.removeAll(chName); - - wrCloseCh(rwShared, getParam("-ch_id", args).toULongLong()); - dataToClient(cmdId, data, TEXT); - } - } - else if (cmdId == ASYNC_RM_CH_MEMBER) - { - QStringList args = parseArgs(data, -1); - QString uName = getParam("-user", args); - - if (noCaseMatch(userName, uName)) - { - QString chName = getParam("-ch_name", args).toLower(); - - chList.removeAll(chName); - - QByteArray peerStat; - - wrCloseCh(rwShared, getParam("-ch_id", args).toULongLong(), peerStat); - - if (!peerStat.isEmpty()) - { - dataToClient(ASYNC_LIMITED_CAST, peerStat, PUB_IPC); - } - - dataToClient(cmdId, data, TEXT); - } - } - else if (cmdId == ASYNC_MEM_LEVEL_CHANGED) - { - QStringList args = parseArgs(data, -1); - QString uName = getParam("-user", args); - QString chName = getParam("-ch_name", args).toLower(); - - if (noCaseMatch(userName, uName)) - { - QByteArray peerStat; - - wrCloseCh(rwShared, getParam("-ch_id", args).toULongLong(), peerStat); - - if (!peerStat.isEmpty()) - { - dataToClient(ASYNC_LIMITED_CAST, peerStat, PUB_IPC); - } - - dataToClient(cmdId, data, TEXT); - } - else if (chList.contains(chName)) - { - dataToClient(cmdId, data, TEXT); - } - } - else if (cmdId == ASYNC_RENAME_CH) - { - QStringList args = parseArgs(data, -1); - QString chName = getParam("-ch_name", args).toLower(); - - if (chList.contains(chName)) - { - QString newName = getParam("-new_name", args).toLower(); - - chList.removeAll(chName); - chList.append(newName); - - dataToClient(cmdId, data, TEXT); - } - } - else if (cmdId == ASYNC_CH_ACT_FLAG) - { - QStringList args = parseArgs(data, -1); - QString chName = getParam("-ch_name", args).toLower(); - - if (chList.contains(chName)) - { - activeUpdate = containsActiveCh(chIds); - } - } - else if ((cmdId == ASYNC_NEW_SUB_CH) || (cmdId == ASYNC_RENAME_SUB_CH)) - { - QStringList args = parseArgs(data, -1); - QString chName = getParam("-ch_name", args).toLower(); - - if (chList.contains(chName)) - { - dataToClient(cmdId, data, TEXT); - } - } - else if ((cmdId == ASYNC_RM_SUB_CH) || (cmdId == ASYNC_SUB_CH_LEVEL_CHG) || - (cmdId == ASYNC_RM_RDONLY) || (cmdId == ASYNC_ADD_RDONLY)) - { - QStringList args = parseArgs(data, -1); - QString chName = getParam("-ch_name", args).toLower(); - QString chId = getParam("-ch_id", args); - QString subId = getParam("-sub_id", args); - QByteArray chSub = wrInt(chId.toULongLong(), 64) + wrInt(subId.toUInt(), 8); - - int pos = chPos(chSub, chIds); - - if (pos != -1) - { - wrCloseCh(rwShared, chSub); - dataToClient(cmdId, data, TEXT); - } - else if (chList.contains(chName)) - { - dataToClient(cmdId, data, TEXT); - } - } + dc = QByteArray(1, 0x00); } + + emit asyncToPeers(ASYNC_LIMITED_CAST, oldSubIds + typeId + sesId + openSubs + dc); } } -void Session::sendStdout() +void Session::castPeerInfo(quint8 typeId) { - if (isSlave()) + if (rd8BitFromBlock(activeUpdate)) { - qDebug() << "CmdExecutor: " << slaveProc->readAllStandardOutput(); + // format: [54bytes(chIds)][1byte(typeId)][rest-of-bytes(PEER_INFO)] + + QByteArray openSubs = rdFromBlock(openWritableSubChs, MAX_OPEN_SUB_CHANNELS * BLKSIZE_SUB_CHANNEL); + QByteArray typeIdBa = wrInt(typeId, 8); + QByteArray frame = createPeerInfoFrame(); + + emit asyncToPeers(ASYNC_LIMITED_CAST, openSubs + typeIdBa + frame); } } -void Session::sendStderr() +void Session::castPingForPeers() { - if (isSlave()) + castPeerInfo(PING_PEERS); +} + +void Session::closeByChId(const QByteArray &chId, bool peerCast) +{ + QByteArray oldSubChs; + + if (peerCast) { - qDebug() << "CmdExecutor Err: " << slaveProc->readAllStandardError(); + oldSubChs = QByteArray(openSubChs, MAX_OPEN_SUB_CHANNELS * BLKSIZE_SUB_CHANNEL); + } + + rmLikeBlkFromBlkset(chId, openWritableSubChs, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL); + + if (rmLikeBlkFromBlkset(chId, openSubChs, MAX_OPEN_SUB_CHANNELS, BLKSIZE_SUB_CHANNEL) && peerCast) + { + castPeerStat(oldSubChs, false); } } + +void Session::privAsyncDataIn(quint16 cmdId, const QByteArray &data) +{ + sharedMem->lock(); + + if (cmdId == ASYNC_END_SESSION) + { + endSession(); + } + else if (cmdId == ASYNC_USER_LOGIN) + { + login(data); + } + else if (cmdId == ASYNC_LOGOUT) + { + logout("", true); + } + else if (cmdId == ASYNC_EXIT) + { + emit closeServer(); + } + else if (cmdId == ASYNC_RESTART) + { + emit resServer(); + } + else if (cmdId == ASYNC_MAXSES) + { + emit setMaxSessions(static_cast(rdInt(data))); + } + else if (cmdId == ASYNC_UPDATE_BANS) + { + emit updateBanList(); + } + else if (cmdId == ASYNC_PING_PEERS) + { + castPingForPeers(); + } + else if (cmdId == ASYNC_OPEN_SUBCH) + { + openSubChannel(data); + } + else if (cmdId == ASYNC_CLOSE_SUBCH) + { + closeSubChannel(data); + } + else if (cmdId == ASYNC_SET_DIR) + { + currentDir = fromTEXT(data); + } + else if (cmdId == ASYNC_DEBUG_TEXT) + { + qDebug() << fromTEXT(data); + } + + sharedMem->unlock(); +} + +void Session::pubAsyncDataIn(quint16 cmdId, const QByteArray &data) +{ + sharedMem->lock(); + + if (cmdId == ASYNC_USER_DELETED) + { + acctDeleted(data); + } + else if (cmdId == ASYNC_CAST) + { + castCatch(data); + } + else if (cmdId == ASYNC_TO_PEER) + { + directDataFromPeer(data); + } + else if (cmdId == ASYNC_P2P) + { + p2p(data); + } + else if (cmdId == ASYNC_CLOSE_P2P) + { + closeP2P(data); + } + else if (cmdId == ASYNC_LIMITED_CAST) + { + limitedCastCatch(data); + } + else if (cmdId == ASYNC_RW_MY_INFO) + { + acctEdited(data); + } + else if (cmdId == ASYNC_USER_RENAMED) + { + acctRenamed(data); + } + else if (cmdId == ASYNC_DISP_RENAMED) + { + acctDispChanged(data); + } + else if (cmdId == ASYNC_USER_RANK_CHANGED) + { + updateRankViaUser(data); + } + else if (cmdId == ASYNC_CMD_RANKS_CHANGED) + { + loadCmds(); + } + else if (cmdId == ASYNC_ENABLE_MOD) + { + addModule(data); + } + else if (cmdId == ASYNC_DISABLE_MOD) + { + rmModule(data); + } + else if ((cmdId == ASYNC_NEW_CH_MEMBER) || (cmdId == ASYNC_INVITED_TO_CH) || + (cmdId == ASYNC_INVITE_ACCEPTED)) + { + userAddedToChannel(cmdId, data); + } + else if (cmdId == ASYNC_RM_CH_MEMBER) + { + userRemovedFromChannel(data); + } + else if (cmdId == ASYNC_DEL_CH) + { + channelDeleted(data); + } + else if (cmdId == ASYNC_MEM_LEVEL_CHANGED) + { + channelMemberLevelUpdated(data); + } + else if (cmdId == ASYNC_RENAME_CH) + { + channelRenamed(data); + } + else if (cmdId == ASYNC_CH_ACT_FLAG) + { + channelActiveFlagUpdated(data); + } + else if ((cmdId == ASYNC_NEW_SUB_CH) || (cmdId == ASYNC_RENAME_SUB_CH)) + { + subChannelAdded(cmdId, data); + } + else if ((cmdId == ASYNC_RM_SUB_CH) || (cmdId == ASYNC_SUB_CH_LEVEL_CHG) || + (cmdId == ASYNC_RM_RDONLY) || (cmdId == ASYNC_ADD_RDONLY)) + { + subChannelUpdated(cmdId, data); + } + + sharedMem->unlock(); +} diff --git a/src/session.h b/src/session.h index f308501..181e50c 100644 --- a/src/session.h +++ b/src/session.h @@ -18,112 +18,108 @@ // . #include "common.h" -#include "int_loader.h" +#include "module.h" #include "make_cert.h" -#include "cmd_executor.h" +#include "cmd_proc.h" -class Session : public QObject +QByteArray wrFrame(quint32 cmdId, const QByteArray &data, uchar dType); + +class Session : public MemShare { Q_OBJECT private: - QList p2pAccepted; - QList p2pPending; - QList chList; - QSharedMemory *exeDebugInfo; - QLocalServer *ipcServ; - QLocalSocket *ipcLink; - QSslSocket *tcpSocket; - QProcess *slaveProc; - RWSharedObjs *rwShared; - SharedObjs *shared; - CmdExecutor *executor; - QTime lastExeCrash; - QString peerIp; - QString pipeName; - QString userName; - QString groupName; - QString displayName; - QString appName; - ushort clientMajor; - ushort clientMinor; - ushort clientPatch; - QByteArray chIds; - QByteArray wrAbleChIds; - QByteArray sessionId; - QByteArray userId; - uint hostRank; - uint flags; - uint ipcFrameSize; - uint tcpFrameSize; - uchar ipcFrameType; - uchar tcpFrameType; - quint16 ipcFrameCmdId; - quint16 tcpFrameCmdId; - bool activeUpdate; - bool chOwnerOverride; - int exeCrashCount; + QSslSocket *tcpSocket; + QString currentDir; + QHash modCmdNames; + QHash > frameQueue; + QHash cmdProcesses; + QHash cmdUniqueNames; + QHash cmdRealNames; + QHash cmdAppById; + QList cmdIds; + quint32 flags; + quint32 tcpPayloadSize; + quint32 tcpFrameCmdId; + quint8 tcpFrameType; - void genSessionId(); - void castPeerInfo(); - void sendLocalInfo(); - void rdExeDebug(); - void addIpAction(const QString &action); - void modLoadCrashCheck(const QString &crashInfo); + void castPingForPeers(); + void sendLocalInfo(); + void loadCmds(); + void closeByChId(const QByteArray &chId, bool peerCast); + void castPeerInfo(quint8 typeId); + void login(const QByteArray &uId); + void logout(const QByteArray &uId, bool reload); + void startCmdProc(quint32 cmdId); + void startModProc(const QString &modApp); + void addIpAction(const QString &action); + void castPeerStat(const QByteArray &oldSubIds, bool isDisconnecting); + ModProcess *initModProc(const QString &modApp); + QByteArray genSessionId(); + + // async_funcs.cpp ---- + + void openSubChannel(const QByteArray &data); + void closeSubChannel(const QByteArray &data); + void acctDeleted(const QByteArray &data); + void acctEdited(const QByteArray &data); + void acctRenamed(const QByteArray &data); + void acctDispChanged(const QByteArray &data); + void castCatch(const QByteArray &data); + void directDataFromPeer(const QByteArray &data); + void p2p(const QByteArray &data); + void closeP2P(const QByteArray &data); + void limitedCastCatch(const QByteArray &data); + void updateRankViaUser(const QByteArray &data); + void addModule(const QByteArray &data); + void rmModule(const QByteArray &data); + void userAddedToChannel(quint16 cmdId, const QByteArray &data); + void userRemovedFromChannel(const QByteArray &data); + void channelDeleted(const QByteArray &data); + void channelMemberLevelUpdated(const QByteArray &data); + void channelRenamed(const QByteArray &data); + void channelActiveFlagUpdated(const QByteArray &data); + void subChannelAdded(quint16 cmdId, const QByteArray &data); + void subChannelUpdated(quint16 cmdId, const QByteArray &data); + + //--------------------- private slots: - void logout(); - void newIPCLink(); - void ipcConnected(); - void ipcDisconnected(); void dataFromClient(); - void dataFromIPC(); void payloadDeleted(); - void sendStdout(); - void sendStderr(); - void exeStarted(); - void closeInstance(); - void authOk(); - void ipcOk(); - void newIPCTimeout(); - void exeFinished(int ret, QProcess::ExitStatus status); - void exeError(QProcess::ProcessError err); - void ipcError(QLocalSocket::LocalSocketError socketError); - void dataToClient(quint16 cmdId, const QByteArray &data, uchar typeId); + void cmdProcFinished(quint32 cmdId); + void cmdProcStarted(quint32 cmdId, CmdProcess *obj); + void asyncToClient(quint16 cmdId, const QByteArray &data, quint8 typeId); + void dataToClient(quint32 cmdId, const QByteArray &data, quint8 typeId); + void dataToCmd(quint32 cmdId, const QByteArray &data, quint8 typeId); public: - explicit Session(QObject *parent = nullptr); - - void initAsMain(QSslSocket *tcp); - void startAsSlave(const QStringList &args); - bool isSlave(); - bool isMain(); + explicit Session(const QString &hostKey, QSslSocket *tcp, QObject *parent = nullptr); public slots: - void backendDataIn(quint16 cmdId, const QByteArray &data); - void peersDataIn(quint16 cmdId, const QByteArray &data); + void pubAsyncDataIn(quint16 cmdId, const QByteArray &data); + void privAsyncDataIn(quint16 cmdId, const QByteArray &data); void connectToPeer(const QSharedPointer &peer); void endSession(); - void run(); + void sesRdy(); + void init(); signals: - void dataToCommand(quint16 cmdId, const QByteArray &data, uchar dType); - void backendToPeers(quint16 cmdId, const QByteArray data); + void killCmd16(quint16 cmdId); + void killCmd32(quint32 cmdId); + void asyncToPeers(quint16 cmdId, const QByteArray data); void connectPeers(QSharedPointer peer); - void setMaxSessions(uint value); - void unloadModFile(const QString &modName); - void loadModFile(const QString &modName); - void delayedModDel(const QString &modName); + void setMaxSessions(quint32 value); void ended(); - void closeExe(); void closeServer(); void resServer(); - void loadCommands(); + void updateBanList(); + void killMods(); }; #endif // SOCKET_H diff --git a/src/tcp_server.cpp b/src/tcp_server.cpp index 0901211..8b98dbc 100644 --- a/src/tcp_server.cpp +++ b/src/tcp_server.cpp @@ -16,60 +16,24 @@ // along with MRCI under the LICENSE.md file. If not, see // . -ModDeleteTimer::ModDeleteTimer(const QString &mod, QStringList *queue, QObject *parent) : QTimer (parent) -{ - delQueue = queue; - modName = mod; - - setInterval(5000); // 5 seconds - - queue->append(mod); - - connect(this, &ModDeleteTimer::timeout, this, &ModDeleteTimer::delMod); -} - -void ModDeleteTimer::resetTimer(const QString &mod) -{ - if (modName == mod) - { - start(); - } -} - -void ModDeleteTimer::delMod() -{ - Query db(this); - - db.setType(Query::PULL, TABLE_MODULES); - db.addColumn(COLUMN_MOD_MAIN); - db.addCondition(COLUMN_MOD_NAME, modName); - db.exec(); - - if (db.rows()) - { - QString file = db.getData(COLUMN_MOD_MAIN).toString(); - - QDir(QFileInfo(file).path()).removeRecursively(); - - db.setType(Query::DEL, TABLE_MODULES); - db.addCondition(COLUMN_MOD_NAME, modName); - db.exec(); - } - - delQueue->removeAll(modName); - - deleteLater(); -} - TCPServer::TCPServer(QObject *parent) : QTcpServer(parent) { - sessionCounter = new QSharedMemory(sessionCountShareKey(), this); - controlPipe = new QLocalServer(this); - controlSocket = nullptr; - flags = 0; + controlPipe = new QLocalServer(this); + hostSharedMem = new QSharedMemory(this); + hostKey = createHostSharedMem(hostSharedMem); + hostLoad = static_cast(hostSharedMem->data()); + controlSocket = nullptr; + flags = 0; - sessionCounter->create(4); - sessionCounter->attach(); +#ifdef Q_OS_LINUX + + setupUnixSignalHandlers(); + + auto *signalHandler = new UnixSignalHandler(QCoreApplication::instance()); + + connect(signalHandler, &UnixSignalHandler::closeServer, this, &TCPServer::closeServer); + +#endif connect(controlPipe, &QLocalServer::newConnection, this, &TCPServer::newPipeConnection); } @@ -96,50 +60,65 @@ void TCPServer::closedPipeConnection() controlSocket = nullptr; } +bool TCPServer::createPipe() +{ + bool ret = controlPipe->listen(HOST_CONTROL_PIPE); + + controlPipePath = controlPipe->fullServerName(); + + if (!ret) + { + if (QFile::exists(controlPipePath)) + { + QFile::remove(controlPipePath); + } + + ret = controlPipe->listen(HOST_CONTROL_PIPE); + } + + return ret; +} bool TCPServer::start() { - bool ret = false; + close(); + cleanupDbConnection(); - QString contrPath = pipesPath() + "/" + QString(APP_NAME) + ".TCPServer.Control"; + Query db(this); - if (QFile::exists(contrPath)) - { - QFile::remove(contrPath); - } + db.setType(Query::PULL, TABLE_SERV_SETTINGS); + db.addColumn(COLUMN_PORT); + db.addColumn(COLUMN_IPADDR); + db.addColumn(COLUMN_MAXSESSIONS); + db.exec(); - if (!controlPipe->listen(contrPath)) + maxSessions = db.getData(COLUMN_MAXSESSIONS).toUInt(); + + bool ret = false; + QString addr = db.getData(COLUMN_IPADDR).toString(); + quint16 port = static_cast(db.getData(COLUMN_PORT).toUInt()); + + if (!createPipe()) { QTextStream(stderr) << "" << endl << "err: Unable to open a control pipe." << endl; QTextStream(stderr) << "err: Reason - " << controlPipe->errorString() << endl; } + else if (!listen(QHostAddress(addr), port)) + { + QTextStream(stderr) << "" << endl << "err: TCP listen failure on address: " << addr << " port: " << port << endl; + QTextStream(stderr) << "err: Reason - " << errorString() << endl; + } + else if (hostKey.isEmpty()) + { + QTextStream(stderr) << "" << endl << "err: Failed to create the host shared memory block." << endl; + QTextStream(stderr) << "err: Reason - " << hostSharedMem->errorString() << endl; + } else { - close(); - cleanupDbConnection(); + updateBanList(); - Query db(this); - - db.setType(Query::PULL, TABLE_SERV_SETTINGS); - db.addColumn(COLUMN_PORT); - db.addColumn(COLUMN_IPADDR); - db.addColumn(COLUMN_MAXSESSIONS); - db.exec(); - - QString addr = db.getData(COLUMN_IPADDR).toString(); - quint16 port = static_cast(db.getData(COLUMN_PORT).toUInt()); - - if (listen(QHostAddress(addr), port)) - { - ret = true; - - flags |= ACCEPTING; - } - else - { - QTextStream(stderr) << "" << endl << "err: TCP listen failure on address: " << addr << " port: " << port << endl; - QTextStream(stderr) << "err: Reason - " << errorString() << endl; - } + ret = true; + flags |= ACCEPTING; } return ret; @@ -147,9 +126,13 @@ bool TCPServer::start() void TCPServer::sessionEnded() { - uint count = rdSessionLoad() - 1; + hostSharedMem->lock(); - wrSessionLoad(count); + quint32 count = rd32BitFromBlock(hostLoad) - 1; + + wr32BitToBlock(count, hostLoad); + + hostSharedMem->unlock(); if ((count == 0) && (flags & CLOSE_ON_EMPTY)) { @@ -169,12 +152,12 @@ void TCPServer::closeServer() { close(); - if (rdSessionLoad() == 0) + if (rd32BitFromBlock(hostLoad) == 0) { cleanupDbConnection(); controlPipe->close(); - sessionCounter->detach(); + hostSharedMem->detach(); QCoreApplication::instance()->quit(); } @@ -189,7 +172,7 @@ void TCPServer::closeServer() void TCPServer::resServer() { - if (rdSessionLoad() == 0) + if (rd32BitFromBlock(hostLoad) == 0) { controlPipe->close(); @@ -206,13 +189,13 @@ void TCPServer::resServer() bool TCPServer::servOverloaded() { - Query db(this); + hostSharedMem->lock(); - db.setType(Query::PULL, TABLE_SERV_SETTINGS); - db.addColumn(COLUMN_MAXSESSIONS); - db.exec(); + bool ret = rd32BitFromBlock(hostLoad) >= maxSessions; - return rdSessionLoad() >= db.getData(COLUMN_MAXSESSIONS).toUInt(); + hostSharedMem->unlock(); + + return ret; } void TCPServer::procPipeIn() @@ -235,44 +218,48 @@ void TCPServer::procPipeIn() db.setType(Query::PULL, TABLE_SERV_SETTINGS); db.addColumn(COLUMN_IPADDR); db.addColumn(COLUMN_PORT); - db.addColumn(COLUMN_MAXSESSIONS); db.exec(); - txtOut << "" << endl; - txtOut << "Host Load: " << rdSessionLoad() << "/" << db.getData(COLUMN_MAXSESSIONS).toUInt() << endl; - txtOut << "Active Address: " << serverAddress().toString() << endl; - txtOut << "Active Port: " << serverPort() << endl; - txtOut << "Set Address: " << db.getData(COLUMN_IPADDR).toString() << endl; - txtOut << "Set Port: " << db.getData(COLUMN_PORT).toUInt() << endl; - txtOut << "Database Path: " << sqlDataPath() << endl; - txtOut << "Modules Install Path: " << modDataPath() << endl << endl; + hostSharedMem->lock(); + txtOut << "" << endl; + txtOut << "Host Load: " << rd32BitFromBlock(hostLoad) << "/" << maxSessions << endl; + txtOut << "Active Address: " << serverAddress().toString() << endl; + txtOut << "Active Port: " << serverPort() << endl; + txtOut << "Set Address: " << db.getData(COLUMN_IPADDR).toString() << endl; + txtOut << "Set Port: " << db.getData(COLUMN_PORT).toUInt() << endl; + txtOut << "Database Path: " << sqlDataPath() << endl << endl; + + hostSharedMem->unlock(); controlSocket->write(toTEXT(text)); } } -bool TCPServer::inBanList(const QString &ip) +void TCPServer::updateBanList() { + banList.clear(); + Query db(this); db.setType(Query::PULL, TABLE_IPBANS); db.addColumn(COLUMN_IPADDR); - db.addCondition(COLUMN_IPADDR, ip); db.exec(); - return db.rows(); + for (int i = 0; i < db.rows(); ++i) + { + banList.append(db.getData(COLUMN_IPADDR, i).toString()); + } } -void TCPServer::delayedModDel(const QString &modName) +void TCPServer::setMaxSessions(quint32 value) { - if (!modDelQueue.contains(modName)) - { - auto *timer = new ModDeleteTimer(modName, &modDelQueue, this); + Query db(this); - connect(this, &TCPServer::resetModDelTimer, timer, &ModDeleteTimer::resetTimer); - } + db.setType(Query::UPDATE, TABLE_SERV_SETTINGS); + db.addColumn(COLUMN_MAXSESSIONS, value); + db.exec(); - emit resetModDelTimer(modName); + maxSessions = value; } void TCPServer::incomingConnection(qintptr socketDescriptor) @@ -291,37 +278,48 @@ void TCPServer::incomingConnection(qintptr socketDescriptor) { resumeAccepting(); - if (inBanList(soc->peerAddress().toString())) + if (banList.contains(soc->peerAddress().toString(), Qt::CaseInsensitive)) { soc->deleteLater(); } else { - auto *ses = new Session(nullptr); + auto buffSize = static_cast(qPow(2, MAX_FRAME_BITS) - 1) + (MAX_FRAME_BITS / 8) + 4; + // max_data_size_per_frame + size_of_size_bytes + size_of_cmd_id + + soc->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, buffSize); + soc->setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, buffSize); + + auto *ses = new Session(hostKey, soc, nullptr); auto *thr = new QThread(nullptr); connect(thr, &QThread::finished, soc, &QSslSocket::deleteLater); connect(thr, &QThread::finished, ses, &QSslSocket::deleteLater); connect(thr, &QThread::finished, thr, &QSslSocket::deleteLater); + connect(thr, &QThread::started, ses, &Session::init); connect(ses, &Session::ended, this, &TCPServer::sessionEnded); connect(ses, &Session::ended, thr, &QThread::quit); connect(ses, &Session::connectPeers, this, &TCPServer::connectPeers); - connect(ses, &Session::delayedModDel, this, &TCPServer::delayedModDel); connect(ses, &Session::closeServer, this, &TCPServer::closeServer); connect(ses, &Session::resServer, this, &TCPServer::resServer); + connect(ses, &Session::setMaxSessions, this, &TCPServer::setMaxSessions); + connect(ses, &Session::updateBanList, this, &TCPServer::updateBanList); connect(this, &TCPServer::connectPeers, ses, &Session::connectToPeer); connect(this, &TCPServer::endAllSessions, ses, &Session::endSession); serializeThread(thr); - ses->initAsMain(soc); ses->moveToThread(thr); soc->moveToThread(thr); thr->start(); - wrSessionLoad(rdSessionLoad() + 1); + hostSharedMem->lock(); + + wr32BitToBlock((rd32BitFromBlock(hostLoad) + 1), hostLoad); + + hostSharedMem->unlock(); } } } diff --git a/src/tcp_server.h b/src/tcp_server.h index f6925f3..7a0fc97 100644 --- a/src/tcp_server.h +++ b/src/tcp_server.h @@ -22,30 +22,7 @@ #include "session.h" #include "make_cert.h" #include "openssl/ssl.h" - -class ModDeleteTimer : public QTimer -{ - Q_OBJECT - -private: - - QString modName; - QStringList *delQueue; - -private slots: - - void delMod(); - -public slots: - - void resetTimer(const QString &mod); - -public: - - explicit ModDeleteTimer(const QString &mod, QStringList *queue, QObject *parent = nullptr); -}; - -//-------------------------- +#include "unix_signal.h" class TCPServer: public QTcpServer { @@ -53,14 +30,18 @@ class TCPServer: public QTcpServer private: - QSharedMemory *sessionCounter; + QSharedMemory *hostSharedMem; QLocalServer *controlPipe; QLocalSocket *controlSocket; - QStringList modDelQueue; - uint flags; + char *hostLoad; + QStringList banList; + QString controlPipePath; + QString hostKey; + quint32 maxSessions; + quint32 flags; bool servOverloaded(); - bool inBanList(const QString &ip); + bool createPipe(); void incomingConnection(qintptr socketDescriptor); private slots: @@ -69,7 +50,8 @@ private slots: void newPipeConnection(); void closedPipeConnection(); void sessionEnded(); - void delayedModDel(const QString &modName); + void updateBanList(); + void setMaxSessions(quint32 value); public slots: @@ -85,7 +67,6 @@ public: signals: void connectPeers(QSharedPointer peer); - void resetModDelTimer(const QString &modName); void endAllSessions(); };