smbclientng.core.utils

  1#!/usr/bin/env python3
  2# -*- coding: utf-8 -*-
  3# File name          : utils.py
  4# Author             : Podalirius (@podalirius_)
  5# Date created       : 23 may 2024
  6
  7
  8import datetime
  9import os
 10import ntpath
 11import re
 12import stat
 13
 14
 15def parse_lm_nt_hashes(lm_nt_hashes_string):
 16    """
 17    Parse the input string containing LM and NT hash values and return them separately.
 18
 19    This function takes a string containing LM and NT hash values, typically separated by a colon (:).
 20    It returns the LM and NT hash values as separate strings. If only one hash value is provided, it is
 21    assumed to be the NT hash and the LM hash is set to its default value. If no valid hash values are
 22    found, both return values are empty strings.
 23
 24    Args:
 25        lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon.
 26
 27    Returns:
 28        tuple: A tuple containing two strings (lm_hash_value, nt_hash_value).
 29               - lm_hash_value: The LM hash value or its default if not provided.
 30               - nt_hash_value: The NT hash value or its default if not provided.
 31    
 32    Extracted from p0dalirius/sectools library
 33    Src: https://github.com/p0dalirius/sectools/blob/7bb3f5cb7815ad4d4845713c8739e2e2b0ea4e75/sectools/windows/crypto.py#L11-L24
 34    """
 35
 36    lm_hash_value, nt_hash_value = "", ""
 37    if lm_nt_hashes_string is not None:
 38        matched = re.match("([0-9a-f]{32})?(:)?([0-9a-f]{32})?", lm_nt_hashes_string.strip().lower())
 39        m_lm_hash, m_sep, m_nt_hash = matched.groups()
 40        if m_lm_hash is None and m_sep is None and m_nt_hash is None:
 41            lm_hash_value, nt_hash_value = "", ""
 42        elif m_lm_hash is None and m_nt_hash is not None:
 43            lm_hash_value = "aad3b435b51404eeaad3b435b51404ee"
 44            nt_hash_value = m_nt_hash
 45        elif m_lm_hash is not None and m_nt_hash is None:
 46            lm_hash_value = m_lm_hash
 47            nt_hash_value = "31d6cfe0d16ae931b73c59d7e0c089c0"
 48    return lm_hash_value, nt_hash_value
 49
 50
 51def b_filesize(l):
 52    """
 53    Convert a file size from bytes to a more readable format using the largest appropriate unit.
 54
 55    This function takes an integer representing a file size in bytes and converts it to a human-readable
 56    string using the largest appropriate unit from bytes (B) to petabytes (PB). The result is rounded to
 57    two decimal places.
 58
 59    Args:
 60        l (int): The file size in bytes.
 61
 62    Returns:
 63        str: A string representing the file size in a more readable format, including the appropriate unit.
 64    """
 65
 66    units = ['B','kB','MB','GB','TB','PB']
 67    for k in range(len(units)):
 68        if l < (1024**(k+1)):
 69            break
 70    return "%4.2f %s" % (round(l/(1024**(k)),2), units[k])
 71
 72
 73def unix_permissions(entryname):
 74    """
 75    Generate a string representing the Unix-style permissions for a given file or directory.
 76
 77    This function uses the os.lstat() method to retrieve the status of the specified file or directory,
 78    then constructs a string that represents the Unix-style permissions based on the mode of the file.
 79
 80    Args:
 81        entryname (str): The path to the file or directory for which permissions are being determined.
 82
 83    Returns:
 84        str: A string of length 10 representing the Unix-style permissions (e.g., '-rwxr-xr--').
 85             The first character is either 'd' (directory), '-' (not a directory), followed by
 86             three groups of 'r', 'w', 'x' (read, write, execute permissions) for owner, group,
 87             and others respectively.
 88    """
 89
 90    mode = os.lstat(entryname).st_mode
 91    permissions = []
 92
 93    permissions.append('d' if stat.S_ISDIR(mode) else '-')
 94
 95    permissions.append('r' if mode & stat.S_IRUSR else '-')
 96    permissions.append('w' if mode & stat.S_IWUSR else '-')
 97    permissions.append('x' if mode & stat.S_IXUSR else '-')
 98
 99    permissions.append('r' if mode & stat.S_IRGRP else '-')
