smbclientng.core.CommandCompleter
1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# File name : CommandCompleter.py 4# Author : Podalirius (@podalirius_) 5# Date created : 20 may 2024 6 7 8import ntpath 9import os 10 11 12class CommandCompleter(object): 13 """ 14 A class to handle command completion for the smbclient-ng shell. 15 16 This class provides a command completion feature that suggests possible command names based on the current input. 17 It uses a dictionary to store commands and their descriptions, which helps in providing hints during the command line 18 interaction in the smbclient-ng shell. 19 20 Attributes: 21 smbSession (SMBSession): An instance of SMBSession which maintains the current SMB session. 22 commands (dict): A dictionary containing command names as keys and their descriptions and subcommands as values. 23 24 Methods: 25 __init__(self, smbSession): Initializes the CommandCompleter with an SMBSession. 26 """ 27 28 commands = { 29 "cd": { 30 "description": [ 31 "Change the current working directory.", 32 "Syntax: 'cd <directory>'" 33 ], 34 "subcommands": [] 35 }, 36 "close": { 37 "description": [ 38 "Closes the SMB connection to the remote machine.", 39 "Syntax: 'close'" 40 ], 41 "subcommands": [] 42 }, 43 "connect": { 44 "description": [ 45 "Connect to the remote machine (useful if connection timed out).", 46 "Syntax: 'connect'" 47 ], 48 "subcommands": [] 49 }, 50 "dir": { 51 "description": [ 52 "List the contents of the current working directory.", 53 "Syntax: 'dir'" 54 ], 55 "subcommands": [] 56 }, 57 "exit": { 58 "description": [ 59 "Exits the smbclient-ng script.", 60 "Syntax: 'exit'" 61 ], 62 "subcommands": [] 63 }, 64 "get": { 65 "description": [ 66 "Get a remote file.", 67 "Syntax: 'get [-r] <directory or file>'" 68 ], 69 "subcommands": [] 70 }, 71 "help": { 72 "description": [ 73 "Displays this help message.", 74 "Syntax: 'help'" 75 ], 76 "subcommands": ["format"] 77 }, 78 "info": { 79 "description": [ 80 "Get information about the server and or the share.", 81 "Syntax: 'info [server|share]'" 82 ], 83 "subcommands": ["server", "share"] 84 }, 85 "lcd": { 86 "description": [ 87 "Changes the current local directory.", 88 "Syntax: 'lcd <directory>'" 89 ], 90 "subcommands": [] 91 }, 92 "lls": { 93 "description": [ 94 "Lists the contents of the current local directory.", 95 "Syntax: 'lls'" 96 ], 97 "subcommands": [] 98 }, 99 "lmkdir": { 100 "description": [ 101 "Creates a new local directory.", 102 "Syntax: 'lmkdir <directory>'" 103 ], 104 "subcommands": [] 105 }, 106 "lpwd": { 107 "description": [ 108 "Shows the current local directory.", 109 "Syntax: 'lpwd'" 110 ], 111 "subcommands": [] 112 }, 113 "lrm": { 114 "description": [ 115 "Removes a local file.", 116 "Syntax: 'lrm <file>'" 117 ], 118 "subcommands": [] 119 }, 120 "lrmdir": { 121 "description": [ 122 "Removes a local directory.", 123 "Syntax: 'lrmdir <directory>'" 124 ], 125 "subcommands": [] 126 }, 127 "ls": { 128 "description": [ 129 "List the contents of the current remote working directory.", 130 "Syntax: 'ls'" 131 ], 132 "subcommands": [] 133 }, 134 "ltree": { 135 "description": [ 136 "Displays a tree view of the local directories.", 137 "Syntax: 'ltree [directory]'" 138 ], 139 "subcommands": [] 140 }, 141 "mkdir": { 142 "description": [ 143 "Creates a new remote directory.", 144 "Syntax: 'mkdir <directory>'" 145 ], 146 "subcommands": [] 147 }, 148 "module": { 149 "description": [ 150 "Loads a specific module for additional functionalities.", 151 "Syntax: 'module <name>'" 152 ], 153 "subcommands": [] 154 }, 155 "put": { 156 "description": [ 157 "Put a local file or directory in a remote directory.", 158 "Syntax: 'put [-r] <directory or file>'" 159 ], 160 "subcommands": [] 161 }, 162 "reconnect": { 163 "description": [ 164 "Reconnect to the remote machine (useful if connection timed out).", 165 "Syntax: 'reconnect'" 166 ], 167 "subcommands": [] 168 }, 169 "reset": { 170 "description": [ 171 "Reset the TTY output, useful if it was broken after printing a binary file on stdout.", 172 "Syntax: 'reset'" 173 ], 174 "subcommands": [] 175 }, 176 "rmdir": { 177 "description": [ 178 "Removes a remote directory.", 179 "Syntax: 'rmdir <directory>'" 180 ], 181 "subcommands": [] 182 }, 183 "rm": { 184 "description": [ 185 "Removes a remote file.", 186 "Syntax: 'rm <file>'" 187 ], 188 "subcommands": [] 189 }, 190 "shares": { 191 "description": [ 192 "Lists the SMB shares served by the remote machine.", 193 "Syntax: 'shares'" 194 ], 195 "subcommands": [] 196 }, 197 "use": { 198 "description": [ 199 "Use a SMB share.", 200 "Syntax: 'use <sharename>'" 201 ], 202 "subcommands": [] 203 }, 204 "tree": { 205 "description": [ 206 "Displays a tree view of the remote directories.", 207 "Syntax: 'tree [directory]'" 208 ], 209 "subcommands": [] 210 }, 211 } 212 213 def __init__(self, smbSession, config): 214 # Objects 215 self.smbSession = smbSession 216 self.config = config 217 # Pre computing for some commands 218 self.commands["help"]["subcommands"] = ["format"] + list(self.commands.keys()) 219 self.commands["help"]["subcommands"].remove("help") 220 221 def complete(self, text, state): 222 """ 223 Function to handle command completion in the LDAP console. 224 225 This function completes the user"s input based on the available options for commands in the LDAP console. 226 227 Args: 228 text (str): The current text input by the user. 229 state (int): The current state of completion. 230 231 Returns: 232 str: The next completion suggestion based on the user"s input state. 233 """ 234 235 if state == 0: 236 237 # No text typed yet, need the list of commands available 238 if len(text) == 0: 239 self.matches = [s for s in self.commands.keys()] 240 241 elif len(text) != 0: 242 # This is for the main command 243 if text.count(" ") == 0: 244 self.matches = [s for s in self.commands.keys() if s and s.startswith(text)] 245 246 # This is for subcommands 247 elif text.count(" ") >= 1: 248 command, remainder = text.split(" ", 1) 249 if command in self.commands.keys(): 250 if command == "use": 251 # Choose SMB Share to connect to 252 self.matches = [ 253 command + " " + s.lower() 254 for s in self.smbSession.list_shares().keys() 255 if s.lower().startswith(remainder.lower()) 256 ] 257 258 elif command in ["cd", "dir", "ls", "mkdir", "rmdir", "tree"]: 259 # Choose remote directory 260 path = "" 261 if '\\' in remainder.strip() or '/' in remainder.strip(): 262 path = remainder.strip().replace('/', ntpath.sep) 263 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) 264 265 directory_contents = self.smbSession.list_contents(path=path).items() 266 267 matching_entries = [] 268 for _, entry in directory_contents: 269 if entry.is_directory() and entry.get_longname() not in [".",".."]: 270 if len(path) != 0: 271 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) 272 else: 273 matching_entries.append(entry.get_longname() + ntpath.sep) 274 275 self.matches = [ 276 command + " " + s 277 for s in matching_entries 278 if s.lower().startswith(remainder.lower()) 279 ] 280 281 elif command in ["get", "rm"]: 282 # Choose local files and directories 283 path = "" 284 if '\\' in remainder.strip() or '/' in remainder.strip(): 285 path = remainder.strip().replace('/', ntpath.sep) 286 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) 287 288 directory_contents = self.smbSession.list_contents(path=path).items() 289 290 matching_entries = [] 291 for _, entry in directory_contents: 292 if entry.get_longname() not in [".",".."]: 293 if len(path) != 0: 294 if entry.is_directory(): 295 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) 296 else: 297 matching_entries.append(path + ntpath.sep + entry.get_longname()) 298 else: 299 if entry.is_directory(): 300 matching_entries.append(entry.get_longname() + ntpath.sep) 301 else: 302 matching_entries.append(entry.get_longname()) 303 304 self.matches = [ 305 command + " " + s 306 for s in matching_entries 307 if s.lower().startswith(remainder.lower()) 308 ] 309 310 elif command in ["lcd", "lls", "put", "lmkdir", "lrm", "lrmdir"]: 311 # Choose directory 312 path = "" 313 if os.path.sep in remainder.strip(): 314 path = path.split(os.path.sep)[:-1] 315 path = os.path.sep.join(path) 316 317 # Current dir 318 if len(path.strip()) == 0: 319 path = "." 320 321 directory_contents = os.listdir(path=path + os.path.sep) 322 matching_entries = [] 323 for entry in directory_contents: 324 if entry not in [".",".."]: 325 entry_path = path + os.path.sep + entry 326 if os.path.isdir(entry_path): 327 matching_entries.append(entry_path + os.path.sep) 328 else: 329 matching_entries.append(entry_path) 330 331 self.matches = [ 332 command + " " + s 333 for s in matching_entries 334 if s.startswith(remainder) 335 ] 336 337 else: 338 # Generic case for subcommands 339 self.matches = [ 340 command + " " + s 341 for s in self.commands[command]["subcommands"] 342 if s.startswith(remainder) 343 ] 344 else: 345 # Unknown subcommand, skipping autocomplete 346 pass 347 else: 348 self.matches = [] 349 else: 350 self.matches = self.commands.keys()[:] 351 352 try: 353 return self.matches[state] + " " 354 except IndexError: 355 return None 356 357 def print_help(self, command=None): 358 """ 359 Prints help information for a specific command or all commands if no command is specified. 360 361 This method displays the help information for the command passed as an argument. If no command is specified, 362 it prints the help information for all available commands. The help information includes the command syntax, 363 description, and any subcommands associated with it. This method is designed to provide users with the necessary 364 guidance on how to use the commands in the smbclient-ng shell. 365 366 Args: 367 command (str, optional): The command to display help information for. If None, help for all commands is displayed. 368 369 Returns: 370 None 371 """ 372 373 if command is not None: 374 if command not in list(self.commands.keys())+["format"]: 375 command = None 376 377 # Print help for a specific command 378 if command is not None: 379 if command == "format": 380 self.print_help_format() 381 else: 382 print("│") 383 if self.config.no_colors: 384 command_str = command + "─"* (15 - len(command)) 385 if len(self.commands[command]["description"]) == 0: 386 print("│ ■ %s┤ " % command_str) 387 elif len(self.commands[command]["description"]) == 1: 388 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) 389 else: 390 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) 391 for line in self.commands[command]["description"][1:]: 392 print("│ %s│ %s " % (" "*(15+3), line)) 393 else: 394 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" 395 if len(self.commands[command]["description"]) == 0: 396 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) 397 elif len(self.commands[command]["description"]) == 1: 398 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) 399 else: 400 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) 401 for line in self.commands[command]["description"][1:]: 402 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+2), line)) 403 print("│") 404 # Generic help 405 else: 406 print("│") 407 commands = sorted(self.commands.keys()) 408 for command in commands: 409 if self.config.no_colors: 410 command_str = command + "─"* (15 - len(command)) 411 if len(self.commands[command]["description"]) == 0: 412 print("│ ■ %s┤ " % command_str) 413 elif len(self.commands[command]["description"]) == 1: 414 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) 415 else: 416 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) 417 for line in self.commands[command]["description"][1:]: 418 print("│ %s│ %s " % (" "*(15+2), line)) 419 else: 420 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" 421 if len(self.commands[command]["description"]) == 0: 422 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) 423 elif len(self.commands[command]["description"]) == 1: 424 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) 425 else: 426 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) 427 for line in self.commands[command]["description"][1:]: 428 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line)) 429 print("│") 430 431 def print_help_format(self): 432 """ 433 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands. 434 435 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning 436 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory. 437 """ 438 439 print("File attributes format:\n") 440 print("\x1b[1mdachnrst\x1b[0m") 441 print("\x1b[90m│││││││└──>\x1b[0m Temporary") 442 print("\x1b[90m││││││└───>\x1b[0m System") 443 print("\x1b[90m│││││└────>\x1b[0m Read-Only") 444 print("\x1b[90m││││└─────>\x1b[0m Normal") 445 print("\x1b[90m│││└──────>\x1b[0m Hidden") 446 print("\x1b[90m││└───────>\x1b[0m Compressed") 447 print("\x1b[90m│└────────>\x1b[0m Archived") 448 print("\x1b[90m└─────────>\x1b[0m Directory")
13class CommandCompleter(object): 14 """ 15 A class to handle command completion for the smbclient-ng shell. 16 17 This class provides a command completion feature that suggests possible command names based on the current input. 18 It uses a dictionary to store commands and their descriptions, which helps in providing hints during the command line 19 interaction in the smbclient-ng shell. 20 21 Attributes: 22 smbSession (SMBSession): An instance of SMBSession which maintains the current SMB session. 23 commands (dict): A dictionary containing command names as keys and their descriptions and subcommands as values. 24 25 Methods: 26 __init__(self, smbSession): Initializes the CommandCompleter with an SMBSession. 27 """ 28 29 commands = { 30 "cd": { 31 "description": [ 32 "Change the current working directory.", 33 "Syntax: 'cd <directory>'" 34 ], 35 "subcommands": [] 36 }, 37 "close": { 38 "description": [ 39 "Closes the SMB connection to the remote machine.", 40 "Syntax: 'close'" 41 ], 42 "subcommands": [] 43 }, 44 "connect": { 45 "description": [ 46 "Connect to the remote machine (useful if connection timed out).", 47 "Syntax: 'connect'" 48 ], 49 "subcommands": [] 50 }, 51 "dir": { 52 "description": [ 53 "List the contents of the current working directory.", 54 "Syntax: 'dir'" 55 ], 56 "subcommands": [] 57 }, 58 "exit": { 59 "description": [ 60 "Exits the smbclient-ng script.", 61 "Syntax: 'exit'" 62 ], 63 "subcommands": [] 64 }, 65 "get": { 66 "description": [ 67 "Get a remote file.", 68 "Syntax: 'get [-r] <directory or file>'" 69 ], 70 "subcommands": [] 71 }, 72 "help": { 73 "description": [ 74 "Displays this help message.", 75 "Syntax: 'help'" 76 ], 77 "subcommands": ["format"] 78 }, 79 "info": { 80 "description": [ 81 "Get information about the server and or the share.", 82 "Syntax: 'info [server|share]'" 83 ], 84 "subcommands": ["server", "share"] 85 }, 86 "lcd": { 87 "description": [ 88 "Changes the current local directory.", 89 "Syntax: 'lcd <directory>'" 90 ], 91 "subcommands": [] 92 }, 93 "lls": { 94 "description": [ 95 "Lists the contents of the current local directory.", 96 "Syntax: 'lls'" 97 ], 98 "subcommands": [] 99 }, 100 "lmkdir": { 101 "description": [ 102 "Creates a new local directory.", 103 "Syntax: 'lmkdir <directory>'" 104 ], 105 "subcommands": [] 106 }, 107 "lpwd": { 108 "description": [ 109 "Shows the current local directory.", 110 "Syntax: 'lpwd'" 111 ], 112 "subcommands": [] 113 }, 114 "lrm": { 115 "description": [ 116 "Removes a local file.", 117 "Syntax: 'lrm <file>'" 118 ], 119 "subcommands": [] 120 }, 121 "lrmdir": { 122 "description": [ 123 "Removes a local directory.", 124 "Syntax: 'lrmdir <directory>'" 125 ], 126 "subcommands": [] 127 }, 128 "ls": { 129 "description": [ 130 "List the contents of the current remote working directory.", 131 "Syntax: 'ls'" 132 ], 133 "subcommands": [] 134 }, 135 "ltree": { 136 "description": [ 137 "Displays a tree view of the local directories.", 138 "Syntax: 'ltree [directory]'" 139 ], 140 "subcommands": [] 141 }, 142 "mkdir": { 143 "description": [ 144 "Creates a new remote directory.", 145 "Syntax: 'mkdir <directory>'" 146 ], 147 "subcommands": [] 148 }, 149 "module": { 150 "description": [ 151 "Loads a specific module for additional functionalities.", 152 "Syntax: 'module <name>'" 153 ], 154 "subcommands": [] 155 }, 156 "put": { 157 "description": [ 158 "Put a local file or directory in a remote directory.", 159 "Syntax: 'put [-r] <directory or file>'" 160 ], 161 "subcommands": [] 162 }, 163 "reconnect": { 164 "description": [ 165 "Reconnect to the remote machine (useful if connection timed out).", 166 "Syntax: 'reconnect'" 167 ], 168 "subcommands": [] 169 }, 170 "reset": { 171 "description": [ 172 "Reset the TTY output, useful if it was broken after printing a binary file on stdout.", 173 "Syntax: 'reset'" 174 ], 175 "subcommands": [] 176 }, 177 "rmdir": { 178 "description": [ 179 "Removes a remote directory.", 180 "Syntax: 'rmdir <directory>'" 181 ], 182 "subcommands": [] 183 }, 184 "rm": { 185 "description": [ 186 "Removes a remote file.", 187 "Syntax: 'rm <file>'" 188 ], 189 "subcommands": [] 190 }, 191 "shares": { 192 "description": [ 193 "Lists the SMB shares served by the remote machine.", 194 "Syntax: 'shares'" 195 ], 196 "subcommands": [] 197 }, 198 "use": { 199 "description": [ 200 "Use a SMB share.", 201 "Syntax: 'use <sharename>'" 202 ], 203 "subcommands": [] 204 }, 205 "tree": { 206 "description": [ 207 "Displays a tree view of the remote directories.", 208 "Syntax: 'tree [directory]'" 209 ], 210 "subcommands": [] 211 }, 212 } 213 214 def __init__(self, smbSession, config): 215 # Objects 216 self.smbSession = smbSession 217 self.config = config 218 # Pre computing for some commands 219 self.commands["help"]["subcommands"] = ["format"] + list(self.commands.keys()) 220 self.commands["help"]["subcommands"].remove("help") 221 222 def complete(self, text, state): 223 """ 224 Function to handle command completion in the LDAP console. 225 226 This function completes the user"s input based on the available options for commands in the LDAP console. 227 228 Args: 229 text (str): The current text input by the user. 230 state (int): The current state of completion. 231 232 Returns: 233 str: The next completion suggestion based on the user"s input state. 234 """ 235 236 if state == 0: 237 238 # No text typed yet, need the list of commands available 239 if len(text) == 0: 240 self.matches = [s for s in self.commands.keys()] 241 242 elif len(text) != 0: 243 # This is for the main command 244 if text.count(" ") == 0: 245 self.matches = [s for s in self.commands.keys() if s and s.startswith(text)] 246 247 # This is for subcommands 248 elif text.count(" ") >= 1: 249 command, remainder = text.split(" ", 1) 250 if command in self.commands.keys(): 251 if command == "use": 252 # Choose SMB Share to connect to 253 self.matches = [ 254 command + " " + s.lower() 255 for s in self.smbSession.list_shares().keys() 256 if s.lower().startswith(remainder.lower()) 257 ] 258 259 elif command in ["cd", "dir", "ls", "mkdir", "rmdir", "tree"]: 260 # Choose remote directory 261 path = "" 262 if '\\' in remainder.strip() or '/' in remainder.strip(): 263 path = remainder.strip().replace('/', ntpath.sep) 264 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) 265 266 directory_contents = self.smbSession.list_contents(path=path).items() 267 268 matching_entries = [] 269 for _, entry in directory_contents: 270 if entry.is_directory() and entry.get_longname() not in [".",".."]: 271 if len(path) != 0: 272 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) 273 else: 274 matching_entries.append(entry.get_longname() + ntpath.sep) 275 276 self.matches = [ 277 command + " " + s 278 for s in matching_entries 279 if s.lower().startswith(remainder.lower()) 280 ] 281 282 elif command in ["get", "rm"]: 283 # Choose local files and directories 284 path = "" 285 if '\\' in remainder.strip() or '/' in remainder.strip(): 286 path = remainder.strip().replace('/', ntpath.sep) 287 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) 288 289 directory_contents = self.smbSession.list_contents(path=path).items() 290 291 matching_entries = [] 292 for _, entry in directory_contents: 293 if entry.get_longname() not in [".",".."]: 294 if len(path) != 0: 295 if entry.is_directory(): 296 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) 297 else: 298 matching_entries.append(path + ntpath.sep + entry.get_longname()) 299 else: 300 if entry.is_directory(): 301 matching_entries.append(entry.get_longname() + ntpath.sep) 302 else: 303 matching_entries.append(entry.get_longname()) 304 305 self.matches = [ 306 command + " " + s 307 for s in matching_entries 308 if s.lower().startswith(remainder.lower()) 309 ] 310 311 elif command in ["lcd", "lls", "put", "lmkdir", "lrm", "lrmdir"]: 312 # Choose directory 313 path = "" 314 if os.path.sep in remainder.strip(): 315 path = path.split(os.path.sep)[:-1] 316 path = os.path.sep.join(path) 317 318 # Current dir 319 if len(path.strip()) == 0: 320 path = "." 321 322 directory_contents = os.listdir(path=path + os.path.sep) 323 matching_entries = [] 324 for entry in directory_contents: 325 if entry not in [".",".."]: 326 entry_path = path + os.path.sep + entry 327 if os.path.isdir(entry_path): 328 matching_entries.append(entry_path + os.path.sep) 329 else: 330 matching_entries.append(entry_path) 331 332 self.matches = [ 333 command + " " + s 334 for s in matching_entries 335 if s.startswith(remainder) 336 ] 337 338 else: 339 # Generic case for subcommands 340 self.matches = [ 341 command + " " + s 342 for s in self.commands[command]["subcommands"] 343 if s.startswith(remainder) 344 ] 345 else: 346 # Unknown subcommand, skipping autocomplete 347 pass 348 else: 349 self.matches = [] 350 else: 351 self.matches = self.commands.keys()[:] 352 353 try: 354 return self.matches[state] + " " 355 except IndexError: 356 return None 357 358 def print_help(self, command=None): 359 """ 360 Prints help information for a specific command or all commands if no command is specified. 361 362 This method displays the help information for the command passed as an argument. If no command is specified, 363 it prints the help information for all available commands. The help information includes the command syntax, 364 description, and any subcommands associated with it. This method is designed to provide users with the necessary 365 guidance on how to use the commands in the smbclient-ng shell. 366 367 Args: 368 command (str, optional): The command to display help information for. If None, help for all commands is displayed. 369 370 Returns: 371 None 372 """ 373 374 if command is not None: 375 if command not in list(self.commands.keys())+["format"]: 376 command = None 377 378 # Print help for a specific command 379 if command is not None: 380 if command == "format": 381 self.print_help_format() 382 else: 383 print("│") 384 if self.config.no_colors: 385 command_str = command + "─"* (15 - len(command)) 386 if len(self.commands[command]["description"]) == 0: 387 print("│ ■ %s┤ " % command_str) 388 elif len(self.commands[command]["description"]) == 1: 389 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) 390 else: 391 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) 392 for line in self.commands[command]["description"][1:]: 393 print("│ %s│ %s " % (" "*(15+3), line)) 394 else: 395 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" 396 if len(self.commands[command]["description"]) == 0: 397 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) 398 elif len(self.commands[command]["description"]) == 1: 399 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) 400 else: 401 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) 402 for line in self.commands[command]["description"][1:]: 403 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+2), line)) 404 print("│") 405 # Generic help 406 else: 407 print("│") 408 commands = sorted(self.commands.keys()) 409 for command in commands: 410 if self.config.no_colors: 411 command_str = command + "─"* (15 - len(command)) 412 if len(self.commands[command]["description"]) == 0: 413 print("│ ■ %s┤ " % command_str) 414 elif len(self.commands[command]["description"]) == 1: 415 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) 416 else: 417 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) 418 for line in self.commands[command]["description"][1:]: 419 print("│ %s│ %s " % (" "*(15+2), line)) 420 else: 421 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" 422 if len(self.commands[command]["description"]) == 0: 423 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) 424 elif len(self.commands[command]["description"]) == 1: 425 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) 426 else: 427 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) 428 for line in self.commands[command]["description"][1:]: 429 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line)) 430 print("│") 431 432 def print_help_format(self): 433 """ 434 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands. 435 436 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning 437 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory. 438 """ 439 440 print("File attributes format:\n") 441 print("\x1b[1mdachnrst\x1b[0m") 442 print("\x1b[90m│││││││└──>\x1b[0m Temporary") 443 print("\x1b[90m││││││└───>\x1b[0m System") 444 print("\x1b[90m│││││└────>\x1b[0m Read-Only") 445 print("\x1b[90m││││└─────>\x1b[0m Normal") 446 print("\x1b[90m│││└──────>\x1b[0m Hidden") 447 print("\x1b[90m││└───────>\x1b[0m Compressed") 448 print("\x1b[90m│└────────>\x1b[0m Archived") 449 print("\x1b[90m└─────────>\x1b[0m Directory")
A class to handle command completion for the smbclient-ng shell.
This class provides a command completion feature that suggests possible command names based on the current input. It uses a dictionary to store commands and their descriptions, which helps in providing hints during the command line interaction in the smbclient-ng shell.
Attributes: smbSession (SMBSession): An instance of SMBSession which maintains the current SMB session. commands (dict): A dictionary containing command names as keys and their descriptions and subcommands as values.
Methods: __init__(self, smbSession): Initializes the CommandCompleter with an SMBSession.
222 def complete(self, text, state): 223 """ 224 Function to handle command completion in the LDAP console. 225 226 This function completes the user"s input based on the available options for commands in the LDAP console. 227 228 Args: 229 text (str): The current text input by the user. 230 state (int): The current state of completion. 231 232 Returns: 233 str: The next completion suggestion based on the user"s input state. 234 """ 235 236 if state == 0: 237 238 # No text typed yet, need the list of commands available 239 if len(text) == 0: 240 self.matches = [s for s in self.commands.keys()] 241 242 elif len(text) != 0: 243 # This is for the main command 244 if text.count(" ") == 0: 245 self.matches = [s for s in self.commands.keys() if s and s.startswith(text)] 246 247 # This is for subcommands 248 elif text.count(" ") >= 1: 249 command, remainder = text.split(" ", 1) 250 if command in self.commands.keys(): 251 if command == "use": 252 # Choose SMB Share to connect to 253 self.matches = [ 254 command + " " + s.lower() 255 for s in self.smbSession.list_shares().keys() 256 if s.lower().startswith(remainder.lower()) 257 ] 258 259 elif command in ["cd", "dir", "ls", "mkdir", "rmdir", "tree"]: 260 # Choose remote directory 261 path = "" 262 if '\\' in remainder.strip() or '/' in remainder.strip(): 263 path = remainder.strip().replace('/', ntpath.sep) 264 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) 265 266 directory_contents = self.smbSession.list_contents(path=path).items() 267 268 matching_entries = [] 269 for _, entry in directory_contents: 270 if entry.is_directory() and entry.get_longname() not in [".",".."]: 271 if len(path) != 0: 272 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) 273 else: 274 matching_entries.append(entry.get_longname() + ntpath.sep) 275 276 self.matches = [ 277 command + " " + s 278 for s in matching_entries 279 if s.lower().startswith(remainder.lower()) 280 ] 281 282 elif command in ["get", "rm"]: 283 # Choose local files and directories 284 path = "" 285 if '\\' in remainder.strip() or '/' in remainder.strip(): 286 path = remainder.strip().replace('/', ntpath.sep) 287 path = ntpath.sep.join(path.split(ntpath.sep)[:-1]) 288 289 directory_contents = self.smbSession.list_contents(path=path).items() 290 291 matching_entries = [] 292 for _, entry in directory_contents: 293 if entry.get_longname() not in [".",".."]: 294 if len(path) != 0: 295 if entry.is_directory(): 296 matching_entries.append(path + ntpath.sep + entry.get_longname() + ntpath.sep) 297 else: 298 matching_entries.append(path + ntpath.sep + entry.get_longname()) 299 else: 300 if entry.is_directory(): 301 matching_entries.append(entry.get_longname() + ntpath.sep) 302 else: 303 matching_entries.append(entry.get_longname()) 304 305 self.matches = [ 306 command + " " + s 307 for s in matching_entries 308 if s.lower().startswith(remainder.lower()) 309 ] 310 311 elif command in ["lcd", "lls", "put", "lmkdir", "lrm", "lrmdir"]: 312 # Choose directory 313 path = "" 314 if os.path.sep in remainder.strip(): 315 path = path.split(os.path.sep)[:-1] 316 path = os.path.sep.join(path) 317 318 # Current dir 319 if len(path.strip()) == 0: 320 path = "." 321 322 directory_contents = os.listdir(path=path + os.path.sep) 323 matching_entries = [] 324 for entry in directory_contents: 325 if entry not in [".",".."]: 326 entry_path = path + os.path.sep + entry 327 if os.path.isdir(entry_path): 328 matching_entries.append(entry_path + os.path.sep) 329 else: 330 matching_entries.append(entry_path) 331 332 self.matches = [ 333 command + " " + s 334 for s in matching_entries 335 if s.startswith(remainder) 336 ] 337 338 else: 339 # Generic case for subcommands 340 self.matches = [ 341 command + " " + s 342 for s in self.commands[command]["subcommands"] 343 if s.startswith(remainder) 344 ] 345 else: 346 # Unknown subcommand, skipping autocomplete 347 pass 348 else: 349 self.matches = [] 350 else: 351 self.matches = self.commands.keys()[:] 352 353 try: 354 return self.matches[state] + " " 355 except IndexError: 356 return None
Function to handle command completion in the LDAP console.
This function completes the user"s input based on the available options for commands in the LDAP console.
Args: text (str): The current text input by the user. state (int): The current state of completion.
Returns: str: The next completion suggestion based on the user"s input state.
358 def print_help(self, command=None): 359 """ 360 Prints help information for a specific command or all commands if no command is specified. 361 362 This method displays the help information for the command passed as an argument. If no command is specified, 363 it prints the help information for all available commands. The help information includes the command syntax, 364 description, and any subcommands associated with it. This method is designed to provide users with the necessary 365 guidance on how to use the commands in the smbclient-ng shell. 366 367 Args: 368 command (str, optional): The command to display help information for. If None, help for all commands is displayed. 369 370 Returns: 371 None 372 """ 373 374 if command is not None: 375 if command not in list(self.commands.keys())+["format"]: 376 command = None 377 378 # Print help for a specific command 379 if command is not None: 380 if command == "format": 381 self.print_help_format() 382 else: 383 print("│") 384 if self.config.no_colors: 385 command_str = command + "─"* (15 - len(command)) 386 if len(self.commands[command]["description"]) == 0: 387 print("│ ■ %s┤ " % command_str) 388 elif len(self.commands[command]["description"]) == 1: 389 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) 390 else: 391 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) 392 for line in self.commands[command]["description"][1:]: 393 print("│ %s│ %s " % (" "*(15+3), line)) 394 else: 395 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" 396 if len(self.commands[command]["description"]) == 0: 397 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) 398 elif len(self.commands[command]["description"]) == 1: 399 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) 400 else: 401 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) 402 for line in self.commands[command]["description"][1:]: 403 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+2), line)) 404 print("│") 405 # Generic help 406 else: 407 print("│") 408 commands = sorted(self.commands.keys()) 409 for command in commands: 410 if self.config.no_colors: 411 command_str = command + "─"* (15 - len(command)) 412 if len(self.commands[command]["description"]) == 0: 413 print("│ ■ %s┤ " % command_str) 414 elif len(self.commands[command]["description"]) == 1: 415 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) 416 else: 417 print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0])) 418 for line in self.commands[command]["description"][1:]: 419 print("│ %s│ %s " % (" "*(15+2), line)) 420 else: 421 command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m" 422 if len(self.commands[command]["description"]) == 0: 423 print("│ ■ %s\x1b[90m┤\x1b[0m " % command_str) 424 elif len(self.commands[command]["description"]) == 1: 425 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) 426 else: 427 print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0])) 428 for line in self.commands[command]["description"][1:]: 429 print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line)) 430 print("│")
Prints help information for a specific command or all commands if no command is specified.
This method displays the help information for the command passed as an argument. If no command is specified, it prints the help information for all available commands. The help information includes the command syntax, description, and any subcommands associated with it. This method is designed to provide users with the necessary guidance on how to use the commands in the smbclient-ng shell.
Args: command (str, optional): The command to display help information for. If None, help for all commands is displayed.
Returns: None
432 def print_help_format(self): 433 """ 434 Prints the help information for the 'format' used in remote 'ls' and 'dir' commands. 435 436 This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning 437 of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory. 438 """ 439 440 print("File attributes format:\n") 441 print("\x1b[1mdachnrst\x1b[0m") 442 print("\x1b[90m│││││││└──>\x1b[0m Temporary") 443 print("\x1b[90m││││││└───>\x1b[0m System") 444 print("\x1b[90m│││││└────>\x1b[0m Read-Only") 445 print("\x1b[90m││││└─────>\x1b[0m Normal") 446 print("\x1b[90m│││└──────>\x1b[0m Hidden") 447 print("\x1b[90m││└───────>\x1b[0m Compressed") 448 print("\x1b[90m│└────────>\x1b[0m Archived") 449 print("\x1b[90m└─────────>\x1b[0m Directory")
Prints the help information for the 'format' used in remote 'ls' and 'dir' commands.
This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory.