From 95dcfef7b2b564111fec931ceed6a999f230951b Mon Sep 17 00:00:00 2001 From: "Remi GASCOU (Podalirius)" <79218792+p0dalirius@users.noreply.github.com> Date: Fri, 31 May 2024 21:24:45 +0200 Subject: [PATCH] Implemented 'ltree' command --- smbclientng/core/CommandCompleter.py | 6 +- smbclientng/core/InteractiveShell.py | 26 ++++-- smbclientng/core/utils.py | 116 ++++++++++++++++++++++++++- 3 files changed, 139 insertions(+), 9 deletions(-) diff --git a/smbclientng/core/CommandCompleter.py b/smbclientng/core/CommandCompleter.py index a8f8ed3..befff6e 100644 --- a/smbclientng/core/CommandCompleter.py +++ b/smbclientng/core/CommandCompleter.py @@ -86,6 +86,10 @@ class CommandCompleter(object): "description": ["List the contents of the current remote working directory.", "Syntax: 'ls'"], "subcommands": [] }, + "ltree": { + "description": ["Displays a tree view of the local directories.", "Syntax: ltree [directory]"], + "subcommands": [] + }, "mkdir": { "description": ["Creates a new remote directory.", "Syntax: 'mkdir '"], "subcommands": [] @@ -123,7 +127,7 @@ class CommandCompleter(object): "subcommands": [] }, "tree": { - "description": ["Displays a tree view of the nested subfolders.", "Syntax: tree [directory]"], + "description": ["Displays a tree view of the remote directories.", "Syntax: tree [directory]"], "subcommands": [] }, } diff --git a/smbclientng/core/InteractiveShell.py b/smbclientng/core/InteractiveShell.py index 7e03cd6..92efe18 100644 --- a/smbclientng/core/InteractiveShell.py +++ b/smbclientng/core/InteractiveShell.py @@ -17,7 +17,7 @@ from rich.console import Console from rich.table import Table from smbclientng.core.CommandCompleter import CommandCompleter -from smbclientng.core.utils import b_filesize, unix_permissions, windows_ls_entry +from smbclientng.core.utils import b_filesize, unix_permissions, windows_ls_entry, local_tree ## Decorators @@ -172,6 +172,10 @@ def process_command(self, command, arguments=[]): elif command == "lmkdir": self.command_lmkdir(arguments, command) + # Shows the current local directory + elif command == "lpwd": + self.command_lpwd(arguments, command) + # Removes a local file elif command == "lrm": self.command_lrm(arguments, command) @@ -181,10 +185,10 @@ def process_command(self, command, arguments=[]): self.command_lrmdir(arguments, command) # Shows the current local directory - elif command == "lpwd": - self.command_lpwd(arguments, command) + elif command == "ltree": + self.command_ltree(arguments, command) - # + # Modules elif command == "module": self.command_module(arguments, command) @@ -373,6 +377,13 @@ def command_lmkdir(self, arguments, command): if not os.path.exists(tmp_path): os.mkdir(path=tmp_path) + def command_lpwd(self, arguments, command): + # Command arguments required : No + # Active SMB connection needed : No + # SMB share needed : No + + print(os.getcwd()) + @command_arguments_required def command_lrm(self, arguments, command): # Command arguments required : Yes @@ -409,12 +420,15 @@ def command_lrmdir(self, arguments, command): else: print("[!] Path '%s' does not exist." % path) - def command_lpwd(self, arguments, command): + def command_ltree(self, arguments, command): # Command arguments required : No # Active SMB connection needed : No # SMB share needed : No - print(os.getcwd()) + if len(arguments) == 0: + local_tree(path='.', config=self.config) + else: + local_tree(path=' '.join(arguments), config=self.config) @active_smb_connection_needed @smb_share_is_set diff --git a/smbclientng/core/utils.py b/smbclientng/core/utils.py index b85cf56..8b46f9c 100644 --- a/smbclientng/core/utils.py +++ b/smbclientng/core/utils.py @@ -149,7 +149,7 @@ def windows_ls_entry(entry, config, pathToPrint=None): """ if pathToPrint is None: - pathToPrint = entry.get_longname() + pathToPrint = entry meta_string = "" meta_string += ("d" if entry.is_directory() else "-") @@ -174,4 +174,116 @@ def windows_ls_entry(entry, config, pathToPrint=None): if config.no_colors: print("%s %10s %s %s" % (meta_string, size_str, date_str, pathToPrint)) else: - print("%s %10s %s \x1b[1m%s\x1b[0m" % (meta_string, size_str, date_str, pathToPrint)) \ No newline at end of file + print("%s %10s %s \x1b[1m%s\x1b[0m" % (meta_string, size_str, date_str, pathToPrint)) + + +def local_tree(path, config): + def recurse_action(base_dir="", path=[], prompt=[]): + bars = ["│ ", "├── ", "└── "] + + local_path = os.path.normpath(base_dir + os.path.sep + os.path.sep.join(path) + os.path.sep) + + entries = [] + try: + entries = os.listdir(local_path) + except Exception as err: + if config.no_colors: + print("%s%s" % (''.join(prompt+[bars[2]]), err)) + else: + print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), err)) + return + + entries = sorted(entries) + + # + if len(entries) > 1: + index = 0 + for entry in entries: + index += 1 + # This is the first entry + if index == 0: + if os.path.isdir(local_path + os.path.sep + entry): + if config.no_colors: + print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep)) + else: + print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep)) + recurse_action( + base_dir=base_dir, + path=path+[entry], + prompt=prompt+["│ "] + ) + else: + if config.no_colors: + print("%s%s" % (''.join(prompt+[bars[1]]), entry)) + else: + print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry)) + + # This is the last entry + elif index == len(entries): + if os.path.isdir(local_path + os.path.sep + entry): + if config.no_colors: + print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep)) + else: + print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep)) + recurse_action( + base_dir=base_dir, + path=path+[entry], + prompt=prompt+[" "] + ) + else: + if config.no_colors: + print("%s%s" % (''.join(prompt+[bars[2]]), entry)) + else: + print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry)) + + # These are entries in the middle + else: + if os.path.isdir(local_path + os.path.sep + entry): + if config.no_colors: + print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep)) + else: + print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep)) + recurse_action( + base_dir=base_dir, + path=path+[entry], + prompt=prompt+["│ "] + ) + else: + if config.no_colors: + print("%s%s" % (''.join(prompt+[bars[1]]), entry)) + else: + print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry)) + + # + elif len(entries) == 1: + entry = entries[0] + if os.path.isdir(local_path + os.path.sep + entry): + if config.no_colors: + print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep)) + else: + print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep)) + recurse_action( + base_dir=base_dir, + path=path+[entry], + prompt=prompt+[" "] + ) + else: + if config.no_colors: + print("%s%s" % (''.join(prompt+[bars[2]]), entry)) + else: + print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry)) + + # Entrypoint + try: + if config.no_colors: + print("%s%s" % (path, os.path.sep)) + else: + print("\x1b[1;96m%s\x1b[0m%s" % (path, os.path.sep)) + recurse_action( + base_dir=os.getcwd(), + path=[path], + prompt=[""] + ) + except (BrokenPipeError, KeyboardInterrupt) as e: + print("[!] Interrupted.") +