100    permissions.append('w' if mode & stat.S_IWGRP else '-')
101    permissions.append('x' if mode & stat.S_IXGRP else '-')
102
103    permissions.append('r' if mode & stat.S_IROTH else '-')
104    permissions.append('w' if mode & stat.S_IWOTH else '-')
105    permissions.append('x' if mode & stat.S_IXOTH else '-')
106
107    return ''.join(permissions)
108
109
110def STYPE_MASK(stype_value):
111    """
112    Extracts the share type flags from a given share type value.
113
114    This function uses bitwise operations to determine which share type flags are set in the provided `stype_value`.
115    It checks against known share type flags and returns a list of the flags that are set.
116
117    Parameters:
118        stype_value (int): The share type value to analyze, typically obtained from SMB share properties.
119
120    Returns:
121        list: A list of strings, where each string represents a share type flag that is set in the input value.
122    """
123
124    known_flags = {
125        ## One of the following values may be specified. You can isolate these values by using the STYPE_MASK value.
126        # Disk drive.
127        "STYPE_DISKTREE": 0x0,
128
129        # Print queue.
130        "STYPE_PRINTQ": 0x1,
131
132        # Communication device.
133        "STYPE_DEVICE": 0x2,
134
135        # Interprocess communication (IPC).
136        "STYPE_IPC": 0x3,
137
138        ## In addition, one or both of the following values may be specified.
139        # Special share reserved for interprocess communication (IPC$) or remote administration of the server (ADMIN$).
140        # Can also refer to administrative shares such as C$, D$, E$, and so forth. For more information, see Network Share Functions.
141        "STYPE_SPECIAL": 0x80000000,
142
143        # A temporary share.
144        "STYPE_TEMPORARY": 0x40000000
145    }
146    flags = []
147    if (stype_value & 0b11) == known_flags["STYPE_DISKTREE"]:
148        flags.append("STYPE_DISKTREE")
149    elif (stype_value & 0b11) == known_flags["STYPE_PRINTQ"]:
150        flags.append("STYPE_PRINTQ")
151    elif (stype_value & 0b11) == known_flags["STYPE_DEVICE"]:
152        flags.append("STYPE_DEVICE")
153    elif (stype_value & 0b11) == known_flags["STYPE_IPC"]:
154        flags.append("STYPE_IPC")
155    if (stype_value & known_flags["STYPE_SPECIAL"]) == known_flags["STYPE_SPECIAL"]:
156        flags.append("STYPE_SPECIAL")
157    if (stype_value & known_flags["STYPE_TEMPORARY"]) == known_flags["STYPE_TEMPORARY"]:
158        flags.append("STYPE_TEMPORARY")
159    return flags
160
161
162def windows_ls_entry(entry, config, pathToPrint=None):
163    """
164    This function generates a metadata string based on the attributes of the provided entry object.
165    
166    Parameters:
167        entry (object): An object representing a file or directory entry.
168
169    Returns:
170        str: A string representing the metadata of the entry, including attributes like directory, archive, compressed, hidden, normal, readonly, system, and temporary.
171    """
172    
173    if pathToPrint is not None:
174        pathToPrint = pathToPrint + ntpath.sep + entry.get_longname()
175    else:
176        pathToPrint = entry.get_longname()
177
178    meta_string = ""
179    meta_string += ("d" if entry.is_directory() else "-")
180    meta_string += ("a" if entry.is_archive() else "-")
181    meta_string += ("c" if entry.is_compressed() else "-")
182    meta_string += ("h" if entry.is_hidden() else "-")
183    meta_string += ("n" if entry.is_normal() else "-")
184    meta_string += ("r" if entry.is_readonly() else "-")
185    meta_string += ("s" if entry.is_system() else "-")
186    meta_string += ("t" if entry.is_temporary() else "-")
187
188    size_str = b_filesize(entry.get_filesize())
189
190    date_str = datetime.datetime.fromtimestamp(entry.get_atime_epoch()).strftime("%Y-%m-%d %H:%M")
191    
192    if entry.is_directory():
193        if config.no_colors:
194            print("%s %10s  %s  %s\\" % (meta_string, size_str, date_str, pathToPrint))
195        else:
196            print("%s %10s  %s  \x1b[1;96m%s\x1b[0m\\" % (meta_string, size_str, date_str, pathToPrint))
197    else:
198        if config.no_colors:
199            print("%s %10s  %s  %s" % (meta_string, size_str, date_str, pathToPrint))
200        else:
201            print("%s %10s  %s  \x1b[1m%s\x1b[0m" % (meta_string, size_str, date_str, pathToPrint))
202
203
204def local_tree(path, config):
205    """
206    This function recursively lists the contents of a directory in a tree-like format.
207
208    Parameters:
209        path (str): The path to the directory to list.
210        config (object): Configuration settings which may affect the output, such as whether to use colors.
211
212    Returns:
213        None: This function does not return anything but prints the directory tree to the console.
214    """
215
216    def recurse_action(base_dir="", path=[], prompt=[]):
217        bars = ["│   ", "├── ", "└── "]
218
219        local_path = os.path.normpath(base_dir + os.path.sep + os.path.sep.join(path) + os.path.sep)
220
221        entries = []
222        try:
223            entries = os.listdir(local_path)
224        except Exception as err:
225            if config.no_colors:
226                print("%s%s" % (''.join(prompt+[bars[2]]), err))
227            else:
228                print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), err))
229            return 
230
231        entries = sorted(entries)
232
233        # 
234        if len(entries) > 1:
235            index = 0
236            for entry in entries:
237                index += 1
238                # This is the first entry 
239                if index == 0:
240                    if os.path.isdir(local_path + os.path.sep + entry):
241                        if config.no_colors:
242                            print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
243                        else:
244                            print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
245                        recurse_action(
246                            base_dir=base_dir, 
247                            path=path+[entry],
248                            prompt=prompt+["│   "]
249                        )
250                    else:
251                        if config.no_colors:
252                            print("%s%s" % (''.join(prompt+[bars[1]]), entry))
253                        else:
254                            print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry))
255
256                # This is the last entry
257                elif index == len(entries):
258                    if os.path.isdir(local_path + os.path.sep + entry):
259                        if config.no_colors:
260                            print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
261                        else:
262                            print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
263                        recurse_action(
264                            base_dir=base_dir, 
265                            path=path+[entry],
266                            prompt=prompt+["    "]
267                        )
268                    else:
269                        if config.no_colors:
270                            print("%s%s" % (''.join(prompt+[bars[2]]), entry))
271                        else:
272                            print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry))
273                    
274                # These are entries in the middle
275                else:
276                    if os.path.isdir(local_path + os.path.sep + entry):
277                        if config.no_colors:
278                            print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
279                        else:
280                            print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
281                        recurse_action(
282                            base_dir=base_dir, 
283                            path=path+[entry],
284                            prompt=prompt+["│   "]
285                        )
286                    else:
287                        if config.no_colors:
288                            print("%s%s" % (''.join(prompt+[bars[1]]), entry))
289                        else:
290                            print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry))
291
292        # 
293        elif len(entries) == 1:
294            entry = entries[0]
295            if os.path.isdir(local_path + os.path.sep + entry):
296                if config.no_colors:
297                    print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
298                else:
299                    print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
300                recurse_action(
301                    base_dir=base_dir, 
302                    path=path+[entry],
303                    prompt=prompt+["    "]
304                )
305            else:
306                if config.no_colors:
307                    print("%s%s" % (''.join(prompt+[bars[2]]), entry))
308                else:
309                    print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry))
310
311    # Entrypoint
312    try:
313        if config.no_colors:
314            print("%s%s" % (path, os.path.sep))
315        else:
316            print("\x1b[1;96m%s\x1b[0m%s" % (path, os.path.sep))
317        recurse_action(
318            base_dir=os.getcwd(),
319            path=[path],
320            prompt=[""]
321        )
322    except (BrokenPipeError, KeyboardInterrupt) as e:
323        print("[!] Interrupted.")
def parse_lm_nt_hashes(lm_nt_hashes_string):
16def parse_lm_nt_hashes(lm_nt_hashes_string):
17    """
18    Parse the input string containing LM and NT hash values and return them separately.
19
20    This function takes a string containing LM and NT hash values, typically separated by a colon (:).
21    It returns the LM and NT hash values as separate strings. If only one hash value is provided, it is
22    assumed to be the NT hash and the LM hash is set to its default value. If no valid hash values are
23    found, both return values are empty strings.
24
25    Args:
26        lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon.
27
28    Returns:
29        tuple: A tuple containing two strings (lm_hash_value, nt_hash_value).
30               - lm_hash_value: The LM hash value or its default if not provided.
31               - nt_hash_value: The NT hash value or its default if not provided.
32    
33    Extracted from p0dalirius/sectools library
34    Src: https://github.com/p0dalirius/sectools/blob/7bb3f5cb7815ad4d4845713c8739e2e2b0ea4e75/sectools/windows/crypto.py#L11-L24
35    """
36
37    lm_hash_value, nt_hash_value = "", ""
38    if lm_nt_hashes_string is not None:
39        matched = re.match("([0-9a-f]{32})?(:)?([0-9a-f]{32})?", lm_nt_hashes_string.strip().lower())
40        m_lm_hash, m_sep, m_nt_hash = matched.groups()
41        if m_lm_hash is None and m_sep is None and m_nt_hash is None:
42            lm_hash_value, nt_hash_value = "", ""
43        elif m_lm_hash is None and m_nt_hash is not None:
44            lm_hash_value = "aad3b435b51404eeaad3b435b51404ee"
45            nt_hash_value = m_nt_hash
46        elif m_lm_hash is not None and m_nt_hash is None:
47            lm_hash_value = m_lm_hash
48            nt_hash_value = "31d6cfe0d16ae931b73c59d7e0c089c0"
49    return lm_hash_value, nt_hash_value

Parse the input string containing LM and NT hash values and return them separately.

This function takes a string containing LM and NT hash values, typically separated by a colon (:). It returns the LM and NT hash values as separate strings. If only one hash value is provided, it is assumed to be the NT hash and the LM hash is set to its default value. If no valid hash values are found, both return values are empty strings.

Args: lm_nt_hashes_string (str): A string containing LM and NT hash values separated by a colon.

Returns: tuple: A tuple containing two strings (lm_hash_value, nt_hash_value). - lm_hash_value: The LM hash value or its default if not provided. - nt_hash_value: The NT hash value or its default if not provided.

Extracted from p0dalirius/sectools library Src: https://github.com/p0dalirius/sectools/blob/7bb3f5cb7815ad4d4845713c8739e2e2b0ea4e75/sectools/windows/crypto.py#L11-L24

def b_filesize(l):
52def b_filesize(l):
53    """
54    Convert a file size from bytes to a more readable format using the largest appropriate unit.
55
56    This function takes an integer representing a file size in bytes and converts it to a human-readable
57    string using the largest appropriate unit from bytes (B) to petabytes (PB). The result is rounded to
58    two decimal places.
59
60    Args:
61        l (int): The file size in bytes.
62
63    Returns:
64        str: A string representing the file size in a more readable format, including the appropriate unit.
65    """
66
67    units = ['B','kB','MB','GB','TB','PB']
68    for k in range(len(units)):
69        if l < (1024**(k+1)):
70            break
71    return "%4.2f %s" % (round(l/(1024**(k)),2), units[k])

Convert a file size from bytes to a more readable format using the largest appropriate unit.

This function takes an integer representing a file size in bytes and converts it to a human-readable string using the largest appropriate unit from bytes (B) to petabytes (PB). The result is rounded to two decimal places.

Args: l (int): The file size in bytes.

Returns: str: A string representing the file size in a more readable format, including the appropriate unit.

def unix_permissions(entryname):
 74def unix_permissions(entryname):
 75    """
 76    Generate a string representing the Unix-style permissions for a given file or directory.
 77
 78    This function uses the os.lstat() method to retrieve the status of the specified file or directory,
 79    then constructs a string that represents the Unix-style permissions based on the mode of the file.
 80
 81    Args:
 82        entryname (str): The path to the file or directory for which permissions are being determined.
 83
 84    Returns:
 85        str: A string of length 10 representing the Unix-style permissions (e.g., '-rwxr-xr--').
 86             The first character is either 'd' (directory), '-' (not a directory), followed by
 87             three groups of 'r', 'w', 'x' (read, write, execute permissions) for owner, group,
 88             and others respectively.
 89    """
 90
 91    mode = os.lstat(entryname).st_mode
 92    permissions = []
 93
 94    permissions.append('d' if stat.S_ISDIR(mode) else '-')
 95
 96    permissions.append('r' if mode & stat.S_IRUSR else '-')
 97    permissions.append('w' if mode & stat.S_IWUSR else '-')
 98    permissions.append('x' if mode & stat.S_IXUSR else '-')
 99
