Slimmed down and simplified host administering

- I decided to remove the entire concept of a root user.
  Instead, the host initializes as a blank slate and it
  will be up to the host admin to create a rank 1 user via
  the new command line option "-add_admin" to do initial
  setup with.

- There is no longer such a concept as a protected user.
  Meaning even the last rank 1 user in the host database
  is allowed to delete or modify the rank of their own
  account. To prevent permanent "admin lock out" in this
  scenario the "-elevate" command line option was created.

- Host settings are no longer stored in the database.
  Instead, host settings are now stored in a conf.json file
  in /etc/mrci/conf.json if running on a linux based OS or
  in %Programdata%\mrci\conf.json if running on Windows.

- Email templates are no longer stored in the database.
  Instead, the templates can be any file formatted in UTF-8
  text stored in the host file system. The files they point
  to can be modified in the conf.json file.

- The conf file also replaced all use env variables so
  MRCI_DB_PATH, MRCI_WORK_DIR, MRCI_PRIV_KEY and
  MRCI_PUB_KEY are no longer in use. SSL/TLS cert paths can
  be modified in the conf file.

- Removed email template cmds set_email_template and
  preview_email.

- Also removed cmds close_host, host_config and
  restart_host. The actions these commands could do is best
  left to the host system command line.

- The database class will now explicitly check for write
  permissions to the database and throw an appropriate
  error message if the check fails. "DROP TABLE" SQL
  abilities were added to make this happen.

- Removed async cmds exit(3), maxses(5) and restart(11).
This commit is contained in:
Maurice O'Neal 2020-11-10 14:47:00 -05:00
parent 4fdbfe1c2f
commit c8f53d1e5c
33 changed files with 727 additions and 1156 deletions

View File

@ -71,7 +71,6 @@ SOURCES += src/main.cpp \
src/commands/mods.cpp \
src/commands/info.cpp \
src/commands/cast.cpp \
src/commands/admin.cpp \
src/commands/auth.cpp \
src/commands/acct_recovery.cpp \
src/commands/table_viewer.cpp \
@ -97,7 +96,6 @@ HEADERS += \
src/commands/mods.h \
src/commands/info.h \
src/commands/cast.h \
src/commands/admin.h \
src/commands/auth.h \
src/commands/acct_recovery.h \
src/commands/table_viewer.h \

View File

@ -38,13 +38,13 @@ def get_qt_from_cli():
return ""
def get_db_header():
def get_info_header():
current_dir = os.path.dirname(__file__)
if current_dir == "":
return "src" + os.sep + "db.h"
return "src" + os.sep + "common.h"
else:
return current_dir + os.sep + "src" + os.sep + "db.h"
return current_dir + os.sep + "src" + os.sep + "common.h"
def get_nearest_subdir(path, sub_name):
dir_list = os.listdir(path)
@ -200,7 +200,7 @@ def complete(app_ver, app_target):
print("You can now run the install.py script to install onto this machine or create an installer.")
def main():
with open(get_db_header()) as file:
with open(get_info_header()) as file:
text = file.read()
app_target = get_app_target(text)

View File

@ -9,7 +9,6 @@
<file>docs/intern_commands/add_sub_ch.md</file>
<file>docs/intern_commands/auth.md</file>
<file>docs/intern_commands/cast.md</file>
<file>docs/intern_commands/close_host.md</file>
<file>docs/intern_commands/close_sub_ch.md</file>
<file>docs/intern_commands/decline_ch.md</file>
<file>docs/intern_commands/find_ch.md</file>
@ -24,7 +23,6 @@
<file>docs/intern_commands/fs_move.md</file>
<file>docs/intern_commands/fs_tree.md</file>
<file>docs/intern_commands/fs_upload.md</file>
<file>docs/intern_commands/host_config.md</file>
<file>docs/intern_commands/host_info.md</file>
<file>docs/intern_commands/invite_to_ch.md</file>
<file>docs/intern_commands/is_email_verified.md</file>
@ -47,7 +45,6 @@
<file>docs/intern_commands/p2p_open.md</file>
<file>docs/intern_commands/p2p_request.md</file>
<file>docs/intern_commands/ping_peers.md</file>
<file>docs/intern_commands/preview_email.md</file>
<file>docs/intern_commands/recover_acct.md</file>
<file>docs/intern_commands/remove_ch_member.md</file>
<file>docs/intern_commands/rename_ch.md</file>
@ -55,7 +52,6 @@
<file>docs/intern_commands/request_new_pw.md</file>
<file>docs/intern_commands/request_new_user_name.md</file>
<file>docs/intern_commands/request_pw_reset.md</file>
<file>docs/intern_commands/restart_host.md</file>
<file>docs/intern_commands/rm_acct.md</file>
<file>docs/intern_commands/rm_ch.md</file>
<file>docs/intern_commands/rm_mod.md</file>
@ -64,7 +60,6 @@
<file>docs/intern_commands/rm_sub_ch.md</file>
<file>docs/intern_commands/set_active_flag.md</file>
<file>docs/intern_commands/set_disp_name.md</file>
<file>docs/intern_commands/set_email_template.md</file>
<file>docs/intern_commands/set_email.md</file>
<file>docs/intern_commands/set_member_level.md</file>
<file>docs/intern_commands/set_pw.md</file>

View File

