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

CommandCompleter(smbSession, config, logger)
329    def __init__(self, smbSession, config, logger):
330        # Objects
331        self.smbSession = smbSession
332        self.config = config
333        self.logger = logger
334        # Pre computing for some commands 
335        self.commands["help"]["subcommands"] = ["format"] + list(self.commands.keys())
336        self.commands["help"]["subcommands"].remove("help")
commands = {'bat': {'description': ['Pretty prints the contents of a remote file.', "Syntax: 'bat <file>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'cat': {'description': ['Get the contents of a remote file.', "Syntax: 'cat <file>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'cd': {'description': ['Change the current working directory.', "Syntax: 'cd <directory>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'close': {'description': ['Closes the SMB connection to the remote machine.', "Syntax: 'close'"], 'subcommands': [], 'autocomplete': []}, 'connect': {'description': ['Connect to the remote machine (useful if connection timed out).', "Syntax: 'connect'"], 'subcommands': [], 'autocomplete': []}, 'debug': {'description': ['Command for dev debugging.', "Syntax: 'debug'"], 'subcommands': [], 'autocomplete': []}, 'dir': {'description': ['List the contents of the current working directory.', "Syntax: 'dir'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'exit': {'description': ['Exits the smbclient-ng script.', "Syntax: 'exit'"], 'subcommands': [], 'autocomplete': []}, 'get': {'description': ['Get a remote file.', "Syntax: 'get [-r] <directory or file>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'help': {'description': ['Displays this help message.', "Syntax: 'help'"], 'subcommands': ['format'], 'autocomplete': []}, 'info': {'description': ['Get information about the server and or the share.', "Syntax: 'info [server|share]'"], 'subcommands': ['server', 'share'], 'autocomplete': []}, 'lbat': {'description': ['Pretty prints the contents of a local file.', "Syntax: 'lbat <file>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'lcat': {'description': ['Print the contents of a local file.', "Syntax: 'lcat <file>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'lcd': {'description': ['Changes the current local directory.', "Syntax: 'lcd <directory>'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'lcp': {'description': ['Create a copy of a local file.', "Syntax: 'lcp <srcfile> <dstfile>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'lls': {'description': ['Lists the contents of the current local directory.', "Syntax: 'lls'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'lmkdir': {'description': ['Creates a new local directory.', "Syntax: 'lmkdir <directory>'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'lpwd': {'description': ['Shows the current local directory.', "Syntax: 'lpwd'"], 'subcommands': [], 'autocomplete': []}, 'lrename': {'description': ['Renames a local file.', "Syntax: 'lrename <oldfilename> <newfilename>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'lrm': {'description': ['Removes a local file.', "Syntax: 'lrm <file>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'lrmdir': {'description': ['Removes a local directory.', "Syntax: 'lrmdir <directory>'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'ls': {'description': ['List the contents of the current remote working directory.', "Syntax: 'ls'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'ltree': {'description': ['Displays a tree view of the local directories.', "Syntax: 'ltree [directory]'"], 'subcommands': [], 'autocomplete': ['local_directory']}, 'mkdir': {'description': ['Creates a new remote directory.', "Syntax: 'mkdir <directory>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'module': {'description': ['Loads a specific module for additional functionalities.', "Syntax: 'module <name>'"], 'subcommands': [], 'autocomplete': []}, 'mount': {'description': ['Creates a mount point of the remote share on the local machine.', "Syntax: 'mount <remote_path> <local_mountpoint>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'put': {'description': ['Put a local file or directory in a remote directory.', "Syntax: 'put [-r] <directory or file>'"], 'subcommands': [], 'autocomplete': ['local_file']}, 'reconnect': {'description': ['Reconnect to the remote machine (useful if connection timed out).', "Syntax: 'reconnect'"], 'subcommands': [], 'autocomplete': []}, 'reset': {'description': ['Reset the TTY output, useful if it was broken after printing a binary file on stdout.', "Syntax: 'reset'"], 'subcommands': [], 'autocomplete': []}, 'rmdir': {'description': ['Removes a remote directory.', "Syntax: 'rmdir <directory>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'rm': {'description': ['Removes a remote file.', "Syntax: 'rm <file>'"], 'subcommands': [], 'autocomplete': ['remote_file']}, 'sizeof': {'description': ['Recursively compute the size of a folder.', "Syntax: 'sizeof [directory|file]'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'sessions': {'description': ['Manage the SMB sessions.', "Syntax: 'sessions [access|create|delete|execute|list]'"], 'subcommands': ['create', 'delete', 'execute', 'interact', 'list'], 'autocomplete': []}, 'shares': {'description': ['Lists the SMB shares served by the remote machine.', "Syntax: 'shares'"], 'subcommands': ['rights'], 'autocomplete': []}, 'tree': {'description': ['Displays a tree view of the remote directories.', "Syntax: 'tree [directory]'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'umount': {'description': ['Removes a mount point of the remote share on the local machine.', "Syntax: 'umount <local_mount_point>'"], 'subcommands': [], 'autocomplete': ['remote_directory']}, 'use': {'description': ['Use a SMB share.', "Syntax: 'use <sharename>'"], 'subcommands': [], 'autocomplete': ['share']}}
smbSession
config
logger
def complete(self, text, state):
338    def complete(self, text, state):
339        """
340        Function to handle command completion in the LDAP console.
341
342        This function completes the user"s input based on the available options for commands in the LDAP console.
343
344        Args:
345            text (str): The current text input by the user.
346            state (int): The current state of completion.
347
348        Returns:
349            str: The next completion suggestion based on the user"s input state.
350        """
351
352        if state == 0:
353            
354            # No text typed yet, need the list of commands available
355            if len(text) == 0:
356                self.matches = [s for s in self.commands.keys()]
357            
358            # Parsing a command
359            elif len(text) != 0:
360                # This is for the main command
361                if text.count(" ") == 0:
362                    self.matches = [s for s in self.commands.keys() if s and s.startswith(text)]
363                
364                # This is for subcommands
365                elif text.count(" ") >= 1:
366                    command, remainder = text.split(" ", 1)
367
368                    if command in self.commands.keys():
369                        self.matches = []
370
371                        # Autocomplete shares
372                        if "share" in self.commands[command]["autocomplete"]:
373                            # Choose SMB Share to connect to
374                            shares = self.smbSession.list_shares()
375                            matching_entries = []
376                            for sharename in shares.keys():
377                                if sharename.lower().startswith(remainder.lower()):
378                                    matching_entries.append(shares[sharename]["name"])
379                            # Final matches
380                            for m in matching_entries:
381                                self.matches.append(command + " " + shlex.quote(m))
382
383                        # Autocomplete directory
384                        if "remote_directory" in self.commands[command]["autocomplete"]:
385                            # Choose remote directory
386                            path = ""
387                            if '\\' in remainder.strip() or '/' in remainder.strip():
388                                path = remainder.strip().replace(ntpath.sep, '/')
389                                path = '/'.join(path.split('/')[:-1]) 
390                            # Get remote directory contents
391                            directory_contents = self.smbSession.list_contents(path=path).items()
392                            # 
393                            matching_entries = []
394                            for _, entry in directory_contents:
395                                if entry.is_directory() and entry.get_longname() not in [".",".."]:
396                                    if len(path) != 0:
397                                        matching_entries.append(path + '/' + entry.get_longname() + '/')
398                                    else:
399                                        matching_entries.append(entry.get_longname() + '/')
400                            #
401                            for m in matching_entries:
402                                if m.lower().startswith(remainder.lower()) or shlex.quote(m).lower().startswith(remainder.lower()):
403                                    self.matches.append(command + " " + shlex.quote(m))
404
405                        # Autocomplete file
406                        if "remote_file" in self.commands[command]["autocomplete"]:
407                            # Choose remote file
408                            path = ""
409                            if '\\' in remainder.strip() or '/' in remainder.strip():
410                                path = remainder.strip().replace(ntpath.sep, '/')
411                                path = '/'.join(path.split('/')[:-1])
412                            # Get remote directory contents
413                            directory_contents = self.smbSession.list_contents(path=path).items()
414                            # 
415                            matching_entries = []
416                            for _, entry in directory_contents:
417                                if (not entry.is_directory()) and entry.get_longname() not in [".",".."]:
418                                    if len(path) != 0:
419                                        matching_entries.append(path + '/' + entry.get_longname())
420                                    else:
421                                        matching_entries.append(entry.get_longname())
422                            # 
423                            for m in matching_entries:
424                                if m.lower().startswith(remainder.lower()) or shlex.quote(m).lower().startswith(remainder.lower()):
425                                    self.matches.append(command + " " + shlex.quote(m))
426
427                        # Autocomplete local_directory
428                        if "local_directory" in self.commands[command]["autocomplete"]:
429                            # Choose directory
430                            path = ""
431                            if os.path.sep in remainder.strip():
432                                path = path.split(os.path.sep)[:-1]
433                                path = os.path.sep.join(path)
434                            # Current dir
435                            if len(path.strip()) == 0:
436                                path = "."
437                            #
438                            directory_contents = os.listdir(path=path + os.path.sep)
439                            matching_entries = []
440                            for entry in directory_contents:
441                                if entry not in [".",".."]:
442                                    entry_path = path + os.path.sep + entry
443                                    if os.path.isdir(entry_path):
444                                        matching_entries.append(entry_path + os.path.sep)
445                            #
446                            for m in matching_entries:
447                                if m.lower().startswith(remainder.lower()) or shlex.quote(m).lower().startswith(remainder.lower()):
448                                    self.matches.append(command + " " + shlex.quote(m))
449
450                        # Autocomplete local_file
451                        if "local_file" in self.commands[command]["autocomplete"]:
452                            # Choose file
453                            path = ""
454                            if os.path.sep in remainder.strip():
455                                path = path.split(os.path.sep)[:-1]
456                                path = os.path.sep.join(path)
457                            # Current dir
458                            if len(path.strip()) == 0:
459                                path = "."
460                            # 
461                            directory_contents = os.listdir(path=(path + os.path.sep))
462                            matching_entries = []
463                            for entry in directory_contents:
464                                if entry not in [".",".."]:
465                                    entry_path = path + os.path.sep + entry
466                                    if not os.path.isdir(entry_path):
467                                        matching_entries.append(entry_path)
468                            # 
469                            for m in matching_entries:
470                                if m.lower().startswith(remainder.lower()) or shlex.quote(m).lower().startswith(remainder.lower()):
471                                    self.matches.append(command + " " + shlex.quote(m))
472
473                        else:
474                            # Generic case for subcommands
475                            for m in self.commands[command]["subcommands"]:
476                                if m.startswith(remainder):
477                                    self.matches.append(command + " " + m)
478                    else:
479                        # Unknown subcommand, skipping autocomplete
480                        pass
481                else:
482                    self.matches = []
483            else:
484                self.matches = self.commands.keys()[:]
485
486        try:
487            return self.matches[state] + " "
488        except IndexError:
489            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.

def print_help(self, command=None):
491    def print_help(self, command=None):
492        """
493        Prints help information for a specific command or all commands if no command is specified.
494
495        This method displays the help information for the command passed as an argument. If no command is specified,
496        it prints the help information for all available commands. The help information includes the command syntax,
497        description, and any subcommands associated with it. This method is designed to provide users with the necessary
498        guidance on how to use the commands in the smbclient-ng shell.
499
500        Args:
501            command (str, optional): The command to display help information for. If None, help for all commands is displayed.
502
503        Returns:
504            None
505        """
506
507        if command is not None:
508            if command not in list(self.commands.keys())+["format"]:
509                command = None
510        
511        # Print help for a specific command
512        if command is not None:
513            if command == "format":
514                self.print_help_format()
515            else:
516                self.logger.print("│")
517                if self.config.no_colors:
518                    command_str = command + "─"* (15 - len(command))
519                    if len(self.commands[command]["description"]) == 0:
520                        self.logger.print("│ ■ %s┤  " % command_str)
521                    elif len(self.commands[command]["description"]) == 1:
522                        self.logger.print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0]))
523                    else:
524                        self.logger.print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0]))
525                        for line in self.commands[command]["description"][1:]:
526                            self.logger.print("│ %s│ %s " % (" "*(15+2), line))
527                else:
528                    command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m"
529                    if len(self.commands[command]["description"]) == 0:
530                        self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m  " % command_str)
531                    elif len(self.commands[command]["description"]) == 1:
532                        self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0]))
533                    else:
534                        self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0]))
535                        for line in self.commands[command]["description"][1:]:
536                            self.logger.print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line))
537                self.logger.print("│")
538        # Generic help
539        else:
540            self.logger.print("│")
541            commands = sorted(self.commands.keys())
542            for command in commands:
543                if self.config.no_colors:
544                    command_str = command + "─"* (15 - len(command))
545                    if len(self.commands[command]["description"]) == 0:
546                        self.logger.print("│ ■ %s┤  " % command_str)
547                    elif len(self.commands[command]["description"]) == 1:
548                        self.logger.print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0]))
549                    else:
550                        self.logger.print("│ ■ %s┤ %s " % (command_str, self.commands[command]["description"][0]))
551                        for line in self.commands[command]["description"][1:]:
552                            self.logger.print("│ %s│ %s " % (" "*(15+2), line))
553                else:
554                    command_str = command + " \x1b[90m" + "─"* (15 - len(command)) + "\x1b[0m"
555                    if len(self.commands[command]["description"]) == 0:
556                        self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m  " % command_str)
557                    elif len(self.commands[command]["description"]) == 1:
558                        self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0]))
559                    else:
560                        self.logger.print("│ ■ %s\x1b[90m┤\x1b[0m %s " % (command_str, self.commands[command]["description"][0]))
561                        for line in self.commands[command]["description"][1:]:
562                            self.logger.print("│ %s\x1b[90m│\x1b[0m %s " % (" "*(15+3), line))
563                self.logger.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