100    permissions.append('r' if mode & stat.S_IRGRP else '-')
101    permissions.append('w' if mode & stat.S_IWGRP else '-')
102    permissions.append('x' if mode & stat.S_IXGRP else '-')
103
104    permissions.append('r' if mode & stat.S_IROTH else '-')
105    permissions.append('w' if mode & stat.S_IWOTH else '-')
106    permissions.append('x' if mode & stat.S_IXOTH else '-')
107
108    return ''.join(permissions)

Generate a string representing the Unix-style permissions for a given file or directory.

This function uses the os.lstat() method to retrieve the status of the specified file or directory, then constructs a string that represents the Unix-style permissions based on the mode of the file.

Args: entryname (str): The path to the file or directory for which permissions are being determined.

Returns: str: A string of length 10 representing the Unix-style permissions (e.g., '-rwxr-xr--'). The first character is either 'd' (directory), '-' (not a directory), followed by three groups of 'r', 'w', 'x' (read, write, execute permissions) for owner, group, and others respectively.

def STYPE_MASK(stype_value):
111def STYPE_MASK(stype_value):
112    """
113    Extracts the share type flags from a given share type value.
114
115    This function uses bitwise operations to determine which share type flags are set in the provided `stype_value`.
116    It checks against known share type flags and returns a list of the flags that are set.
117
118    Parameters:
119        stype_value (int): The share type value to analyze, typically obtained from SMB share properties.
120
121    Returns:
122        list: A list of strings, where each string represents a share type flag that is set in the input value.
123    """
124
125    known_flags = {
126        ## One of the following values may be specified. You can isolate these values by using the STYPE_MASK value.
127        # Disk drive.
128        "STYPE_DISKTREE": 0x0,
129
130        # Print queue.
131        "STYPE_PRINTQ": 0x1,
132
133        # Communication device.
134        "STYPE_DEVICE": 0x2,
135
136        # Interprocess communication (IPC).
137        "STYPE_IPC": 0x3,
138
139        ## In addition, one or both of the following values may be specified.
140        # Special share reserved for interprocess communication (IPC$) or remote administration of the server (ADMIN$).
141        # Can also refer to administrative shares such as C$, D$, E$, and so forth. For more information, see Network Share Functions.
142        "STYPE_SPECIAL": 0x80000000,
143
144        # A temporary share.
145        "STYPE_TEMPORARY": 0x40000000
146    }
147    flags = []
148    if (stype_value & 0b11) == known_flags["STYPE_DISKTREE"]:
149        flags.append("STYPE_DISKTREE")
150    elif (stype_value & 0b11) == known_flags["STYPE_PRINTQ"]:
151        flags.append("STYPE_PRINTQ")
152    elif (stype_value & 0b11) == known_flags["STYPE_DEVICE"]:
153        flags.append("STYPE_DEVICE")
154    elif (stype_value & 0b11) == known_flags["STYPE_IPC"]:
155        flags.append("STYPE_IPC")
156    if (stype_value & known_flags["STYPE_SPECIAL"]) == known_flags["STYPE_SPECIAL"]:
157        flags.append("STYPE_SPECIAL")
158    if (stype_value & known_flags["STYPE_TEMPORARY"]) == known_flags["STYPE_TEMPORARY"]:
159        flags.append("STYPE_TEMPORARY")
160    return flags