@ -1,6 +1,6 @@
# MRCI #
(Modular Remote Command Interpreter) is a command interpreter primarily designed to provide any type of remote service to connected clients. As the name implies, it is expandable via 3rd party modules by adding addtional commands that remote clients can run on the host. It has a fully feasured user account management system with access control to certain commands for certain users. All persistent data is handled by a SQLite database and all remote connections are handled via TCP and encrypted in SSL/TLS.
(Modular Remote Command Interpreter) is a command interpreter primarily designed to provide any type of remote service to connected clients. As the name implies, it is expandable via 3rd party modules by adding addtional commands that remote clients can run on the host. It has a fully feasured user account management system with access control to certain commands for certain users.
### Usage ###
@ -12,18 +12,17 @@ Usage: mrci <argument>
-help : display usage information about this application.
-stop : stop the current host instance if one is currently running.
-about : display versioning/warranty information about this application.
-addr : set the listening address and port for TCP clients.
-status : display status information about the host instance if it is currently running.
-reset_root : reset the root account password to the default password.
-host : start a new host instance. (this blocks)
-host_trig : start a new host instance. (this does not block)
-default_pw : show the default password.
-public_cmds : run the internal module to list it's public commands. for internal use only.
-exempt_cmds : run the internal module to list it's rank exempt commands. for internal use only.
-user_cmds : run the internal module to list it's user commands. for internal use only.
-run_cmd : run an internal module command. for internal use only.
-ls_sql_drvs : list all available SQL drivers that the host currently supports.
-load_ssl : re-load the host SSL certificate without stopping the host instance.
-elevate : elevate any user account to rank 1.
-add_admin : create a rank 1 account with a randomized password.
Internal module | -public_cmds, -user_cmds, -exempt_cmds, -run_cmd |:
@ -33,17 +32,20 @@ Internal module | -public_cmds, -user_cmds, -exempt_cmds, -run_cmd |:
Details:
addr - this argument takes a {ip_address:port} string. it will return an error if not formatted correctly
examples: 10.102.9.2:35516 or 0.0.0.0:35516.
add_admin - this argument takes a single string representing a user name to create a rank 1 account with.
the host will set a randomized password for it and display it on the CLI. this user will be
required to change the password upon logging in.
example: -add_admin somebody
elevate - this argument takes a single string representing a user name to an account to promote to rank 1.
example: -elevate somebody
run_cmd - this argument is used by the host itself, along side the internal module arguments below to run
the internal command names passed by it. this is not ment to be run directly by human input.
the executable will auto close if it fails to connect to the pipe and/or shared memory segments
```
The host can only be managed via a connected client that supports text input/output so the host application is always listening for clients while running entirely in the background. By default the host listen for clients on address 0.0.0.0 and port 35516, effectively making it reachable on any network interface of the host platform via that specific port.
Any one user account registered with the host can be given root privileges which basically gives this user unrestricted access to anything in the host for administrative purposes. When a host instance is created for the first time, it will create a new user account called 'root' with a randomized default password. To find out what the default password is, run -default_pw. When logging in for the fist time, the host will require you to change the user name and password before continuing.
The host can be managed via a connected client that supports text input/output so the host application is always listening for clients while running entirely in the background. By default the host listen for clients on address 0.0.0.0 and port 35516, effectively making it reachable on any network interface of the host platform via that specific port.
### More Than Just a Command Interpreter ###
@ -101,7 +103,7 @@ while running the install script, it will ask you to input 1 of 3 options:
***local machine*** - This option will install the built application onto the local machine without creating an installer.
***create installer*** - This option creates an installer that can be distributed to other machines to installation. The resulting installer is just a regular .py script file that the target machine can run if it has Python3 insalled. Only Python3 needs to be installed and an internet connection is not required.
***create installer*** - This option creates an installer that can be distributed to other machines for installation. The resulting installer is just a regular .py script file that the target machine can run if it has Python3 insalled. Only Python3 needs to be installed and an internet connection is not required.
***exit*** - Cancel the installation.

View File

@ -32,15 +32,12 @@ enum AsyncCommands : quint16
{
ASYNC_RDY = 1, // client | retricted
ASYNC_SYS_MSG = 2, // client | retricted
ASYNC_EXIT = 3, // internal | private
ASYNC_CAST = 4, // client | public
ASYNC_MAXSES = 5, // internal | private
ASYNC_LOGOUT = 6, // internal | private
ASYNC_USER_DELETED = 7, // client | public
ASYNC_DISP_RENAMED = 8, // internal | public
ASYNC_USER_RANK_CHANGED = 9, // internal | public
ASYNC_CMD_RANKS_CHANGED = 10, // internal | public
ASYNC_RESTART = 11, // internal | private
ASYNC_ENABLE_MOD = 12, // internal | public
ASYNC_DISABLE_MOD = 13, // internal | public
ASYNC_END_SESSION = 14, // internal | private
@ -84,10 +81,7 @@ enum AsyncCommands : quint16
This command signals to the client that your current session is now ready to start running commands. This is sent to the client after successfully setting up the tcp connection ([protocol](protocol.md)). It can carry [TEXT](type_ids.md) data that can be displayed directly to the user if needed.
```ASYNC_SYS_MSG (2)```
This command carry [TEXT](type_ids.md) or [ERR](type_ids.md) data that are system messages that can be directly displayed to the user of needed. It is also used to carry [HOST_CERT](type_ids.md) data during the tcp connection setup and MY_INFO when local user account information has changed.
```ASYNC_EXIT (3)```
This is an internal async command that doesn't carry any data. It is used to send a ```closeServer()``` signal to the TCPServer object in the main process. This will cause it stop listing for clients, close all sessions and then close the main process.
This command carry [TEXT](type_ids.md) or [ERR](type_ids.md) data that are system messages that can be directly displayed to the user of needed. It is also used to carry MY_INFO data when local user account information is changed.
```ASYNC_CAST (4)```
This is an internal only command that carries a 54byte open sub-channels list described in section 5.3 and an embedded frame that can then be sent to clients that have any of the matching open sub-channels. It drops that sub-channel list before arriving at the client so it will apppear like a regular [mrci frame](protocol.md) of any data type.
@ -96,9 +90,6 @@ from_module: [54bytes(sub_channel_list)][1byte(type_id)][rest-of-bytes(pay_load)
to_client: [type_id][cmd_id(4)][branch_id(0)][size_of_payload][payload]
```
```ASYNC_MAXSES (5)```
Internal only async command can used by modules to send a 4byte unsigned 32bit int to the session object to change the maximum amount the concurrent sessions for the TCPServer object.
```ASYNC_LOGOUT (6)```
This internal only async command doesn't carry any data. This can be used by modules to tell the session object to logout the current user.
@ -106,7 +97,7 @@ This internal only async command doesn't carry any data. This can be used by mod
This command carries a 32byte user id hash of the user account that was delete from the host database. All sessions that are currently logged into this account will force logout.
```ASYNC_DISP_RENAMED (8)```
This command carries a combination of the 32byte user id hash and the 64byte new display name (UTF-16LE, padded with 0x00) of the user account that changed it's display name. This will trigger all sessions that are currently logged into this account to send an updated [MY_INFO](type_ids.md) frame via ASYNC_SYS_MSG to the clients.
This command carries a combination of the 32byte user id hash and the 64byte new display name (UTF-8, padded with 0x00) of the user account that changed it's display name. This will trigger all sessions that are currently logged into this account to send an updated [MY_INFO](type_ids.md) frame via ASYNC_SYS_MSG to the clients.
```
from_module: [32bytes(user_id)][64bytes(new_disp_name)]
to_client: [type_id(9)][cmd_id(2)][branch_id(0)][size_of_payload][payload(MY_INFO)]
@ -122,9 +113,6 @@ to_client: [type_id(9)][cmd_id(2)][branch_id(0)][size_of_payload][payload(MY_I
```ASYNC_CMD_RANKS_CHANGED (10)```
This internal only command doesn't carry any data, it just triggers all sessions to re-load runable commands.
```ASYNC_RESTART (11)```
This internal only async commmand doesn't carry any data. It is used to send a ```resServer()``` signal to the TCPServer object in the main process. This will cause it stop listing for clients, close all sessions, reload the host settings and start listening for clients again.
```ASYNC_ENABLE_MOD (12)```
This internal only async commmand that carry a [TEXT](type_ids.md) path to a module executable. All session objects that receive this will then attempt to load the module.
@ -182,7 +170,7 @@ to_client: [type_id(26)][cmd_id(22)][branch_id(0)][size_of_payload][payload(CH_I
```
```ASYNC_RENAME_CH (23)```
This async command carries a combination of the channel id and a 16bit null terminated UTF-16LE string containing the new name of the channel that has been renamed. This command desn't use any of the standard frame formats so it sends a [BYTES](type_ids.md) frame to the client.
This async command carries a combination of the channel id and a null terminated UTF-8 string containing the new name of the channel that has been renamed. This command desn't use any of the standard frame formats so it sends a [BYTES](type_ids.md) frame to the client.
```
to_client: [type_id(14)][cmd_id(28)][branch_id(0)][size_of_payload][payload(channel_id + new_channel_name)]
```
@ -194,7 +182,7 @@ format: [8bytes(64bit_ch_id)][1byte(8bit_sub_ch_id)]
```
```ASYNC_NEW_SUB_CH (25)```
This async command carries a comination of the channel id, sub-channel id, access level and a 16bit null terminated UTF-16LE string containing the sub-channel name when a new sub-channel is created. All sessions that are logged in as a member of the channel forwards the data to the clients as a [BYTES](type_ids.md) frame.
This async command carries a comination of the channel id, sub-channel id, access level and a null terminated UTF-8 string containing the sub-channel name when a new sub-channel is created. All sessions that are logged in as a member of the channel forwards the data to the clients as a [BYTES](type_ids.md) frame.
```
to_client: [type_id(14)][cmd_id(25)][branch_id(0)][8bytes(64bit_ch_id)][1byte(8bit_sub_ch_id)]
[1byte(8bit_access_level)][16bit_null_term_sub-channel_name]
@ -207,7 +195,7 @@ to_client: [type_id(14)][cmd_id(26)][branch_id(0)][8bytes(64bit_ch_id)][1byte(8b
```
```ASYNC_RENAME_SUB_CH (27)```
This async command carries a combination of the channel id, sub-channel id, access level and a 16bit null terminated UTF-16LE string containing the new sub-channel name. All sessions that are logged in as a member of the channel forwards the data to the clients as a [BYTES](type_ids.md) frame.
This async command carries a combination of the channel id, sub-channel id, access level and a null terminated UTF-8 string containing the new sub-channel name. All sessions that are logged in as a member of the channel forwards the data to the clients as a [BYTES](type_ids.md) frame.
```
to_client: [type_id(14)][cmd_id(27)][branch_id(0)][8bytes(64bit_ch_id)][1byte(8bit_sub_ch_id)]
[16bit_null_term_sub-channel_name]
@ -303,7 +291,7 @@ This async command doesn't carry any data. Any module command that sends it tell
### 5.3 Open Sub-Channel List ###
An open sub-channel list is a binary data structure that string togeather up to 6 channel-sub combinations that indicate which channel id and sub id combinations are currently open. Each sub-channel are 9bytes long and the list itself maintians a fixed length of 54bytes so it is padded with 0x00 chars to maintain the fixed length (this padding can appear anywhere in 9byte increments within the list). Each sub-channel is formatted like this:
An open sub-channel list is a binary data structure that string togeather up to 6 sub-channel combinations that indicate which channel id and sub id combinations are currently open. Each sub-channel are 9bytes long and the list itself maintians a fixed length of 54bytes so it is padded with 0x00 chars to maintain the fixed length (this padding can appear anywhere in 9byte increments within the list). Each sub-channel is formatted like this:
```
bytes[0-7] - 64bit LE unsigned int (channel id)

View File

@ -4,9 +4,9 @@ Other than transporting data to be processed by modules, the host have a few oth
### 4.2 Host Ranks ###
Each user registered in the host must be assigned a host rank. The lower it's numeric value, the higher the level of access the user has in the host with 0 being invalid. When initialized, the host defines a ```root``` user with a host rank of 1; giving it the highest level of access possible in the host. The host also defines a default initial host rank of 2 for all new accounts that are created in the host. This can be re-configured at any time using the [host_config](intern_commands.md) internal command.
Each user registered in the host must be assigned a host rank. The lower it's numeric value, the higher the level of access the user has in the host with 0 being invalid. The host defines a default initial host rank for all new users in the host config file located at /etc/mrci/conf.json if running on a linux based os or %PROGRAMDATA%\mrci\conf.json if running on windows.
Some internal commands have the ability to change the user account information of other accounts. The host will in general does not allow users of a lower level of access to any user information of higher access level. For example: a user of host rank ```1``` can force change another user's email address if that user's rank is ```2``` or higher but the rank ```2``` user can't do the same in reverse.
Some internal commands have the ability to change the user account information of other accounts. The host will in general not allow users of a lower level of access to any user information of higher access level. For example: a user of host rank ```1``` can force change another user's email address if that user's rank is ```2``` or higher but the rank ```2``` user can't do the same in reverse.
Host ranks can also be assigned to the commands themselves via the command names of specific modules. By doing this, the host can be configured to allow users of certain ranks or higher access to running certain commands. For example: if a command named ```this_cmd``` is assigned a host rank of ```6```, all users with a host rank value of ```6``` or lower will have access to running this command. All commands that don't have an assigned rank will be assumed a rank of ```1``` but all commands that define itself as rank exempt can bypass this and allow the user to run it regardless of the user's host rank. Exemptions would also disregard the assigned rank of the command.
@ -71,6 +71,171 @@ public-level(5):
All sub-channels can be configured with it's own "lowest level of access" level that can make it so only certain channel members can open it. For example, the channel owner or admin can set a sub-channel's minimum level to 4 to make it so only channel regular members and above can open the sub-channel or it can be set to 5 to make it so anybody can open/close the sub-channel, affectively making it a public sub-channel.
There can only be one channel owner so if the owner decides change the privilege of any other member in the channel to owner, the current owner will automatically step down to level 2 (admin). Also note that privilege level 5 is reserved for users that are not a member of the channel and cannot be assigned to any user that are currently members of the channel.
There can only be one channel owner so if the owner decides to change the privilege of any other member in the channel to owner, the current owner will automatically step down to level 2 (admin). Also note that privilege level 5 is reserved for users that are not a member of the channel and cannot be assigned to any user that are currently members of the channel.
Sub-channels can also be assigned what is called *read-only* flags. These flags are attached to the sub-channel id and privilege level. It is decoupled from all changes that could occur with the sub-channel so this means the sub-channel can get renamed or even deleted but the read-only flag would still remain. What a read-only flag actual does is make it so certain users of the matching level can listen for broadcast data but cannot send out broadcast data to the sub-channel. So a read-only flag for example can be added to a sub-channel id for privilege 5 to make it so public users can listen to the sub-channel but cannot send out anything to it.
Sub-channels can also be assigned what is called *read-only* flags. These flags are attached to the sub-channel id and privilege level. It is decoupled from all changes that could occur with the sub-channel so this means the sub-channel can get renamed or even deleted but the read-only flag would still remain. What a read-only flag actual does is make it so certain users of the matching level can listen for broadcast data but cannot send out broadcast data to the sub-channel. So a read-only flag for example can be added to a sub-channel id for privilege 5 to make it so public users can listen to the sub-channel but cannot send out anything to it.
### 4.4 Host Config File ###
This application stores important global settings in a single json formatted file located at /etc/mrci/conf.json if running on a Linux based OS or %Programdata%\mrci\conf.json if running on windows. Here is a description of all the settings that are stored in that file and what are considered valid vaules.
```
all_channels_active_update : bool
This option tells the host if all sub-channels should be considered
active or not. otherwise, the active flag can be toggled on/off at the
individual sub-channels. active sub-channels send/receive PEER_INFO or
PEER_STAT frames to all peer clients connected to the sub-channel so
they can be made aware of each other's status and public information.
without the active flag, no such frames are transferred.
auto_lock_limit : int
The autolock threshold is an integer value that determines how many
failed login attempts can be made before the user account is locked
by the host.
db_driver : string
The host can support different types of SQL databases depending on
what QT database drivers are installed in the system. these drivers
are usually found in /opt/mrci/sqldrivers if running on a Linux
based OS or %programfiles%\MRCI\sqldrivers if running on Windows.
you can also run mrci -ls_sql_drvs to get a list of available
drivers. the default is QSQLITE.
db_host_name : string
This value contains the network, internet or file location address
of the database.
db_password : string
This value contains the authentication password to the database if
password protected.
db_user_name : string
This value contains the authentication username to the database if
password protected.
email_verify_subject : string
The host will use this string as the email subject when sending an
email verification email. an email verification basically sends
a numeric code to the target user's email address and awaits input
of the code by the user. if the entered code is correct, that
verifies that the user is the owner of the email address.
email_verify_template : string
Path to a any text file formatted in UTF8 unicode in the local host
file system. the text contained in this file will be used in the
actual email body when sending a email verification email to the
target user's email address. the message body must contain all of
the keywords in section 4.5.
enable_email_verify : bool
This enables/disables automated email confirmations. this tells the
host if it is allowed to load the verify_email command for any user,
regardless of rank.
enable_public_reg : bool
Public registration basically allows un-logged in clients to run the
add_acct command. doing this allows un-registered users to become
registered users without the need to contact an admin.
enable_pw_reset : bool
This enables automated password resets via email so users can reset
their account passwords without the need to contact an admin. this
basically tells the host if it is allowed to load the
request_pw_reset and recover_acct commands or not.
initial_rank : int
The initial host rank is the rank all new user accounts are
registered with when created. the host rank itself is an integer
value that determine what commands each user can or cannot run.
see section 4.2 for more info on host ranks.
listening_addr : string
This is the local address that the host listen on for clients.
the default is 0.0.0.0 which means the host will listen on any
local address connected to the host.
listening_port : int
This is the port that the host will listen on for clients.
mail_client_cmd : string
This is the command line template that the host will use when
sending emails. just like the email templates, this command
also require certain keywords to be present to successfully
construct a working command line. see section 4.6 for these
keywords.
max_sessions : int
Max sessions is an integar value that determines how many
simultaneous clients the host will be allowed to run at once.
max_sub_channels : int
This option sets the maximum amount of sub-channels each channel
can have. this must range between 1 and 255.
reset_pw_mail_subject : string
The host will use this string as the email subject when sending a
password reset email. the email body will contain a temp password
that the user will need to enter when running the recover_acct
command and in turn be able to change the account password.
reset_pw_mail_template : string
Path to a any text file formatted in UTF8 unicode in the local host
file system. the text contained in this file will be used in the
actual email body when sending a password reset email to the target
user's email address. the message body must contain all of the
keywords in section 4.5.
tls_cert_chain : string
Path to the SSL/TLS cert file used for secure TCP connections. more
than one cert file can be defined to form a complete chain by colon
seperating multiple file paths.
tls_priv_key : string
Path to the SSL/TLS private key used for secure TCP connections.
```
### 4.5 Email Template Keywords ###
The host will replace the following keywords in the email template with actual values/text when contructing the email.
```
%user_name% - the registered username of the target.
%date% - the date and time stamp of the email being sent.
%otp% - standing for "one time password" this keyword is
the temporary password or verification code that
is sent to the target's email.
```
### 4.6 Email Command Line Template Keywords ###
The host will replace the following keywords in the command line template with actual values/text before running it.
```
%message_body% - the fully built message body text from an email
template.
%subject% - the email subject text.
%target_email% - target email address that the email will be
sent to.
```

View File

@ -22,8 +22,6 @@ The host is extendable via 3rd party modules but the host itself is an internal
* [ch_owner_override](intern_commands/ch_owner_override.md) - set/unset the channel owner override flag for your current session.
* [close_host](intern_commands/close_host.md) - close the host instance.
* [close_sub_ch](intern_commands/close_sub_ch.md) - close a sub-channel for your current session.
* [decline_ch](intern_commands/decline_ch.md) - decline an invite to a channel.
@ -52,8 +50,6 @@ The host is extendable via 3rd party modules but the host itself is an internal
* [fs_upload](intern_commands/fs_upload.md) - upload a single file to the host.
* [host_config](intern_commands/host_config.md) - view/change various host settings.
* [host_info](intern_commands/host_info.md) - display system information about the host.
* [invite_to_ch](intern_commands/invite_to_ch.md) - invite a host user to join a channel.
@ -98,8 +94,6 @@ The host is extendable via 3rd party modules but the host itself is an internal
* [ping_peers](intern_commands/ping_peers.md) - ping all peer sessions with any matching sub-channels to return information about themselves to you.
* [preview_email](intern_commands/preview_email.md) - preview the confirmation or password reset emails with dummy values.
* [recover_acct](intern_commands/recover_acct.md) - login to a user account using a temporary password.
* [remove_ch_member](intern_commands/remove_ch_member.md) - remove a user as a member of a channel you currently a member of or cancel an invite.
@ -114,8 +108,6 @@ The host is extendable via 3rd party modules but the host itself is an internal
* [request_pw_reset](intern_commands/request_pw_reset.md) - request a password reset for a user account.
* [restart_host](intern_commands/restart_host.md) - re-start the host instance.
* [rm_acct](intern_commands/rm_acct.md) - delete a user account from the host database.
* [rm_ch](intern_commands/rm_ch.md) - permanently remove a channel and all of it's sub-shannels from the host.
@ -134,8 +126,6 @@ The host is extendable via 3rd party modules but the host itself is an internal
* [set_email](intern_commands/set_email.md) - set the user account email address.
* [set_email_template](intern_commands/set_email_template.md) - set the email template used by the host to send emails for user account resets and confirmations.
* [set_member_level](intern_commands/set_member_level.md) - set the user privilege levels of a channel member.
* [set_pw](intern_commands/set_pw.md) - update your account password.

View File

@ -27,14 +27,14 @@ A full description of the type id's can be found in the [Type_IDs.md](type_ids.m
### 2.3 Module Command Line Options ###
```
The host will call the module with just one of these options:
The host will call the module with just one of these parameters:
-public_cmds : send a NEW_CMD frame for all public commands the module can run.
-exempt_cmds : send a NEW_CMD frame all rank exempt commands the module can run.
-user_cmds : send a NEW_CMD frame for all user rank enforced commands the module can run.
-run_cmd {command_name} : run a module command based on the command name.
The host will include all 3 of these options with the above option:
The host will include all 3 of these parameters with the above option:
-pipe {pipe_name/path} : the named pipe used to establish a data connection with the session.
-mem_ses {key_name} : the shared memory key for the session object.
@ -45,12 +45,12 @@ notes:
* When the session calls the module in list mode (-public_cmds, -exempt_cmds or -user_cmds), it will only respond to frame type ids: [NEW_CMD](type_ids.md) or [ERR](type_ids.md) from the module; all other data types are ignored. Modules called in run mode (-run_cmd) on the other hand will open up all frame type ids.
* When the session detects that the module successfully established a pipe connection, it will send a [HOST_VER](type_ids.md) frame to the module so the module can decide if it supports the host. If the host is not compatible or the module fails for what ever other reason, the module can send a useful error message [ERR](type_ids.md) and then terminate gracefully. The error message will be added to the host debug log where it can be used by host admins to find out what went wrong. The HOST_VER frame is sent only when the module is called with the -public_cmds, -exempt_cmds or -user_cmds options.
* When the session detects that the module successfully established a pipe connection, it will send a [HOST_VER](type_ids.md) frame to the module so the module can decide if it supports the host. If the host is not compatible or the module fails for what ever other reason, the module can send a useful error message [ERR](type_ids.md) and then terminate gracefully. The error message will be added to the host debug log where it can be used by host admins to find out what went wrong. The HOST_VER frame is sent only when the module is called with the -public_cmds, -exempt_cmds or -user_cmds parameter.
* When the module sends a [NEW_CMD](type_ids.md) frame, the 16bit command id is needed but does not need to be valid, it just needs to be there as a place holder. The session will auto fill a valid command id before sending the data to the client. A valid NEW_CMD frame must have a minimum of 259 bytes and a valid command name. the session will ignore all NEW_CMD frames the doesn't meet these requirements. See section [6.3](shared_data.md) for what would be considered a valid command name.
* The session will call all modules with the -public_cmds when created for the first time or when the user logout so it doesn't matter if the command names returned to the session overlap with -exempt_cmd or -user_cmds. When a user is logged in, it will then call 2 instances of each module with the -exempt_cmds and -user_cmds options so the command names should not overlap when these options are active.
* When a session starts, it will call all modules with the -public_cmds and it doesn't matter if the command names returned to the session overlap with -exempt_cmd or -user_cmds. When a user is logged in, it will then call 2 instances of each module with the -exempt_cmds and -user_cmds parameters so the command names should not overlap when these parameters are active.
* Modules called with -run_cmd does not need to terminate after running the requested command, instead it must send an [IDLE](type_ids.md) frame to indicate that the command is finished when it eventually does finish. This is desired because not only it tells the client that the command is finished but it also makes it so the session doesn't need to recreate the module process on every subsequent call to the module.
* Modules called with -run_cmd does not need to terminate after running the requested command, instead it must send an [IDLE](type_ids.md) frame to indicate that the command is finished when it eventually does finish. This is desired because not only it tells the client that the command is finished but it also makes it so the session doesn't need to recreate the module process on every subsequent call to the command.
* The session will send a [KILL_CMD](type_ids.md) to the module after 2 mins of being idle (no IPC/Pipe activity). The module will have 3 seconds to do this before it is force killed. This will also happen when the user ends the session and the module process needs to terminate. Modules must still send an [IDLE](type_ids.md) frame to indicate the command is finished if a command was running.

View File

@ -1,6 +1,6 @@
### 1.1 The Protocol ###
The main goal of this application is to transport data from remote TCP clients to the [Modules](modules.md) defined in the host. How the data is processed and/or returned back to the client depends entirely on the type of data being transported or the module itself. The data format that the host understands for data transport is referred to as MRCI frames described below in section 1.2.
The main goal of this application is to transport data from remote TCP clients to the [Modules](modules.md) defined in the host. How the data is processed and/or returned back to the client depends entirely on the module itself. The data format that the host understands for data transport is referred to as MRCI frames described below in section 1.2.
Before any MRCI frames can be transported, both the host and client need basic information about each other. This is done by having the client send a fixed length client header when it successfully connects to the host and the host will reply with it's own fixed length host header. Descriptions of these headers can be found in sections 1.4 and 1.5.
@ -20,7 +20,7 @@ notes:
* A full description of the type id's can be found in the [Type_IDs.md](type_ids.md) document.
* Modules call commands via a command name but the host will assign a unique command id to all command names so clients can call them using a simple 2 byte integer instead of full text. The command ids can change as the modules change so it is recommended for clients to not depend on consistant command ids but depend on the [ASYNC_ADD_CMD](async.md) and [ASYNC_RM_CMD](async.md) async commands.
* Modules call commands via a command name but the host will assign a unique command id to all command names so clients can call them using a simple 2 byte integer instead of full text. The command ids can change as the modules change so it is recommended for clients to not depend on consistant command ids but depend on the [ASYNC_ADD_CMD](async.md) and [ASYNC_RM_CMD](async.md) async commands instead.
* The branch id is an id that can be assigned by the client itself to run muliple instances of the same command. Commands sent by a certain branch id will result in data sent back to the client from the module with that same branch id.
@ -48,7 +48,7 @@ TCP_Rev - this indicate any changes to the MRCI protocol that interface the
Mod_Rev - this indicate any changes to the IPC protocol that interface the
host with the modules via named pipes. any changes to the IPC
frames, NEW_CMD/CMD_ID type ids, etc. will cause this rev to
frames or major changes to async commands will cause this rev to
increment.
```

View File

@ -154,6 +154,9 @@ def local_install(app_target, app_name):
if make_install_dir(install_dir):
if not os.path.exists("/var/opt/" + app_target):
os.makedirs("/var/opt/" + app_target)
if not os.path.exists("/etc/" + app_target):
os.makedirs("/etc/" + app_target)
template_to_deploy("app_dir/linux/" + app_target + ".sh", install_dir + "/" + app_target + ".sh", install_dir, app_name, app_target)
template_to_deploy("app_dir/linux/uninstall.sh", install_dir + "/uninstall.sh", install_dir, app_name, app_target)
@ -168,7 +171,8 @@ def local_install(app_target, app_name):
subprocess.run(["useradd", "-r", app_target])
subprocess.run(["chmod", "-R", "755", install_dir])
subprocess.run(["chmod", "755", "/etc/systemd/system/" + app_target + ".service"])
subprocess.run(["chown", "-R", app_target + ":" + app_target, "/var/opt/" + app_target])
subprocess.run(["chown", "-R", app_target + ":" + app_target, "/var/opt/" + app_target])
subprocess.run(["chown", "-R", app_target + ":" + app_target, "/etc/" + app_target])
subprocess.run(["systemctl", "start", app_target])
subprocess.run(["systemctl", "enable", app_target])
@ -247,7 +251,7 @@ def make_install(app_ver, app_name):
print("adding file: " + file)
zip_file.write(file)
sub_copy_file(__file__, path, "main(is_sfx=False)", "main(is_sfx=True)\n\n\n", 7700)
sub_copy_file(__file__, path, "main(is_sfx=False)", "main(is_sfx=True)\n\n\n", 11000)
with open(path, "a") as dst_file, open("app_dir.zip", "rb") as src_file:
print("Packing the compressed app_dir into the sfx script file --")

View File

@ -18,6 +18,7 @@
// <http://www.gnu.org/licenses/>.
#include "common.h"
#include "db.h"
class IPCWorker : public QObject
{

View File

@ -508,13 +508,6 @@ bool CmdProcess::validAsync(quint16 async, const QByteArray &data, QTextStream &
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))
@ -624,9 +617,8 @@ bool CmdProcess::validAsync(quint16 async, const QByteArray &data, QTextStream &
void CmdProcess::asyncDirector(quint16 id, const QByteArray &payload)
{
if ((id == ASYNC_EXIT) || (id == ASYNC_MAXSES) || (id == ASYNC_LOGOUT) || (id == ASYNC_RESTART) ||
(id == ASYNC_END_SESSION) || (id == ASYNC_USER_LOGIN) || (id == ASYNC_OPEN_SUBCH) || (id == ASYNC_CLOSE_SUBCH) ||
(id == ASYNC_KEEP_ALIVE) || (id == ASYNC_SET_DIR) || (id == ASYNC_DEBUG_TEXT))
if ((id == ASYNC_KEEP_ALIVE) || (id == ASYNC_DEBUG_TEXT) || (id == ASYNC_LOGOUT) || (id == ASYNC_SET_DIR) ||
(id == ASYNC_END_SESSION) || (id == ASYNC_USER_LOGIN) || (id == ASYNC_OPEN_SUBCH) || (id == ASYNC_CLOSE_SUBCH))
{
emit privIPC(id, payload);
}

View File

@ -18,6 +18,7 @@
// <http://www.gnu.org/licenses/>.
#include "common.h"
#include "db.h"
class ModProcess : public QProcess
{

View File

@ -20,15 +20,11 @@ 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";}
QString VerifyEmail::cmdName() {return "verify_email";}
QString IsEmailVerified::cmdName() {return "is_email_verified";}
QString SetEmailTemplate::cmdName() {return "set_email_template";}
QString PreviewEmail::cmdName() {return "preview_email";}
void delRecoverPw(const QByteArray &uId)
{
@ -84,11 +80,8 @@ void RecoverAcct::addToThreshold()
db.addColumn(COLUMN_ACCEPTED, false);
db.exec();
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_LOCK_LIMIT);
db.exec();
auto maxAttempts = db.getData(COLUMN_LOCK_LIMIT).toUInt();
auto confObj = confObject();
auto maxAttempts = confObj[CONF_AUTO_LOCK_LIM].toInt();
db.setType(Query::PULL, TABLE_AUTH_LOG);
db.addColumn(COLUMN_IPADDR);
@ -98,7 +91,7 @@ void RecoverAcct::addToThreshold()
db.addCondition(COLUMN_ACCEPTED, false);
db.exec();
if (static_cast<quint32>(db.rows()) > maxAttempts)
if (db.rows() > maxAttempts)
{
delRecoverPw(uId);
@ -206,11 +199,17 @@ void ResetPwRequest::procIn(const QByteArray &binIn, uchar dType)
{
if (dType == TEXT)
{
auto args = parseArgs(binIn, 2);
auto email = getParam("-email", args);
auto name = getParam("-user", args);
auto confObj = confObject();
auto cmdLine = confObj[CONF_MAIL_CLIENT_CMD].toString();
auto subject = confObj[CONF_PW_RES_EMAIL_SUBJECT].toString();
auto msgFile = confObj[CONF_PW_RES_EMAIL_TEMP].toString();
auto args = parseArgs(binIn, 2);
auto email = getParam("-email", args);
auto name = getParam("-user", args);
QByteArray uId;
QString body;
QString err;
if (!email.isEmpty() && validEmailAddr(email))
{
@ -219,7 +218,11 @@ void ResetPwRequest::procIn(const QByteArray &binIn, uchar dType)
retCode = INVALID_PARAMS;
if (name.isEmpty() || !validUserName(name))
if (!getEmailParams(cmdLine, msgFile, &body, &err))
{
errTxt(err);
}
else if (name.isEmpty() || !validUserName(name))
{
errTxt("err: The -user or -email argument is empty, not found or invalid.\n");
}
@ -231,7 +234,9 @@ void ResetPwRequest::procIn(const QByteArray &binIn, uchar dType)
{
retCode = NO_ERRORS;
auto pw = genPw();
auto pw = genPw();
auto date = QDateTime::currentDateTimeUtc().toString("YYYY-MM-DD HH:MM:SS");
auto mailArgs = parseArgs(cmdLine.toUtf8(), -1);
if (recoverPWExists(uId))
{
@ -242,36 +247,23 @@ void ResetPwRequest::procIn(const QByteArray &binIn, uchar dType)
createTempPw(uId, pw);
}
Query db(this);
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_TEMP_PW_SUBJECT);
db.addColumn(COLUMN_TEMP_PW_MSG);
db.addColumn(COLUMN_MAILERBIN);
db.addColumn(COLUMN_MAIL_SEND);
db.exec();
auto date = QDateTime::currentDateTimeUtc().toString("YYYY-MM-DD HH:MM:SS");
auto subject = db.getData(COLUMN_TEMP_PW_SUBJECT).toString();
auto body = db.getData(COLUMN_TEMP_PW_MSG).toString();
auto app = db.getData(COLUMN_MAILERBIN).toString();
auto cmdLine = db.getData(COLUMN_MAIL_SEND).toString();
body.replace(DATE_SUB, date);
body.replace(USERNAME_SUB, name);
body.replace(TEMP_PW_SUB, pw);
body.replace(OTP_SUB, pw);
cmdLine.replace(TARGET_EMAIL_SUB, email);
cmdLine.replace(SUBJECT_SUB, "'" + escapeChars(subject, '\\', '\'') + "'");
cmdLine.replace(MSG_SUB, "'" + escapeChars(body, '\\', '\'') + "'");
if (QProcess::startDetached(expandEnvVariables(app), parseArgs(cmdLine.toUtf8(), -1)))
if (QProcess::startDetached(expandEnvVariables(mailArgs[0]), mailArgs.mid(1)))
{
mainTxt("A temporary password was sent to the email address associated with the account. this password will expire in 1hour.\n");
mainTxt("A temporary password was sent to the email address associated with the account. this password will expire in 1 hour.\n");
}
else
{
errTxt("err: The host email system has reported an internal error, try again later.\n");
retCode = EXECUTION_FAIL;
errTxt("err: The host email system has reported an internal error, please try again later.\n");
}
}
}
@ -311,50 +303,53 @@ void VerifyEmail::procIn(const QByteArray &binIn, quint8 dType)
{
uId = rdFromBlock(userId, BLKSIZE_USER_ID);
auto email = getEmailForUser(uId);
auto confObj = confObject();
auto cmdLine = confObj[CONF_MAIL_CLIENT_CMD].toString();
auto subject = confObj[CONF_EVERIFY_SUBJECT].toString();
auto msgFile = confObj[CONF_EVERIFY_TEMP].toString();
auto email = getEmailForUser(uId);
QString body;
QString err;
if (email.isEmpty())
{
retCode = INVALID_PARAMS;
errTxt("err: Your account currently has no email address, please update it.\n");
errTxt("err: Your account currently has no email address, please contact an admin to update it.\n");
}
else if (!getEmailParams(cmdLine, msgFile, &body, &err))
{
retCode = INVALID_PARAMS;
errTxt(err);
}
else
{
flags |= MORE_INPUT;
code = QString::number(QRandomGenerator::global()->bounded(100000, 999999));
Query db(this);
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_CONFIRM_SUBJECT);
db.addColumn(COLUMN_CONFIRM_MSG);
db.addColumn(COLUMN_MAILERBIN);
db.addColumn(COLUMN_MAIL_SEND);
db.exec();
auto uName = rdStringFromBlock(userName, BLKSIZE_USER_NAME);
auto date = QDateTime::currentDateTimeUtc().toString("yyyy-MM-dd HH:mm:ss");
auto subject = db.getData(COLUMN_CONFIRM_SUBJECT).toString();
auto body = db.getData(COLUMN_CONFIRM_MSG).toString();
auto app = db.getData(COLUMN_MAILERBIN).toString();
auto cmdLine = db.getData(COLUMN_MAIL_SEND).toString();
auto uName = rdStringFromBlock(userName, BLKSIZE_USER_NAME);
auto date = QDateTime::currentDateTimeUtc().toString("yyyy-MM-dd HH:mm:ss");
auto mailArgs = parseArgs(cmdLine.toUtf8(), -1);
body.replace(DATE_SUB, date);
body.replace(USERNAME_SUB, uName);
body.replace(CONFIRM_CODE_SUB, code);
body.replace(OTP_SUB, code);
cmdLine.replace(TARGET_EMAIL_SUB, email);
cmdLine.replace(SUBJECT_SUB, "'" + escapeChars(subject, '\\', '\'') + "'");
cmdLine.replace(MSG_SUB, "'" + escapeChars(body, '\\', '\'') + "'");
if (QProcess::startDetached(expandEnvVariables(app), parseArgs(cmdLine.toUtf8(), -1)))
if (QProcess::startDetached(expandEnvVariables(mailArgs[0]), mailArgs.mid(1)))
{
privTxt("A confirmation code was sent to your email address: " + email + "\n\n" + "Please enter that code now or leave blank to cancel: ");
}
else
{
errTxt("err: The host email system has reported an internal error, try again later.\n");
retCode = EXECUTION_FAIL;
errTxt("err: The host email system has reported an internal error, please try again later.\n");
}
}
}
@ -378,252 +373,3 @@ void IsEmailVerified::procIn(const QByteArray &binIn, quint8 dType)
mainTxt(boolStr(db.getData(COLUMN_EMAIL_VERIFIED).toBool()) + "\n");
}
}
void SetEmailTemplate::procIn(const QByteArray &binIn, quint8 dType)
{
if ((flags & MORE_INPUT) && (dType == GEN_FILE))
{
bodyText.append(QString::fromUtf8(binIn));
dataSent += binIn.size();
mainTxt(QString::number(dataSent) + "/" + len + "\n");
if (dataSent >= len.toInt())
{
mainTxt("\nUpload complete.\n");
proc();
}
}
else if ((dType == GEN_FILE) || (dType == TEXT))
{
auto args = parseArgs(binIn, 9);
dataSent = 0;
textFromFile = argExists("-client_file", args);
subject = getParam("-subject", args);
bodyText = getParam("-body", args);
len = getParam("-len", args);
if (argExists("-reset_template", args))
{
eType = PW_RESET;
}
else if (argExists("-confirm_template", args))
{
eType = CONFIRM_EMAIL;
}
else
{
eType = NONE;
}
retCode = INVALID_PARAMS;
if (eType == NONE)
{
errTxt("err: Which template do you want to change? -reset_template or -confirm_template not found.\n");
}
else if (textFromFile && !isInt(len))
{
errTxt("err: '" + len + "' given in -len is not a valid integer.\n");
}
else if (textFromFile && (len.toInt() <= 0))
{
errTxt("err: The text file size cannot be 0 or less than 0.\n");
}
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");
}
else
{
retCode = NO_ERRORS;
if (argExists("-subject", args) && subject.isEmpty())
{
if (eType == CONFIRM_EMAIL) subject = DEFAULT_CONFIRM_SUBJECT;
else subject = DEFAULT_TEMP_PW_SUBJECT;
}
if (argExists("-body", args) && bodyText.isEmpty())
{
if (eType == CONFIRM_EMAIL) bodyText = TXT_ConfirmCodeTemplate;
else bodyText = TXT_TempPwTemplate;
}
if (textFromFile)
{
mainTxt("Input hooked...awaiting text file data.\n\n");
bodyText.clear();
flags |= MORE_INPUT;
emit procOut(QByteArray(), GEN_FILE);
emit procOut(QByteArray(), GEN_FILE);
}
else
{
proc();
}
}
}
}
void SetEmailTemplate::proc()
{
if (bodyText.isEmpty() && subject.isEmpty())
{
retCode = INVALID_PARAMS;
errTxt("err: The email body and subject text are empty, nothing will be changed.\n");
}
else
{
Query db(this);
db.setType(Query::UPDATE, TABLE_SERV_SETTINGS);
QString codeSub;
QString bodyColumn;
QString subjectColumn;
auto execQuery = false;
if (eType == PW_RESET)
{
codeSub = TEMP_PW_SUB;
bodyColumn = COLUMN_TEMP_PW_MSG;
subjectColumn = COLUMN_TEMP_PW_SUBJECT;
}
else
{
codeSub = CONFIRM_CODE_SUB;
bodyColumn = COLUMN_CONFIRM_MSG;
subjectColumn = COLUMN_CONFIRM_SUBJECT;
}
if (!bodyText.isEmpty())
{
retCode = INVALID_PARAMS;
if (!bodyText.contains(DATE_SUB, Qt::CaseInsensitive))
{
errTxt("err: The email body does not contain: " + QString(DATE_SUB) + "\n");
}
else if (!bodyText.contains(USERNAME_SUB, Qt::CaseInsensitive))
{
errTxt("err: The email body does not contain: " + QString(USERNAME_SUB) + "\n");
}
else if (!bodyText.contains(codeSub, Qt::CaseInsensitive))
{
errTxt("err: The email body does not contain: " + codeSub + "\n");
}
else if (bodyText.size() > 10000)
{
errTxt("err: The email body is too large. it cannot exceed 10,000 chars.\n");
}
else
{
mainTxt("Email body updated.\n");
db.addColumn(bodyColumn, bodyText);
execQuery = true;
retCode = NO_ERRORS;
}
}
if (!subject.isEmpty())
{
retCode = INVALID_PARAMS;
if (subject.size() > 120)
{
errTxt("err: The subject is too large. it cannot exceed 120 chars.\n");
}
else
{
mainTxt("Email subject updated.\n");
db.addColumn(subjectColumn, subject);
execQuery = true;
retCode = NO_ERRORS;
}
}
if (execQuery) db.exec();
}
flags &= ~MORE_INPUT;
}
void PreviewEmail::procIn(const QByteArray &binIn, quint8 dType)
{
if (dType == TEXT)
{
auto args = parseArgs(binIn, 4);
auto eType = NONE;
if (argExists("-reset_email", args)) eType = PW_RESET;
else if (argExists("-confirm_email", args)) eType = CONFIRM_EMAIL;
if (eType == NONE)
{
retCode = INVALID_PARAMS;
errTxt("err: which template do you want to preview? -reset_email or -confirm_email not found.\n");
}
else
{
QString codeSub;
QString code;
QString bodyColumn;
QString subjectColumn;
auto date = QDateTime::currentDateTimeUtc().toString("yyyy-MM-dd HH:mm:ss");
if (eType == PW_RESET)
{
codeSub = TEMP_PW_SUB;
code = genPw();
bodyColumn = COLUMN_TEMP_PW_MSG;
subjectColumn = COLUMN_TEMP_PW_SUBJECT;
}
else
{
codeSub = CONFIRM_CODE_SUB;
code = QString::number(QRandomGenerator::global()->bounded(100000, 999999));
bodyColumn = COLUMN_CONFIRM_MSG;
subjectColumn = COLUMN_CONFIRM_SUBJECT;
}
Query db(this);
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(bodyColumn);
db.addColumn(subjectColumn);
db.exec();
auto uName = rdStringFromBlock(userName, BLKSIZE_USER_NAME);
auto subject = db.getData(subjectColumn).toString();
auto body = db.getData(bodyColumn).toString();
body.replace(DATE_SUB, date);
body.replace(USERNAME_SUB, uName);
body.replace(codeSub, code);
QString txt;
QTextStream txtOut(&txt);
txtOut << "-----Subject-------" << Qt::endl << Qt::endl;
txtOut << subject << Qt::endl << Qt::endl;
txtOut << "-----Body----------" << Qt::endl << Qt::endl;
mainTxt(txt);
bigTxt(body);
}
}
}

View File

@ -1,4 +1,4 @@
#ifndef ACCT_RECOVERY_H
#ifndef ACCT_RECOVERY_H
#define ACCT_RECOVERY_H
// This file is part of MRCI.
@ -20,13 +20,6 @@
#include "../common.h"
#include "../cmd_object.h"
enum TemplateType
{
PW_RESET,
CONFIRM_EMAIL,
NONE
};
bool expired(const QByteArray &uId);
void delRecoverPw(const QByteArray &uId);
@ -100,45 +93,4 @@ public:
explicit IsEmailVerified(QObject *parent = nullptr);
};
//------------------
class SetEmailTemplate : public CmdObject
{
Q_OBJECT
private:
QString bodyText;
QString subject;
QString len;
int dataSent;
bool textFromFile;
TemplateType eType;
void proc();
public:
static QString cmdName();
void procIn(const QByteArray &binIn, quint8 dType);
explicit SetEmailTemplate(QObject *parent = nullptr);
};
//-----------------
class PreviewEmail : public CmdObject
{
Q_OBJECT
public:
static QString cmdName();
void procIn(const QByteArray &binIn, quint8 dType);
explicit PreviewEmail(QObject *parent = nullptr);
};
#endif // ACCT_RECOVERY_H

View File

@ -47,11 +47,7 @@ void Auth::addToThreshold()
db.addColumn(COLUMN_ACCEPTED, false);
db.exec();
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_LOCK_LIMIT);
db.exec();
auto maxAttempts = db.getData(COLUMN_LOCK_LIMIT).toUInt();
auto maxAttempts = confObject()[CONF_AUTO_LOCK_LIM].toInt();
db.setType(Query::PULL, TABLE_AUTH_LOG);
db.addColumn(COLUMN_IPADDR);
@ -61,7 +57,7 @@ void Auth::addToThreshold()
db.addCondition(COLUMN_ACCEPTED, false);
db.exec();
if (static_cast<quint32>(db.rows()) > maxAttempts)
if (db.rows() > maxAttempts)
{
db.setType(Query::UPDATE, TABLE_USERS);
db.addColumn(COLUMN_LOCKED, true);
@ -86,12 +82,6 @@ void Auth::confirmAuth()
db.addCondition(COLUMN_COUNT, true);
db.addCondition(COLUMN_USER_ID, uId);
db.addCondition(COLUMN_AUTH_ATTEMPT, true);
if (rootUserId() == uId)
{
db.addCondition(COLUMN_IPADDR, ip);
}
db.exec();
db.setType(Query::PUSH, TABLE_AUTH_LOG);
@ -167,11 +157,6 @@ void Auth::procIn(const QByteArray &binIn, quint8 dType)
flags &= ~MORE_INPUT;
retCode = ABORTED;
}
else if (noCaseMatch(DEFAULT_ROOT_USER, text))
{
errTxt("err: '" + QString(DEFAULT_ROOT_USER) + "' is a reserved keyword. invalid for use as a user name.\n");
promptTxt("Enter a new user name (leave blank to cancel): ");
}
else if (validEmailAddr(text))
{
errTxt("err: Invaild user name. it looks like an email address.\n");

View File

@ -486,7 +486,7 @@ void SetActiveState::procIn(const QByteArray &binIn, quint8 dType)
db.addCondition(COLUMN_SUB_CH_ID, subId);
db.exec();
if (globalActiveFlag())
if (confObject()[CONF_ALL_CH_UPDATE].toBool())
{
mainTxt("warning: The host currently have the global active update flag set so setting this flag at the sub-channel level does nothing.\n");
}
@ -541,7 +541,9 @@ void CreateSubCh::procIn(const QByteArray &binIn, quint8 dType)
}
else if (!genSubId(chId, &subId))
{
errTxt("err: This channel has reached the maximum amount sub-channels it can have (" + QString::number(maxSubChannels()) + ").\n");
auto max = confObject()[CONF_MAX_SUBS].toInt();
errTxt("err: This channel has reached the maximum amount sub-channels it can have (" + QString::number(max) + ").\n");
}
else
{

View File

@ -75,10 +75,6 @@ void ListCommands::onIPCConnected()
{
genType = QByteArray(1, GEN_UPLOAD);
}
else if (cmdName == SetEmailTemplate::cmdName())
{
genType = QByteArray(1, GEN_UPLOAD);
}
QByteArray frame;
@ -100,21 +96,16 @@ void HostInfo::procIn(const QByteArray &binIn, quint8 dType)
if (dType == TEXT)
{
Query db(this);
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_IPADDR);
db.addColumn(COLUMN_PORT);
db.addColumn(COLUMN_MAXSESSIONS);
db.exec();
QString txt;
QTextStream txtOut(&txt);
hostSharedMem->lock();
quint32 sesCount = rd32BitFromBlock(hostLoad);
quint32 maxSes = db.getData(COLUMN_MAXSESSIONS).toUInt();
auto confObj = confObject();
auto sesCount = rd32BitFromBlock(hostLoad);
auto maxSes = confObj[CONF_MAX_SESSIONS].toInt();
auto addr = confObj[CONF_LISTEN_ADDR].toString();
auto port = confObj[CONF_LISTEN_PORT].toInt();
hostSharedMem->unlock();
@ -123,8 +114,8 @@ void HostInfo::procIn(const QByteArray &binIn, quint8 dType)
txtOut << "Host Name: " << QSysInfo::machineHostName() << Qt::endl;
txtOut << "Host OS: " << QSysInfo::prettyProductName() << Qt::endl;
txtOut << "Load: " << sesCount << "/" << maxSes << Qt::endl;
txtOut << "Listening Addr: " << db.getData(COLUMN_IPADDR).toString() << Qt::endl;
txtOut << "Listening Port: " << db.getData(COLUMN_PORT).toUInt() << Qt::endl;
txtOut << "Listening Addr: " << addr << Qt::endl;
txtOut << "Listening Port: " << port << Qt::endl;
mainTxt(txt);
}

View File

@ -148,7 +148,7 @@ void CreateUser::procIn(const QByteArray &binIn, quint8 dType)
errTxt(errMsg + "\n");
privTxt("Enter a new password (leave blank to cancel): ");
}
else if (!createUser(newName, email, dispName, password))
else if (!createUser(newName, email, dispName, password, confObject()[CONF_INIT_RANK].toInt()))
{
retCode = INVALID_PARAMS;
@ -181,10 +181,6 @@ void CreateUser::procIn(const QByteArray &binIn, quint8 dType)
{
errTxt("err: Invalid username. it must be 2-24 chars long and contain no spaces.\n");
}
else if (noCaseMatch(DEFAULT_ROOT_USER, newName))
{
errTxt("err: '" + QString(DEFAULT_ROOT_USER) + "' is a reserved keyword. invalid for use as a username.\n");
}
else if (validEmailAddr(newName))
{
errTxt("err: Invaild username. it looks like an email address.\n");
@ -277,10 +273,6 @@ void RemoveUser::procIn(const QByteArray &binIn, quint8 dType)
{
errTxt("err: The requested user name does not exists.\n");
}
else if (rootUserId() == uId)
{
errTxt("err: Unable to delete root user: '" + uName + "'\n");
}
else if (isChOwner(uId))
{
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");
@ -346,11 +338,7 @@ void ChangeUserRank::procIn(const QByteArray &binIn, quint8 dType)
{
errTxt("err: The requested user account does not exists.\n");
}
else if (rootUserId() == uId)
{
errTxt("err: You are not allowed to change the rank of root user: '" + uName + "'\n");
}
else if (!canModifyUser(uId, rd32BitFromBlock(hostRank), false))
else if (!canModifyUser(uId, rd32BitFromBlock(hostRank), uId == rdFromBlock(userId, BLKSIZE_USER_ID)))
{
errTxt("err: The target user out ranks you or is equal to your own rank. access denied.\n");
}
@ -423,10 +411,6 @@ void ChangeUsername::procIn(const QByteArray &binIn, quint8 dType)
{
errTxt("err: Invalid username. it must be 2-24 chars long and contain no spaces.\n");
}
else if (noCaseMatch(DEFAULT_ROOT_USER, newName))
{
errTxt("err: '" + QString(DEFAULT_ROOT_USER) + "' is a reserved keyword. invalid for use as a username.\n");
}
else if (validEmailAddr(newName))
{
errTxt("err: Invaild username. it looks like an email address.\n");

View File

@ -16,14 +16,184 @@
// along with MRCI under the LICENSE.md file. If not, see
// <http://www.gnu.org/licenses/>.
QString sslCertChain()
#include "db.h"
QString getLocalFilePath(const QString &fileName, bool var)
{
return expandEnvVariables(qEnvironmentVariable(ENV_PUB_KEY, DEFAULT_PUB_KEY_NAME));
#ifdef Q_OS_WINDOWS
auto path = QDir::homePath() + "\\AppData\\" + APP_TARGET + "\\" + fileName;
#else
auto path = QDir::homePath() + "/." + APP_TARGET + "/" + fileName;
#endif
if (!QFile::exists(path))
{
#ifdef Q_OS_WINDOWS
path = expandEnvVariables("%PROGRAMDATA%\\") + APP_TARGET + "\\" + fileName;
#else
if (var)
{
path = QString("/var/opt/") + APP_TARGET + "/" + fileName;
}
else
{
path = QString("/etc/") + APP_TARGET + "/" + fileName;
}
#endif
}
mkPath(QFileInfo(path).path());
return path;
}
QString sslPrivKey()
QJsonObject confObject()
{
return expandEnvVariables(qEnvironmentVariable(ENV_PRIV_KEY, DEFAULT_PRIV_KEY_NAME));
QJsonObject obj;
QFile file(getLocalFilePath(CONF_FILENAME));
if (file.exists())
{
if (file.open(QFile::ReadOnly))
{
obj = QJsonDocument::fromJson(file.readAll()).object();
}
else if (file.remove())
{
obj = confObject();
}
}
else
{
obj.insert(CONF_LISTEN_ADDR, DEFAULT_LISTEN_ADDRESS);
obj.insert(CONF_LISTEN_PORT, DEFAULT_LISTEN_PORT);
obj.insert(CONF_AUTO_LOCK_LIM, DEFAULT_LOCK_LIMIT);
obj.insert(CONF_MAX_SESSIONS, DEFAULT_MAXSESSIONS);
obj.insert(CONF_MAX_SUBS, DEFAULT_MAX_SUBS);
obj.insert(CONF_INIT_RANK, DEFAULT_INIT_RANK);
obj.insert(CONF_ENABLE_PUB_REG, false);
obj.insert(CONF_ENABLE_EVERIFY, true);
obj.insert(CONF_ENABLE_PWRES, true);
obj.insert(CONF_ALL_CH_UPDATE, false);
obj.insert(CONF_DB_DRIVER, DEFAULT_DB_DRIVER);
obj.insert(CONF_DB_ADDR, getLocalFilePath(DEFAULT_DB_FILENAME, true));
obj.insert(CONF_DB_UNAME, QSysInfo::machineHostName());
obj.insert(CONF_DB_PW, QString(QSysInfo::machineUniqueId().toHex()));
obj.insert(CONF_MAIL_CLIENT_CMD, DEFAULT_MAIL_SEND);
obj.insert(CONF_CERT_CHAIN, getLocalFilePath(DEFAULT_CERT_FILENAME));
obj.insert(CONF_PRIV_KEY, getLocalFilePath(DEFAULT_PRIV_FILENAME));
obj.insert(CONF_PW_RES_EMAIL_SUBJECT, DEFAULT_TEMP_PW_SUBJECT);
obj.insert(CONF_EVERIFY_SUBJECT, DEFAULT_CONFIRM_SUBJECT);
obj.insert(CONF_PW_RES_EMAIL_TEMP, getLocalFilePath(DEFAULT_RES_PW_FILENAME));
obj.insert(CONF_EVERIFY_TEMP, getLocalFilePath(DEFAULT_EVERIFY_FILENAME));
wrDefaultMailTemplates(obj);
if (file.open(QFile::WriteOnly | QFile::Truncate))
{
file.write(QJsonDocument(obj).toJson());
}
}
file.close();
return obj;
}
void wrDefaultMailTemplates(const QJsonObject &obj)
{
QFile file(obj[CONF_PW_RES_EMAIL_TEMP].toString());
if (file.open(QFile::WriteOnly | QFile::Truncate))
{
file.write(TXT_TempPwTemplate);
}
file.close();
file.setFileName(obj[CONF_EVERIFY_TEMP].toString());
if (file.open(QFile::WriteOnly | QFile::Truncate))
{
file.write(TXT_ConfirmCodeTemplate);
}
file.close();
}
void updateConf(const QJsonObject &obj)
{
QFile file(getLocalFilePath(CONF_FILENAME));
if (file.open(QFile::WriteOnly | QFile::Truncate))
{
file.write(QJsonDocument(obj).toJson());
}
file.close();
}
void updateConf(const char *key, const QJsonValue &value)
{
auto obj = confObject();
obj[key] = value;
updateConf(obj);
}
bool getEmailParams(const QString &mailCmd, const QString &bodyFile, QString *bodyText, QString *errMsg)
{
auto ret = false;
errMsg->clear();
bodyText->clear();
QFile file(bodyFile);
if (!file.open(QFile::ReadOnly))
{
errMsg->append("err: The host could not open the email message template, please notify a system administrator.\n");
qDebug() << "err: Could not open the email message template file '" << bodyFile << "' reason: " << file.errorString();
}
else
{
auto body = QString(file.readAll());
if ((!body.contains(DATE_SUB, Qt::CaseInsensitive)) ||
(!body.contains(USERNAME_SUB, Qt::CaseInsensitive)) ||
(!body.contains(OTP_SUB, Qt::CaseInsensitive)))
{
errMsg->append("err: The host email message template is invalid, please notify a system administrator.\n");
qDebug() << "err: Email message template '" << bodyFile << "' is missing one of the following key words: " << DATE_SUB << ", " << USERNAME_SUB << ", " << OTP_SUB;
}
else if ((mailCmd.contains(SUBJECT_SUB, Qt::CaseInsensitive)) ||
(mailCmd.contains(MSG_SUB, Qt::CaseInsensitive)) ||
(mailCmd.contains(TARGET_EMAIL_SUB, Qt::CaseInsensitive)))
{
errMsg->append("err: The host email client parameters are invalid, please notify a system administrator.\n");
qDebug() << "err: Email client command line '" << mailCmd << "' is missing one of the following key words: " << SUBJECT_SUB << ", " << MSG_SUB << ", " << TARGET_EMAIL_SUB;
}
else
{
ret = true;
}
}
file.close();
return ret;
}
QByteArray rdFileContents(const QString &path, QTextStream &msg)
@ -524,7 +694,7 @@ bool fullMatchChs(const char *openChs, const char *comp)
void containsActiveCh(const char *subChs, char *actBlock)
{
if (globalActiveFlag())
if (confObject()[CONF_ALL_CH_UPDATE].toBool())
{
wr8BitToBlock(1, actBlock);
}
@ -558,34 +728,11 @@ void containsActiveCh(const char *subChs, char *actBlock)
void printDatabaseInfo(QTextStream &txt)
{
auto json = getDbSettings();
auto driver = json["driver"].toString();
auto confObj = confObject();
txt << "Database Parameters --" << Qt::endl << Qt::endl;
txt << "Driver: " << driver << Qt::endl;
if (driver == "QSQLITE")
{
txt << "File: " << sqlDataPath() << Qt::endl;
}
else
{
txt << "Host: " << json["host_name"].toString() << Qt::endl;
txt << "User: " << json["user_name"].toString() << Qt::endl;
}
txt << Qt::endl;
}
QString defaultPw()
{
Query db;
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_DEFAULT_PASS);
db.exec();
return db.getData(COLUMN_DEFAULT_PASS).toString();
txt << "Driver: " << confObj[CONF_DB_DRIVER].toString() << Qt::endl;
txt << "Host: " << confObj[CONF_DB_ADDR].toString() << Qt::endl << Qt::endl;
}
bool channelExists(const QString &chName, quint64 *chId)
@ -637,17 +784,6 @@ bool inviteExists(const QByteArray &uId, quint64 chId)
return db.rows();
}
bool globalActiveFlag()
{
Query db;
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_ACTIVE_UPDATE);
db.exec();
return db.getData(COLUMN_ACTIVE_UPDATE).toBool();
}
bool genSubId(quint64 chId, quint8 *newId)
{
bool ret = false;
@ -659,7 +795,7 @@ bool genSubId(quint64 chId, quint8 *newId)
db.addCondition(COLUMN_CHANNEL_ID, chId);
db.exec();
if (db.rows() < maxSubChannels())
if (db.rows() < confObject()[CONF_MAX_SUBS].toInt())
{
QList<quint8> subList;
@ -691,17 +827,6 @@ bool isChOwner(const QByteArray &uId)
return db.rows();
}
int maxSubChannels()
{
Query db;
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_MAX_SUB_CH);
db.exec();
return db.getData(COLUMN_MAX_SUB_CH).toInt();
}
int channelAccessLevel(const QByteArray &uId, const char *override, quint64 chId)
{
if (rd8BitFromBlock(override) == 1)

View File

@ -36,7 +36,6 @@
#include <QDir>
#include <QSysInfo>
#include <QFileInfoList>
#include <QTemporaryFile>
#include <QChar>
#include <QtMath>
#include <QStorageInfo>
@ -63,35 +62,157 @@
#include <QTcpSocket>
#include <QMessageLogContext>
#include <QtGlobal>
#include <QLibrary>
#include <QJsonObject>
#include <QJsonDocument>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QVariant>
#include <QList>
#include <QSqlError>
#include <QDir>
#include <QFile>
#include <QTextCodec>
#include <QCryptographicHash>
#include <QDateTime>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include "db.h"
#include "shell.h"
#include "mem_share.h"
#define APP_NAME "MRCI"
#define APP_VER "5.0.2.1"
#define APP_TARGET "mrci"
#define SERVER_HEADER_TAG "MRCI"
#define HOST_CONTROL_PIPE "MRCI_HOST_CONTROL"
#define FRAME_HEADER_SIZE 8
#define MAX_FRAME_BITS 24
#define LOCAL_BUFFSIZE 16777215
#define CLIENT_HEADER_LEN 292
#define SERVER_HEADER_TAG "MRCI"
#define HOST_CONTROL_PIPE "MRCI_HOST_CONTROL"
#define MAX_LS_ENTRIES 50
#define SUBJECT_SUB "%subject%"
#define MSG_SUB "%message_body%"
#define TARGET_EMAIL_SUB "%target_email%"
#define OTP_SUB "%otp%"
#define USERNAME_SUB "%user_name%"
#define DATE_SUB "%date%"
#ifdef Q_OS_WIN
#define DEFAULT_MAIL_SEND "%COMSPEC% echo %message_body% | mutt -s %subject% %target_email%"
#else
#define DEFAULT_MAIL_SEND "/bin/sh -c \"echo %message_body% | mutt -s %subject% %target_email%\""
#endif
#define DEFAULT_DB_DRIVER "QSQLITE"
#define DEFAULT_DB_FILENAME "data.db"
#define DEFAULT_CERT_FILENAME "tls_chain.pem"
#define DEFAULT_PRIV_FILENAME "tls_priv.pem"
#define DEFAULT_RES_PW_FILENAME "res_pw_template.txt"
#define DEFAULT_EVERIFY_FILENAME "email_verify_template.txt"
#define DEFAULT_CONFIRM_SUBJECT "Email Verification"
#define DEFAULT_TEMP_PW_SUBJECT "Password Reset"
#define DEFAULT_LISTEN_ADDRESS "0.0.0.0"
#define DEFAULT_LISTEN_PORT 35516
#define DEFAULT_LOCK_LIMIT 20
#define DEFAULT_MAXSESSIONS 100
#define DEFAULT_MAX_SUBS 50
#define DEFAULT_INIT_RANK 2
#define CONF_FILENAME "conf.json"
#define CONF_LISTEN_ADDR "listening_addr"
#define CONF_LISTEN_PORT "listening_port"
#define CONF_AUTO_LOCK_LIM "auto_lock_limit"
#define CONF_MAX_SESSIONS "max_sessions"
#define CONF_MAX_SUBS "max_sub_channels"
#define CONF_INIT_RANK "initial_rank"
#define CONF_ENABLE_PUB_REG "enable_public_reg"
#define CONF_ENABLE_EVERIFY "enable_email_verify"
#define CONF_ENABLE_PWRES "enable_pw_reset"
#define CONF_ALL_CH_UPDATE "all_channels_active_update"
#define CONF_DB_DRIVER "db_driver"
#define CONF_DB_ADDR "db_host_name"
#define CONF_DB_UNAME "db_user_name"
#define CONF_DB_PW "db_password"
#define CONF_MAIL_CLIENT_CMD "mail_client_cmd"
#define CONF_CERT_CHAIN "tls_cert_chain"
#define CONF_PRIV_KEY "tls_priv_key"
#define CONF_PW_RES_EMAIL_SUBJECT "reset_pw_mail_subject"
#define CONF_EVERIFY_SUBJECT "email_verify_subject"
#define CONF_PW_RES_EMAIL_TEMP "reset_pw_mail_template"
#define CONF_EVERIFY_TEMP "email_verify_template"
#define TABLE_IPHIST "ip_history"
#define TABLE_USERS "users"
#define TABLE_CMD_RANKS "command_ranks"
#define TABLE_AUTH_LOG "auth_log"
#define TABLE_PW_RECOVERY "pw_recovery"
#define TABLE_DMESG "host_debug_messages"
#define TABLE_CHANNELS "channels"
#define TABLE_CH_MEMBERS "channel_members"
#define TABLE_SUB_CHANNELS "sub_channels"
#define TABLE_RDONLY_CAST "read_only_flags"
#define TABLE_MODULES "modules"
#define COLUMN_IPADDR "ip_address"
#define COLUMN_LOGENTRY "log_entry"
#define COLUMN_SESSION_ID "session_id"
#define COLUMN_TIME "time_stamp"
#define COLUMN_USERNAME "user_name"
#define COLUMN_HOST_RANK "host_rank"
#define COLUMN_COMMAND "command_name"
#define COLUMN_MOD_MAIN "module_executable"
#define COLUMN_SALT "salt"
#define COLUMN_HASH "hash"
#define COLUMN_EMAIL "email_address"
#define COLUMN_NEED_PASS "new_password_req"
#define COLUMN_NEED_NAME "new_name_req"
#define COLUMN_EMAIL_VERIFIED "email_verified"
#define COLUMN_LOCKED "locked"
#define COLUMN_AUTH_ATTEMPT "auth_attempt"
#define COLUMN_RECOVER_ATTEMPT "recover_attempt"
#define COLUMN_COUNT "count_to_threshold"
#define COLUMN_ACCEPTED "accepted"
#define COLUMN_DISPLAY_NAME "display_name"
#define COLUMN_USER_ID "user_id"
#define COLUMN_CHANNEL_NAME "channel_name"
#define COLUMN_CHANNEL_ID "channel_id"
#define COLUMN_ACTIVE_UPDATE "active_updates"
#define COLUMN_SUB_CH_NAME "sub_channel_name"
#define COLUMN_SUB_CH_ID "sub_channel_id"
#define COLUMN_PENDING_INVITE "pending_invite"
#define COLUMN_LOWEST_LEVEL "lowest_access_level"
#define COLUMN_ACCESS_LEVEL "access_level"
#define COLUMN_APP_NAME "client_app"
#define TXT_TempPwTemplate "\
A password reset was requested for your account: %user_name%\n\
Your recovery password is as follows:\n\n\
%otp%\n\n\
PLEASE IGNORE THIS EMAIL IF YOU MADE NO SUCH REQUEST.\n\n\
Date requested: %date%."
#define TXT_ConfirmCodeTemplate "\
Please confirm your email address for account: %user_name%\n\
Your confirmation code is as follows:\n\n\
%otp%\n\n\
Date requested: %date%."
enum AsyncCommands : quint16
{
ASYNC_RDY = 1, // client | retricted
ASYNC_SYS_MSG = 2, // client | retricted
ASYNC_EXIT = 3, // internal | private
ASYNC_CAST = 4, // client | public
ASYNC_MAXSES = 5, // internal | private
ASYNC_LOGOUT = 6, // internal | private
ASYNC_USER_DELETED = 7, // client | public
ASYNC_DISP_RENAMED = 8, // internal | public
ASYNC_USER_RANK_CHANGED = 9, // internal | public
ASYNC_CMD_RANKS_CHANGED = 10, // internal | public
ASYNC_RESTART = 11, // internal | private
ASYNC_ENABLE_MOD = 12, // internal | public
ASYNC_DISABLE_MOD = 13, // internal | public
ASYNC_END_SESSION = 14, // internal | private
@ -221,6 +342,7 @@ enum ChannelMemberLevel : quint8
};
class Session;
class Query;
QByteArray toFixedTEXT(const QString &txt, int len);
QByteArray nullTermTEXT(const QString &txt);
@ -232,6 +354,10 @@ void serializeThread(QThread *thr);
void mkPath(const QString &path);
void listDir(QList<QPair<QString,QString> > &list, const QString &srcPath, const QString &dstPath);
void containsActiveCh(const char *subChs, char *actBlock);
void updateConf(const char *key, const QJsonValue &value);
void updateConf(const QJsonObject &obj);
void wrDefaultMailTemplates(const QJsonObject &obj);
bool getEmailParams(const QString &mailCmd, const QString &bodyFile, QString *bodyText, QString *errMsg);
bool acceptablePw(const QString &pw, const QByteArray &uId, QString *errMsg);
bool acceptablePw(const QString &pw, const QString &uName, const QString &dispName, const QString &email, QString *errMsg);
bool containsNewLine(const QString &str);
@ -261,12 +387,10 @@ bool noCaseMatch(const QString &strA, const QString &strB);
bool argExists(const QString &key, const QStringList &args);
bool matchAnyCh(const char *chsA, const char *chsB);
bool fullMatchChs(const char *openChs, const char *comp);
bool globalActiveFlag();
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 getUserNameForEmail(const QString &email);
QString getEmailForUser(const QByteArray &uId);
QString getDispName(const QByteArray &uId);
@ -275,10 +399,9 @@ QString boolStr(bool state);
QString getParam(const QString &key, const QStringList &args);
QString escapeChars(const QString &str, const QChar &escapeChr, const QChar &chr);
QString genSerialNumber();
QString defaultPw();
QString sslCertChain();
QString sslPrivKey();
QString getLocalFilePath(const QString &fileName, bool var = false);
QStringList parseArgs(const QByteArray &data, int maxArgs, int *pos = nullptr);
QJsonObject confObject();
//---------------------------

View File

@ -25,31 +25,25 @@ QString columnType(const QString &column)
{
QString ret;
if ((column == COLUMN_IPADDR) || (column == COLUMN_LOGENTRY) || (column == COLUMN_USERNAME) ||
(column == COLUMN_CHANNEL_NAME) || (column == COLUMN_EMAIL) || (column == COLUMN_SUB_CH_NAME) ||
(column == COLUMN_COMMAND) || (column == COLUMN_CLIENT_VER) || (column == COLUMN_DISPLAY_NAME))
if ((column == COLUMN_IPADDR) || (column == COLUMN_LOGENTRY) || (column == COLUMN_USERNAME) ||
(column == COLUMN_CHANNEL_NAME) || (column == COLUMN_EMAIL) || (column == COLUMN_SUB_CH_NAME) ||
(column == COLUMN_COMMAND) || (column == COLUMN_DISPLAY_NAME))
{
ret = "TEXT COLLATE NOCASE";
}
else if ((column == COLUMN_COUNT) || (column == COLUMN_ACCEPTED) || (column == COLUMN_LOCKED) ||
(column == COLUMN_NEED_PASS) || (column == COLUMN_NEED_NAME) || (column == COLUMN_PUB_USERS) ||
(column == COLUMN_AUTH_ATTEMPT) || (column == COLUMN_EMAIL_VERIFIED) || (column == COLUMN_ENABLE_PW_RESET) ||
(column == COLUMN_ENABLE_CONFIRM) || (column == COLUMN_RECOVER_ATTEMPT) || (column == COLUMN_ACTIVE_UPDATE) ||
(column == COLUMN_PENDING_INVITE))
else if ((column == COLUMN_COUNT) || (column == COLUMN_ACCEPTED) || (column == COLUMN_LOCKED) ||
(column == COLUMN_NEED_PASS) || (column == COLUMN_NEED_NAME) || (column == COLUMN_PENDING_INVITE) ||
(column == COLUMN_AUTH_ATTEMPT) || (column == COLUMN_EMAIL_VERIFIED) || (column == COLUMN_ACTIVE_UPDATE) ||
(column == COLUMN_RECOVER_ATTEMPT))
{
ret = "BOOL";
}
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_MAIN))
else if (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_INITRANK))
else if ((column == COLUMN_CHANNEL_ID) || (column == COLUMN_LOWEST_LEVEL) || (column == COLUMN_SUB_CH_ID) ||
(column == COLUMN_HOST_RANK) || (column == COLUMN_ACCESS_LEVEL))
{
ret = "INTEGER";
}
@ -76,11 +70,6 @@ QByteArray genUniqueHash()
return hasher.result();
}
QString sqlDataPath()
{
return expandEnvVariables(qEnvironmentVariable(ENV_DB_PATH, DEFAULT_DB_FILE));
}
QList<int> genSequence(int min, int max, int len)
{
QList<int> ret;
@ -189,17 +178,6 @@ QString genPw()
return ret;
}
quint32 initHostRank()
{
Query db;
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_INITRANK);
db.exec();
return db.getData(COLUMN_INITRANK).toUInt();
}
QByteArray getSalt(const QByteArray &uId, const QString &table)
{
Query db;
@ -212,77 +190,20 @@ QByteArray getSalt(const QByteArray &uId, const QString &table)
return db.getData(COLUMN_SALT).toByteArray();
}
QByteArray rootUserId()
bool testDbWritable()
{
Query db;
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_ROOT_USER);
db.setType(Query::CREATE_TABLE, "test");
db.addColumn("test");
db.exec();
auto id = db.getData(COLUMN_ROOT_USER).toByteArray();
db.setType(Query::DEL_TABLE, "test");
if (id.isEmpty())
{
db.setType(Query::PULL, TABLE_USERS);
db.addColumn(COLUMN_USER_ID);
db.addCondition(COLUMN_USERNAME, DEFAULT_ROOT_USER);
db.exec();
id = db.getData(COLUMN_USER_ID).toByteArray();
}
return id;
return db.exec();
}
QJsonObject getDbSettings(bool defaults)
{
QJsonObject ret;
QFile file(DEFAULT_DB_JSON_FILE);
if (file.exists() && !defaults)
{
if (file.open(QFile::ReadOnly))
{
ret = QJsonDocument::fromJson(file.readAll()).object();
}
else
{
ret = getDbSettings(true);
}
}
else
{
ret.insert("driver", "QSQLITE");
ret.insert("host_name", "localhost");
ret.insert("user_name", QSysInfo::machineHostName());
ret.insert("password", QString(QSysInfo::machineUniqueId().toHex()));
if (file.open(QFile::WriteOnly | QFile::Truncate))
{
file.write(QJsonDocument(ret).toJson());
}
}
file.close();
return ret;
}
void saveDbSettings(const QJsonObject &obj)
{
QFile file(DEFAULT_DB_JSON_FILE);
if (file.open(QFile::WriteOnly | QFile::Truncate))
{
file.write(QJsonDocument(obj).toJson());
}
file.close();
}
bool createUser(const QString &userName, const QString &email, const QString &dispName, const QString &password)
bool createUser(const QString &userName, const QString &email, const QString &dispName, const QString &password, int rank, bool requireNewPass)
{
auto ret = false;
auto newUId = genUniqueHash();
@ -292,7 +213,7 @@ bool createUser(const QString &userName, const QString &email, const QString &di
db.setType(Query::PUSH, TABLE_USERS);
db.addColumn(COLUMN_USERNAME, userName);
db.addColumn(COLUMN_EMAIL, email);
db.addColumn(COLUMN_HOST_RANK, initHostRank());
db.addColumn(COLUMN_HOST_RANK, rank);
db.addColumn(COLUMN_DISPLAY_NAME, dispName);
db.addColumn(COLUMN_EMAIL_VERIFIED, false);
db.addColumn(COLUMN_NEED_PASS, false);
@ -303,7 +224,7 @@ bool createUser(const QString &userName, const QString &email, const QString &di
if (db.exec())
{
ret = updatePassword(newUId, password, TABLE_USERS);
ret = updatePassword(newUId, password, TABLE_USERS, requireNewPass);
}
return ret;
@ -401,28 +322,34 @@ Query::Query(QObject *parent) : QObject(parent)
if (!QSqlDatabase::contains(getConnectionName()))
{
auto settings = getDbSettings();
auto driver = settings["driver"].toString();
auto db = QSqlDatabase::addDatabase(driver, getConnectionName());
auto confObj = confObject();
auto driver = confObj[CONF_DB_DRIVER].toString();
auto db = QSqlDatabase::addDatabase(driver, getConnectionName());
db.setConnectOptions("ISC_DPB_LC_CTYPE=UTF8");
if (driver == "QSQLITE")
{
db.setDatabaseName(sqlDataPath());
db.setDatabaseName(confObj[CONF_DB_ADDR].toString());
}
else
{
db.setDatabaseName(APP_NAME);
db.setUserName(settings["user_name"].toString());
db.setHostName(settings["host_name"].toString());
db.setPassword(settings["password"].toString());
db.setUserName(confObj[CONF_DB_UNAME].toString());
db.setHostName(confObj[CONF_DB_ADDR].toString());
db.setPassword(confObj[CONF_DB_PW].toString());
}
if (db.open())
{
enableForeignKeys(true);
setTextEncoding("UTF8");
if (!testDbWritable())
{
queryOk = false;
lastErr = "Write access to the database is denied.";
}
}
else
{
@ -562,6 +489,11 @@ void Query::setType(QueryType qType, const QString &tbl)
break;
}
case DEL_TABLE:
txt << "DROP TABLE " << tbl;
break;
}
}

156
src/db.h
View File

@ -17,150 +17,7 @@
// along with MRCI under the LICENSE.md file. If not, see
// <http://www.gnu.org/licenses/>.
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QVariant>
#include <QList>
#include <QSqlError>
#include <QDir>
#include <QCoreApplication>
#include <QFile>
#include <QTextCodec>
#include <QThread>
#include <QCryptographicHash>
#include <QTextStream>
#include <QStringList>
#include <QProcess>
#include <QDateTime>
#include <QRandomGenerator>
#include <QJsonObject>
#include <QJsonDocument>
#include "shell.h"
#define APP_NAME "MRCI"
#define APP_VER "4.1.2.0"
#define APP_TARGET "mrci"
#ifdef Q_OS_WIN
#define DEFAULT_MAILBIN "%COMSPEC%"
#define DEFAULT_MAIL_SEND "echo %message_body% | mutt -s %subject% %target_email%"
#define DEFAULT_WORK_DIR "%PROGRAMDATA%\\mrci"
#else
#define DEFAULT_MAILBIN "/bin/sh"
#define DEFAULT_MAIL_SEND "-c \"echo %message_body% | mutt -s %subject% %target_email%\""
#define DEFAULT_WORK_DIR "/var/opt/mrci"
#endif
#define ENV_DB_PATH "MRCI_DB_PATH"
#define ENV_WORK_DIR "MRCI_WORK_DIR"
#define ENV_PRIV_KEY "MRCI_PRIV_KEY"
#define ENV_PUB_KEY "MRCI_PUB_KEY"
#define SUBJECT_SUB "%subject%"
#define MSG_SUB "%message_body%"
#define TARGET_EMAIL_SUB "%target_email%"
#define CONFIRM_CODE_SUB "%confirmation_code%"
#define TEMP_PW_SUB "%temp_pw%"
#define USERNAME_SUB "%user_name%"
#define DATE_SUB "%date%"
#define DEFAULT_PUB_KEY_NAME "cert.pem"
#define DEFAULT_PRIV_KEY_NAME "priv.pem"
#define DEFAULT_DB_FILE "data.db"
#define DEFAULT_DB_JSON_FILE "db_settings.json"
#define DEFAULT_ROOT_USER "root"
#define DEFAULT_CONFIRM_SUBJECT "Email Verification"
#define DEFAULT_TEMP_PW_SUBJECT "Password Reset"
#define DEFAULT_LISTEN_ADDRESS "0.0.0.0"
#define DEFAULT_LISTEN_PORT 35516
#define DEFAULT_BAN_LIMIT 30
#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
#define TXT_TempPwTemplate "\
A password reset was requested for account: %user_name%\n\
Your recovery password is as follows:\n\n\
%temp_pw%\n\n\
PLEASE IGNORE THIS EMAIL IF YOU MADE NO SUCH REQUEST.\n\n\
Date requested: %date%."
#define TXT_ConfirmCodeTemplate "\
Please confirm your email address for account: %user_name%\n\
Your confirmation code is as follows:\n\n\
%confirmation_code%\n\n\
Date requested: %date%."
#define TABLE_IPHIST "ip_history"
#define TABLE_USERS "users"
#define TABLE_SERV_SETTINGS "host_settings"
#define TABLE_CMD_RANKS "command_ranks"
#define TABLE_AUTH_LOG "auth_log"
#define TABLE_PW_RECOVERY "pw_recovery"
#define TABLE_DMESG "host_debug_messages"
#define TABLE_CHANNELS "channels"
#define TABLE_CH_MEMBERS "channel_members"
#define TABLE_SUB_CHANNELS "sub_channels"
#define TABLE_RDONLY_CAST "read_only_flags"
#define TABLE_MODULES "modules"
#define COLUMN_IPADDR "ip_address"
#define COLUMN_LOGENTRY "log_entry"
#define COLUMN_CLIENT_VER "client_version"
#define COLUMN_SESSION_ID "session_id"
#define COLUMN_TIME "time_stamp"
#define COLUMN_USERNAME "user_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_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"
#define COLUMN_EMAIL "email_address"
#define COLUMN_NEED_PASS "new_password_req"
#define COLUMN_NEED_NAME "new_name_req"
#define COLUMN_EMAIL_VERIFIED "email_verified"
#define COLUMN_LOCKED "locked"
#define COLUMN_ZIPBIN "archiver_executable"
#define COLUMN_ZIPEXTRACT "extract_command"
#define COLUMN_ZIPCOMPRESS "compress_command"
#define COLUMN_AUTH_ATTEMPT "auth_attempt"
#define COLUMN_RECOVER_ATTEMPT "recover_attempt"
#define COLUMN_COUNT "count_to_threshold"
#define COLUMN_ACCEPTED "accepted"
#define COLUMN_MAILERBIN "mailer_executable"
#define COLUMN_MAIL_SEND "mailer_send_command"
#define COLUMN_TEMP_PW_MSG "temp_pw_email_template"
#define COLUMN_CONFIRM_MSG "verify_email_template"
#define COLUMN_CONFIRM_SUBJECT "verify_email_subject"
#define COLUMN_TEMP_PW_SUBJECT "temp_pw_email_subject"
#define COLUMN_ENABLE_PW_RESET "enable_pw_reset"
#define COLUMN_ENABLE_CONFIRM "eable_email_verify"
#define COLUMN_DISPLAY_NAME "display_name"
#define COLUMN_USER_ID "user_id"
#define COLUMN_CHANNEL_NAME "channel_name"
#define COLUMN_CHANNEL_ID "channel_id"
#define COLUMN_ACTIVE_UPDATE "active_updates"
#define COLUMN_SUB_CH_NAME "sub_channel_name"
#define COLUMN_SUB_CH_ID "sub_channel_id"
#define COLUMN_PENDING_INVITE "pending_invite"
#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"
#define COLUMN_ROOT_USER "root_user"
#include "common.h"
QString genPw();
QList<int> genSequence(int min, int max, int len);
@ -168,17 +25,14 @@ QChar genLetter();
QChar genNum();
QChar genSpecialChar();
int inRange(int pos, int min, int max);
QString sqlDataPath();
QString columnType(const QString &column);
quint32 initHostRank();
QByteArray getSalt(const QByteArray &uId, const QString &table);
QByteArray genUniqueHash();
QByteArray rootUserId();
QJsonObject getDbSettings(bool defaults = false);
bool createUser(const QString &userName, const QString &email, const QString &dispName, const QString &password);
bool createUser(const QString &userName, const QString &email, const QString &dispName, const QString &password, int rank, bool requireNewPass = false);
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);
bool testDbWritable();
void cleanupDbConnection();
void saveDbSettings(const QJsonObject &obj);
void moveCharLeft(int pos, QString &str);
@ -198,7 +52,8 @@ public:
DEL,
INNER_JOIN_PULL,
CREATE_TABLE,
ALTER_TABLE
ALTER_TABLE,
DEL_TABLE
};
enum Condition
@ -251,6 +106,7 @@ public:
QStringList columnsInTable(const QString &tbl);
QVariant getData(const QString &column, int row = 0);
QString errDetail();
QString errString();
QList<QList<QVariant> > &allData();
private:

View File

@ -23,9 +23,6 @@ bool setupDb(QString *errMsg)
errMsg->clear();
Query query(QThread::currentThread());
Query defaults(QThread::currentThread());
QString randPw;
if (query.inErrorstate())
{
@ -107,37 +104,6 @@ bool setupDb(QString *errMsg)
query.addUnique(COLUMN_USER_ID);
ret = query.exec();
if (query.createExecuted())
{
auto uId = genUniqueHash();
query.setType(Query::PUSH, TABLE_USERS);
query.addColumn(COLUMN_USERNAME, DEFAULT_ROOT_USER);
query.addColumn(COLUMN_HOST_RANK, 1);
query.addColumn(COLUMN_NEED_PASS, true);
query.addColumn(COLUMN_NEED_NAME, true);
query.addColumn(COLUMN_LOCKED, false);
query.addColumn(COLUMN_EMAIL_VERIFIED, false);
query.addColumn(COLUMN_USER_ID, uId);
query.addRandBlob(COLUMN_SALT, 128);
ret = query.exec();
if (ret)
{
randPw = genPw();
ret = updatePassword(uId, randPw, TABLE_USERS, true);
}
}
else
{
query.setType(Query::UPDATE, TABLE_USERS);
query.addColumn(COLUMN_NEED_NAME, true);
query.addCondition(COLUMN_USERNAME, DEFAULT_ROOT_USER);
ret = query.exec();
}
}
if (ret)
@ -199,126 +165,8 @@ bool setupDb(QString *errMsg)
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);
}
if (ret)
{
query.setType(Query::CREATE_TABLE, TABLE_SERV_SETTINGS);
query.addColumn(COLUMN_IPADDR);
query.addColumn(COLUMN_PORT);
query.addColumn(COLUMN_BAN_LIMIT);
query.addColumn(COLUMN_LOCK_LIMIT);
query.addColumn(COLUMN_MAXSESSIONS);
query.addColumn(COLUMN_PUB_USERS);
query.addColumn(COLUMN_ZIPBIN);
query.addColumn(COLUMN_ZIPCOMPRESS);
query.addColumn(COLUMN_ZIPEXTRACT);
query.addColumn(COLUMN_INITRANK);
query.addColumn(COLUMN_MAILERBIN);
query.addColumn(COLUMN_MAIL_SEND);
query.addColumn(COLUMN_CONFIRM_SUBJECT);
query.addColumn(COLUMN_TEMP_PW_SUBJECT);
query.addColumn(COLUMN_CONFIRM_MSG);
query.addColumn(COLUMN_TEMP_PW_MSG);
query.addColumn(COLUMN_ENABLE_CONFIRM);
query.addColumn(COLUMN_ENABLE_PW_RESET);
query.addColumn(COLUMN_ACTIVE_UPDATE);
query.addColumn(COLUMN_MAX_SUB_CH);
query.addColumn(COLUMN_DEFAULT_PASS);
query.addColumn(COLUMN_ROOT_USER);
ret = query.exec();
if (randPw.isEmpty())
{
randPw = genPw();
}
auto rootUId = rootUserId();
if (query.createExecuted())
{
query.setType(Query::PUSH, TABLE_SERV_SETTINGS);
query.addColumn(COLUMN_IPADDR, DEFAULT_LISTEN_ADDRESS);
query.addColumn(COLUMN_PORT, DEFAULT_LISTEN_PORT);
query.addColumn(COLUMN_BAN_LIMIT, DEFAULT_BAN_LIMIT);
query.addColumn(COLUMN_LOCK_LIMIT, DEFAULT_LOCK_LIMIT);
query.addColumn(COLUMN_MAXSESSIONS, DEFAULT_MAXSESSIONS);
query.addColumn(COLUMN_PUB_USERS, false);
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);
query.addColumn(COLUMN_TEMP_PW_SUBJECT, DEFAULT_TEMP_PW_SUBJECT);
query.addColumn(COLUMN_CONFIRM_MSG, TXT_ConfirmCodeTemplate);
query.addColumn(COLUMN_TEMP_PW_MSG, TXT_TempPwTemplate);
query.addColumn(COLUMN_ENABLE_CONFIRM, true);
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);
query.addColumn(COLUMN_ROOT_USER, rootUId);
ret = query.exec();
}
else
{
query.setType(Query::PULL, TABLE_SERV_SETTINGS);
query.addColumn(COLUMN_IPADDR);
query.addColumn(COLUMN_PORT);
query.addColumn(COLUMN_BAN_LIMIT);
query.addColumn(COLUMN_LOCK_LIMIT);
query.addColumn(COLUMN_MAXSESSIONS);
query.addColumn(COLUMN_PUB_USERS);
query.addColumn(COLUMN_ZIPBIN);
query.addColumn(COLUMN_ZIPCOMPRESS);
query.addColumn(COLUMN_ZIPEXTRACT);
query.addColumn(COLUMN_INITRANK);
query.addColumn(COLUMN_MAILERBIN);
query.addColumn(COLUMN_MAIL_SEND);
query.addColumn(COLUMN_CONFIRM_SUBJECT);
query.addColumn(COLUMN_TEMP_PW_SUBJECT);
query.addColumn(COLUMN_CONFIRM_MSG);
query.addColumn(COLUMN_TEMP_PW_MSG);
query.addColumn(COLUMN_ENABLE_CONFIRM);
query.addColumn(COLUMN_ENABLE_PW_RESET);
query.addColumn(COLUMN_ACTIVE_UPDATE);
query.addColumn(COLUMN_MAX_SUB_CH);
query.addColumn(COLUMN_DEFAULT_PASS);
query.addColumn(COLUMN_ROOT_USER);
ret = query.exec();
if (ret)
{
defaults.setType(Query::UPDATE, TABLE_SERV_SETTINGS);
if (query.getData(COLUMN_IPADDR).isNull()) defaults.addColumn(COLUMN_IPADDR, DEFAULT_LISTEN_ADDRESS);
if (query.getData(COLUMN_PORT).isNull()) defaults.addColumn(COLUMN_PORT, DEFAULT_LISTEN_PORT);
if (query.getData(COLUMN_BAN_LIMIT).isNull()) defaults.addColumn(COLUMN_BAN_LIMIT, DEFAULT_BAN_LIMIT);
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_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);
if (query.getData(COLUMN_TEMP_PW_SUBJECT).isNull()) defaults.addColumn(COLUMN_TEMP_PW_SUBJECT, DEFAULT_TEMP_PW_SUBJECT);
if (query.getData(COLUMN_CONFIRM_MSG).isNull()) defaults.addColumn(COLUMN_CONFIRM_MSG, TXT_ConfirmCodeTemplate);
if (query.getData(COLUMN_TEMP_PW_MSG).isNull()) defaults.addColumn(COLUMN_TEMP_PW_MSG, TXT_TempPwTemplate);
if (query.getData(COLUMN_ENABLE_CONFIRM).isNull()) defaults.addColumn(COLUMN_ENABLE_CONFIRM, true);
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 (query.getData(COLUMN_ROOT_USER).isNull()) defaults.addColumn(COLUMN_ROOT_USER, rootUId);
if (defaults.columns())
{
ret = defaults.exec();
}
}
}
}
if (query.inErrorstate())
@ -327,11 +175,5 @@ bool setupDb(QString *errMsg)
errMsg->append(query.errDetail());
}
if (defaults.inErrorstate())
{
errMsg->append("setup of default parameters: \n");
errMsg->append(defaults.errDetail());
}
return ret;
}

View File

@ -64,28 +64,31 @@ void showHelp()
txtOut << " -help : display usage information about this application." << Qt::endl;
txtOut << " -stop : stop the current host instance if one is currently running." << Qt::endl;
txtOut << " -about : display versioning/warranty information about this application." << Qt::endl;
txtOut << " -addr : set the listening address and port for TCP clients." << Qt::endl;
txtOut << " -status : display status information about the host instance if it is currently running." << Qt::endl;
txtOut << " -reset_root : reset the root account password to the default password." << Qt::endl;
txtOut << " -host : start a new host instance. (this blocks)" << Qt::endl;
txtOut << " -host_trig : start a new host instance. (this does not block)" << Qt::endl;
txtOut << " -default_pw : show the default password." << Qt::endl;
txtOut << " -public_cmds : run the internal module to list it's public commands. for internal use only." << Qt::endl;
txtOut << " -exempt_cmds : run the internal module to list it's rank exempt commands. for internal use only." << Qt::endl;
txtOut << " -user_cmds : run the internal module to list it's user commands. for internal use only." << Qt::endl;
txtOut << " -run_cmd : run an internal module command. for internal use only." << Qt::endl;
txtOut << " -ls_sql_drvs : list all available SQL drivers that the host currently supports." << Qt::endl;
txtOut << " -load_ssl : re-load the host SSL certificate without stopping the host instance." << Qt::endl << Qt::endl;
txtOut << " -load_ssl : re-load the host SSL certificate without stopping the host instance." << Qt::endl;
txtOut << " -elevate : elevate any user account to rank 1." << Qt::endl;
txtOut << " -add_admin : create a rank 1 account with a randomized one time password." << Qt::endl << Qt::endl;
txtOut << "Internal module | -public_cmds, -user_cmds, -exempt_cmds, -run_cmd |:" << Qt::endl << Qt::endl;
txtOut << " -pipe : the named pipe used to establish a data connection with the session." << Qt::endl;
txtOut << " -mem_ses : the shared memory key for the session." << Qt::endl;
txtOut << " -mem_host : the shared memory key for the host main process." << Qt::endl << Qt::endl;
txtOut << "Details:" << Qt::endl << Qt::endl;
txtOut << "addr - this argument takes a {ip_address:port} string. it will return an error if not formatted correctly" << Qt::endl;
txtOut << " examples: 10.102.9.2:35516 or 0.0.0.0:35516." << Qt::endl << Qt::endl;
txtOut << "run_cmd - this argument is used by the host itself along with the internal module arguments to run the" << Qt::endl;
txtOut << " internal command names passed by it. this is not ment to be run directly by human input. the" << Qt::endl;
txtOut << " executable will auto close if it fails to connect to the pipe and/or shared memory segments" << Qt::endl << Qt::endl;
txtOut << "add_admin - this argument takes a single string representing a user name to create a rank 1 account with." << Qt::endl;
txtOut << " the host will set a randomized password for it and display it on the CLI. this user will be" << Qt::endl;
txtOut << " required to change the password upon logging in." << Qt::endl;
txtOut << " example: -add_admin somebody" << Qt::endl << Qt::endl;
txtOut << "elevate - this argument takes a single string representing a user name to an account to promote to rank 1." << Qt::endl;
txtOut << " example: -elevate somebody" << Qt::endl << Qt::endl;
txtOut << "run_cmd - this argument is used by the host itself along with the internal module arguments to run the" << Qt::endl;
txtOut << " internal command names passed by it. this is not ment to be run directly by human input. the" << Qt::endl;
txtOut << " executable will auto close if it fails to connect to the pipe and/or shared memory segments" << Qt::endl << Qt::endl;
}
void soeDueToDbErr(int *retCode, const QString *errMsg)
@ -121,15 +124,9 @@ int main(int argc, char *argv[])
serializeThread(app.thread());
auto workDir = expandEnvVariables(qEnvironmentVariable(ENV_WORK_DIR, DEFAULT_WORK_DIR));
auto args = QCoreApplication::arguments();
auto ret = 0;
auto args = QCoreApplication::arguments();
auto ret = 0;
QDir dir(workDir);
if (!dir.exists()) dir.mkpath(workDir);
QDir::setCurrent(workDir);
QCoreApplication::setApplicationName(APP_NAME);
QCoreApplication::setApplicationVersion(APP_VER);
@ -144,13 +141,6 @@ int main(int argc, char *argv[])
args.contains("-exempt_cmds", Qt::CaseInsensitive) ||
args.contains("-user_cmds", Qt::CaseInsensitive))
{
// security note: it is critical that the above internal arguments are checked
// first. external clients have the ability to pass additional
// args and those args come through here. it can be a security
// threat if an external arg is a powerful arg like "reset_root"
// and it ends up getting processed unintentionally by this
// function.
if (setupDb(&err))
{
auto *mod = new Module(&app);
@ -208,64 +198,66 @@ int main(int argc, char *argv[])
{
QProcess::startDetached(QCoreApplication::applicationFilePath(), QStringList() << "-host");
}
else if (args.contains("-addr", Qt::CaseInsensitive))
else if (args.contains("-elevate", Qt::CaseInsensitive))
{
auto params = getParam("-addr", args);
auto addr = params.split(':');
ret = 1;
ret = 128;
QByteArray uId;
if (addr.size() != 2)
if (args.size() <= 2)
{
QTextStream(stderr) << "" << Qt::endl << "err: Address string parsing error, number of params found: " << addr.size() << Qt::endl;
QTextStream(stderr) << "err: A user name was not given." << Qt::endl;
}
else if (!validUserName(args[2]))
{
QTextStream(stderr) << "err: Invalid user name." << Qt::endl;
}
else if (!userExists(args[2], &uId))
{
QTextStream(stderr) << "err: The user name does not exists." << Qt::endl;
}
else
{
bool pOk;
auto port = addr[1].toUShort(&pOk);
Query db;
if (!pOk)
{
QTextStream(stderr) << "" << Qt::endl << "err: Invalid port." << Qt::endl;
}
else if (port == 0)
{
QTextStream(stderr) << "" << Qt::endl << "err: The port cannot be 0." << Qt::endl;
}
else if (QHostAddress(addr[0]).isNull())
{
QTextStream(stderr) << "" << Qt::endl << "err: Invalid ip address." << Qt::endl;
}
else
{
Query db(&app);
db.setType(Query::UPDATE, TABLE_USERS);
db.addColumn(COLUMN_HOST_RANK, 1);
db.addCondition(COLUMN_USER_ID, uId);
db.exec();
db.setType(Query::UPDATE, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_IPADDR, addr[0]);
db.addColumn(COLUMN_PORT, port);
db.exec();
ret = shellToHost(args, true, app);
}
ret = 0;
}
}
else if (args.contains("-reset_root", Qt::CaseInsensitive))
else if (args.contains("-add_admin", Qt::CaseInsensitive))
{
auto uId = rootUserId();
ret = 1;
Query db(&app);
if (args.size() <= 2)
{
QTextStream(stderr) << "err: A user name was not given." << Qt::endl;
}
else if (!validUserName(args[2]))
{
QTextStream(stderr) << "err: Invalid user name." << Qt::endl;
}
else if (userExists(args[2]))
{
QTextStream(stderr) << "err: The user name already exists." << Qt::endl;
}
else
{
auto randPw = genPw();
db.setType(Query::UPDATE, TABLE_USERS);
db.addColumn(COLUMN_LOCKED, false);
db.addCondition(COLUMN_USER_ID, uId);
db.exec();
createUser(args[2], "", "", randPw, 1, true);
updatePassword(uId, defaultPw(), TABLE_USERS, true);
QTextStream(stdout) << "password: " << randPw << Qt::endl;
ret = 0;
}
}
else if (args.contains("-default_pw", Qt::CaseInsensitive))
else
{
QTextStream(stdout) << "" << Qt::endl << " Root User : " << getUserName(rootUserId()) << Qt::endl;
QTextStream(stdout) << " Default Password: " << defaultPw() << Qt::endl << Qt::endl;
showHelp();
}
}
else

View File

@ -222,8 +222,8 @@ bool genDefaultSSLFiles(const QString &outsideAddr, QTextStream &msg)
auto ret = genRSAKey(cert, msg);
if (ret) ret = genX509(cert, outsideAddr, msg);
if (ret) ret = writePrivateKey(DEFAULT_PRIV_KEY_NAME, cert, msg);
if (ret) ret = writeX509(DEFAULT_PUB_KEY_NAME, cert, msg);
if (ret) ret = writePrivateKey(DEFAULT_PRIV_FILENAME, cert, msg);
if (ret) ret = writeX509(DEFAULT_CERT_FILENAME, cert, msg);
cert->cleanup();
cert->deleteLater();

View File

@ -32,7 +32,7 @@
#include <QHostAddress>
#include <QSslKey>
#include "db.h"
#include "common.h"
class Cert : public QObject
{

View File

@ -16,36 +16,12 @@
// along with MRCI under the LICENSE.md file. If not, see
// <http://www.gnu.org/licenses/>.
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();
}
Module::Module(QObject *parent) : QObject(parent) {}
QStringList Module::userCmdList()
{
QStringList ret;
ret << CloseHost::cmdName();
ret << RestartHost::cmdName();
ret << Cast::cmdName();
ret << OpenSubChannel::cmdName();
ret << CloseSubChannel::cmdName();
@ -63,15 +39,12 @@ QStringList Module::userCmdList()
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();
@ -121,12 +94,14 @@ QStringList Module::pubCmdList()
ret << Auth::cmdName();
ret << MyInfo::cmdName();
if (pubReg)
auto confObj = confObject();
if (confObj[CONF_ENABLE_PUB_REG].toBool())
{
ret << CreateUser::cmdName();
}
if (passwrdResets)
if (confObj[CONF_ENABLE_PWRES].toBool())
{
ret << ResetPwRequest::cmdName();
ret << RecoverAcct::cmdName();
@ -147,7 +122,9 @@ QStringList Module::rankExemptList()
ret << ChangeEmail::cmdName();
ret << IsEmailVerified::cmdName();
if (emailConfirmation)
auto confObj = confObject();
if (confObj[CONF_ENABLE_EVERIFY].toBool())
{
ret << VerifyEmail::cmdName();
}
@ -161,9 +138,7 @@ bool Module::runCmd(const QString &name)
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);
if (noCaseMatch(name, Auth::cmdName())) new Auth(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);
@ -182,7 +157,6 @@ bool Module::runCmd(const QString &name)
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);
@ -194,8 +168,6 @@ bool Module::runCmd(const QString &name)
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);

View File

@ -19,7 +19,6 @@
#include "common.h"
#include "cmd_object.h"
#include "commands/admin.h"
#include "commands/cast.h"
#include "commands/info.h"
#include "commands/mods.h"
@ -37,10 +36,6 @@ class Module : public QObject
private:
bool pubReg;
bool emailConfirmation;
bool passwrdResets;
bool runCmd(const QString &name);
void listCmds(const QStringList &list);
void loadSettings();

View File

@ -594,18 +594,6 @@ void Session::privAsyncDataIn(quint16 cmdId, const QByteArray &data)
{
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<quint32>(rdInt(data)));
}
else if (cmdId == ASYNC_PING_PEERS)
{
castPingForPeers();

View File

@ -120,10 +120,7 @@ signals:
void killCmd32(quint32 cmdId);
void asyncToPeers(quint16 cmdId, const QByteArray data);
void connectPeers(QSharedPointer<SessionCarrier> peer);
void setMaxSessions(quint32 value);
void ended();
void closeServer();
void resServer();
void killMods();
};

View File

@ -95,19 +95,10 @@ bool TCPServer::start()
close();
cleanupDbConnection();
Query db(this);
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_PORT);
db.addColumn(COLUMN_IPADDR);
db.addColumn(COLUMN_MAXSESSIONS);
db.exec();
maxSessions = db.getData(COLUMN_MAXSESSIONS).toUInt();
auto ret = false;
auto addr = db.getData(COLUMN_IPADDR).toString();
auto port = static_cast<quint16>(db.getData(COLUMN_PORT).toUInt());
auto conf = confObject();
auto addr = conf[CONF_LISTEN_ADDR].toString();
auto port = conf[CONF_LISTEN_PORT].toInt();
if (!createPipe())
{
@ -149,10 +140,6 @@ void TCPServer::sessionEnded()
{
closeServer();
}
else if ((count == 0) && (flags & RES_ON_EMPTY))
{
resServer();
}
else if (!(flags & (CLOSE_ON_EMPTY | RES_ON_EMPTY)) && !servOverloaded())
{
resumeAccepting();
@ -181,28 +168,12 @@ void TCPServer::closeServer()
}
}
void TCPServer::resServer()
{
if (rd32BitFromBlock(hostLoad) == 0)
{
controlPipe->close();
start();
}
else
{
flags |= RES_ON_EMPTY;
flags &= ~CLOSE_ON_EMPTY;
emit endAllSessions();
}
}
bool TCPServer::servOverloaded()
{
hostSharedMem->lock();
bool ret = rd32BitFromBlock(hostLoad) >= maxSessions;
auto confObj = confObject();
auto ret = rd32BitFromBlock(hostLoad) >= static_cast<quint32>(confObj[CONF_MAX_SESSIONS].toInt());
hostSharedMem->unlock();
@ -248,22 +219,16 @@ void TCPServer::procPipeIn()
QString text;
QTextStream txtOut(&text);
Query db(this);
db.setType(Query::PULL, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_IPADDR);
db.addColumn(COLUMN_PORT);
db.exec();
hostSharedMem->lock();
auto confObj = confObject();
txtOut << "" << Qt::endl;
txtOut << "Host Load: " << rd32BitFromBlock(hostLoad) << "/" << maxSessions << Qt::endl;
txtOut << "Address: " << serverAddress().toString() << Qt::endl;
txtOut << "Port: " << serverPort() << Qt::endl;
txtOut << "Working Path: " << QDir::currentPath() << Qt::endl;
txtOut << "SSL Chain: " << sslCertChain() << Qt::endl;
txtOut << "SSL Private: " << sslPrivKey() << Qt::endl << Qt::endl;
txtOut << "Host Load: " << rd32BitFromBlock(hostLoad) << "/" << confObj[CONF_MAX_SESSIONS].toInt() << Qt::endl;
txtOut << "Address: " << serverAddress().toString() << Qt::endl;
txtOut << "Port: " << serverPort() << Qt::endl;
txtOut << "SSL Chain: " << confObj[CONF_CERT_CHAIN].toString() << Qt::endl;
txtOut << "SSL Private: " << confObj[CONF_PRIV_KEY].toString() << Qt::endl << Qt::endl;
printDatabaseInfo(txtOut);
@ -272,17 +237,6 @@ void TCPServer::procPipeIn()
}
}
void TCPServer::setMaxSessions(quint32 value)
{
Query db(this);
db.setType(Query::UPDATE, TABLE_SERV_SETTINGS);
db.addColumn(COLUMN_MAXSESSIONS, value);
db.exec();
maxSessions = value;
}
void TCPServer::incomingConnection(qintptr socketDescriptor)
{
auto *soc = new QSslSocket(nullptr);
@ -316,9 +270,6 @@ void TCPServer::incomingConnection(qintptr socketDescriptor)
connect(ses, &Session::ended, this, &TCPServer::sessionEnded);
connect(ses, &Session::ended, thr, &QThread::quit);
connect(ses, &Session::connectPeers, this, &TCPServer::connectPeers);
connect(ses, &Session::closeServer, this, &TCPServer::closeServer);
connect(ses, &Session::resServer, this, &TCPServer::resServer);
connect(ses, &Session::setMaxSessions, this, &TCPServer::setMaxSessions);
connect(this, &TCPServer::connectPeers, ses, &Session::connectToPeer);
connect(this, &TCPServer::endAllSessions, ses, &Session::endSession);
@ -407,12 +358,14 @@ QString TCPServer::loadSSLData(bool onReload)
QString txtMsg;
QTextStream stream(&txtMsg);
auto chain = sslCertChain().split(":");
auto priv = sslPrivKey();
auto localObj = confObject();
auto privPath = localObj[CONF_PRIV_KEY].toString();
auto pubPath = localObj[CONF_CERT_CHAIN].toString();
auto chain = pubPath.split(":");
auto allCertsExists = true;
auto privKeyExists = QFile::exists(priv);
auto privKeyExists = QFile::exists(privPath);
stream << "Private key: " << priv << Qt::endl;
stream << "Private key: " << privPath << Qt::endl;
if (!privKeyExists)
{
@ -433,16 +386,19 @@ QString TCPServer::loadSSLData(bool onReload)
if (chain.isEmpty())
{
stream << "No cert files are defined in the env." << Qt::endl;
stream << "No cert files are defined in the conf file." << Qt::endl;
allCertsExists = false;
}
stream << Qt::endl;
auto defaultPriv = getLocalFilePath(DEFAULT_PRIV_FILENAME);
auto defaultCert = getLocalFilePath(DEFAULT_CERT_FILENAME);
if (allCertsExists && privKeyExists)
{
if (onReload && (priv == DEFAULT_PRIV_KEY_NAME) && (sslCertChain() == DEFAULT_PUB_KEY_NAME))
if (onReload && (privPath == defaultPriv) && (pubPath == defaultCert))
{
stream << "Re-generating self-signed cert." << Qt::endl;
@ -452,18 +408,18 @@ QString TCPServer::loadSSLData(bool onReload)
}
}
applyPrivKey(priv, stream);
applyPrivKey(privPath, stream);
applyCerts(chain, stream);
}
else if ((priv == DEFAULT_PRIV_KEY_NAME) && (sslCertChain() == DEFAULT_PUB_KEY_NAME))
else if ((privPath == defaultPriv) && (pubPath == defaultCert))
{
stream << "Generating self-signed cert." << Qt::endl;
if (genDefaultSSLFiles(wanIP, stream))
{
stream << Qt::endl << "The default self-signed cert files are generated successfully." << Qt::endl << Qt::endl;
stream << Qt::endl << "The default self-signed cert files were generated successfully." << Qt::endl << Qt::endl;
applyPrivKey(priv, stream);
applyPrivKey(privPath, stream);
applyCerts(chain, stream);
}
}

View File

@ -39,7 +39,6 @@ private:
QSslKey sslKey;
QString hostKey;
QString wanIP;
quint32 maxSessions;
quint32 flags;
QString loadSSLData(bool onReload);
@ -55,12 +54,10 @@ private slots:
void newPipeConnection();
void closedPipeConnection();
void sessionEnded();
void setMaxSessions(quint32 value);
void replyFromIpify(QNetworkReply *reply);
public slots:
void resServer();
void closeServer();
public: