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