Extracts the share type flags from a given share type value.

This function uses bitwise operations to determine which share type flags are set in the provided stype_value. It checks against known share type flags and returns a list of the flags that are set.

Parameters: stype_value (int): The share type value to analyze, typically obtained from SMB share properties.

Returns: list: A list of strings, where each string represents a share type flag that is set in the input value.

def windows_ls_entry(entry, config, pathToPrint=None):
163def windows_ls_entry(entry, config, pathToPrint=None):
164    """
165    This function generates a metadata string based on the attributes of the provided entry object.
166    
167    Parameters:
168        entry (object): An object representing a file or directory entry.
169
170    Returns:
171        str: A string representing the metadata of the entry, including attributes like directory, archive, compressed, hidden, normal, readonly, system, and temporary.
172    """
173    
174    if pathToPrint is not None:
175        pathToPrint = pathToPrint + ntpath.sep + entry.get_longname()
176    else:
177        pathToPrint = entry.get_longname()
178
179    meta_string = ""
180    meta_string += ("d" if entry.is_directory() else "-")
181    meta_string += ("a" if entry.is_archive() else "-")
182    meta_string += ("c" if entry.is_compressed() else "-")
183    meta_string += ("h" if entry.is_hidden() else "-")
184    meta_string += ("n" if entry.is_normal() else "-")
185    meta_string += ("r" if entry.is_readonly() else "-")
186    meta_string += ("s" if entry.is_system() else "-")
187    meta_string += ("t" if entry.is_temporary() else "-")
188
189    size_str = b_filesize(entry.get_filesize())
190
191    date_str = datetime.datetime.fromtimestamp(entry.get_atime_epoch()).strftime("%Y-%m-%d %H:%M")
192    
193    if entry.is_directory():
194        if config.no_colors:
195            print("%s %10s  %s  %s\\" % (meta_string, size_str, date_str, pathToPrint))
196        else:
197            print("%s %10s  %s  \x1b[1;96m%s\x1b[0m\\" % (meta_string, size_str, date_str, pathToPrint))
198    else:
199        if config.no_colors:
200            print("%s %10s  %s  %s" % (meta_string, size_str, date_str, pathToPrint))
201        else:
202            print("%s %10s  %s  \x1b[1m%s\x1b[0m" % (meta_string, size_str, date_str, pathToPrint))

This function generates a metadata string based on the attributes of the provided entry object.

Parameters: entry (object): An object representing a file or directory entry.

Returns: str: A string representing the metadata of the entry, including attributes like directory, archive, compressed, hidden, normal, readonly, system, and temporary.