def print_help_format(self):
565    def print_help_format(self):
566        """
567        Prints the help information for the 'format' used in remote 'ls' and 'dir' commands.
568
569        This function displays the format of file attributes used in the smbclient-ng shell. It explains the meaning
570        of each character in the file attribute string, such as whether a file is read-only, hidden, or a directory.
571        """
572        if self.config.no_colors:
573            self.logger.print("File attributes format:\n")
574            self.logger.print("dachnrst")
575            self.logger.print("│││││││└──> Temporary")
576            self.logger.print("││││││└───> System")
577            self.logger.print("│││││└────> Read-Only")
578            self.logger.print("││││└─────> Normal")
579            self.logger.print("│││└──────> Hidden")
580            self.logger.print("││└───────> Compressed")
581            self.logger.print("│└────────> Archived")
582            self.logger.print("└─────────> Directory")
583        else:
584            self.logger.print("File attributes format:\n")
585            self.logger.print("dachnrst")
586            self.logger.print("\x1b[90m│││││││└──>\x1b[0m Temporary")
587            self.logger.print("\x1b[90m││││││└───>\x1b[0m System")
588            self.logger.print("\x1b[90m│││││└────>\x1b[0m Read-Only")
589            self.logger.print("\x1b[90m││││└─────>\x1b[0m Normal")
590            self.logger.print("\x1b[90m│││└──────>\x1b[0m Hidden")
591            self.logger.print("\x1b[90m││└───────>\x1b[0m Compressed")
592            self.logger.print("\x1b[90m│└────────>\x1b[0m Archived")
593            self.logger.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.