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