def local_tree(path, config):
205def local_tree(path, config):
206    """
207    This function recursively lists the contents of a directory in a tree-like format.
208
209    Parameters:
210        path (str): The path to the directory to list.
211        config (object): Configuration settings which may affect the output, such as whether to use colors.
212
213    Returns:
214        None: This function does not return anything but prints the directory tree to the console.
215    """
216
217    def recurse_action(base_dir="", path=[], prompt=[]):
218        bars = ["│   ", "├── ", "└── "]
219
220        local_path = os.path.normpath(base_dir + os.path.sep + os.path.sep.join(path) + os.path.sep)
221
222        entries = []
223        try:
224            entries = os.listdir(local_path)
225        except Exception as err:
226            if config.no_colors:
227                print("%s%s" % (''.join(prompt+[bars[2]]), err))
228            else:
229                print("%s\x1b[1;91m%s\x1b[0m" % (''.join(prompt+[bars[2]]), err))
230            return 
231
232        entries = sorted(entries)
233
234        # 
235        if len(entries) > 1:
236            index = 0
237            for entry in entries:
238                index += 1
239                # This is the first entry 
240                if index == 0:
241                    if os.path.isdir(local_path + os.path.sep + entry):
242                        if config.no_colors:
243                            print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
244                        else:
245                            print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
246                        recurse_action(
247                            base_dir=base_dir, 
248                            path=path+[entry],
249                            prompt=prompt+["│   "]
250                        )
251                    else:
252                        if config.no_colors:
253                            print("%s%s" % (''.join(prompt+[bars[1]]), entry))
254                        else:
255                            print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry))
256
257                # This is the last entry
258                elif index == len(entries):
259                    if os.path.isdir(local_path + os.path.sep + entry):
260                        if config.no_colors:
261                            print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
262                        else:
263                            print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
264                        recurse_action(
265                            base_dir=base_dir, 
266                            path=path+[entry],
267                            prompt=prompt+["    "]
268                        )
269                    else:
270                        if config.no_colors:
271                            print("%s%s" % (''.join(prompt+[bars[2]]), entry))
272                        else:
273                            print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry))
274                    
275                # These are entries in the middle
276                else:
277                    if os.path.isdir(local_path + os.path.sep + entry):
278                        if config.no_colors:
279                            print("%s%s%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
280                        else:
281                            print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[1]]), entry, os.path.sep))
282                        recurse_action(
283                            base_dir=base_dir, 
284                            path=path+[entry],
285                            prompt=prompt+["│   "]
286                        )
287                    else:
288                        if config.no_colors:
289                            print("%s%s" % (''.join(prompt+[bars[1]]), entry))
290                        else:
291                            print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[1]]), entry))
292
293        # 
294        elif len(entries) == 1:
295            entry = entries[0]
296            if os.path.isdir(local_path + os.path.sep + entry):
297                if config.no_colors:
298                    print("%s%s%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
299                else:
300                    print("%s\x1b[1;96m%s\x1b[0m%s" % (''.join(prompt+[bars[2]]), entry, os.path.sep))
301                recurse_action(
302                    base_dir=base_dir, 
303                    path=path+[entry],
304                    prompt=prompt+["    "]
305                )
306            else:
307                if config.no_colors:
308                    print("%s%s" % (''.join(prompt+[bars[2]]), entry))
309                else:
310                    print("%s\x1b[1m%s\x1b[0m" % (''.join(prompt+[bars[2]]), entry))
311
312    # Entrypoint
313    try:
314        if config.no_colors:
315            print("%s%s" % (path, os.path.sep))
316        else:
317            print("\x1b[1;96m%s\x1b[0m%s" % (path, os.path.sep))
318        recurse_action(
319            base_dir=os.getcwd(),
320            path=[path],
321            prompt=[""]
322        )
323    except (BrokenPipeError, KeyboardInterrupt) as e:
324        print("[!] Interrupted.")

This function recursively lists the contents of a directory in a tree-like format.

Parameters: path (str): The path to the directory to list. config (object): Configuration settings which may affect the output, such as whether to use colors.

Returns: None: This function does not return anything but prints the directory tree to the console.