diff --git a/TODO..md b/TODO..md
index 4f9e347..c5c5f7d 100755
--- a/TODO..md
+++ b/TODO..md
@@ -1,11 +1,8 @@
### File System
- recursive ls
-- ls a single file
-- rm with wildcard
### Task Scheduler
- all in one (create, execute, delete)
-- add task run modifiers (run every X min/hour)
- load task xml
### Registry
diff --git a/src/slingerpkg/__init__.py b/src/slingerpkg/__init__.py
index 75cf27e..576f0b1 100755
--- a/src/slingerpkg/__init__.py
+++ b/src/slingerpkg/__init__.py
@@ -1,2 +1,2 @@
-__version__ = '0.4.0'
+__version__ = '0.5.0'
__package__ = 'slinger'
\ No newline at end of file
diff --git a/src/slingerpkg/lib/dcetransport.py b/src/slingerpkg/lib/dcetransport.py
index ec1c791..c5bfbd0 100755
--- a/src/slingerpkg/lib/dcetransport.py
+++ b/src/slingerpkg/lib/dcetransport.py
@@ -231,11 +231,8 @@ def _create_task(self, task_name, folder_path, task_xml):
flags = tsch.TASK_CREATE | tsch.TASK_FLAG_SYSTEM_REQUIRED | tsch.TASK_FLAG_HIDDEN
sddl = '' # Security descriptor definition language string (empty string for default permissions)
abs_path = folder_path + "\\" + task_name
- abs_path = abs_path .replace(r'\\', chr(92))
- print_log(f"Creating Task: {abs_path}")
- # Register the task
- # tsch.hSchRpcRegisterTask(dce, '\\%s' % tmpName, xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
-
+ abs_path = abs_path.replace(r'\\', chr(92))
+
response = tsch.hSchRpcRegisterTask(self.dce, abs_path, task_xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
return response
diff --git a/src/slingerpkg/lib/schtasks.py b/src/slingerpkg/lib/schtasks.py
index 22134f7..fd84aca 100755
--- a/src/slingerpkg/lib/schtasks.py
+++ b/src/slingerpkg/lib/schtasks.py
@@ -179,8 +179,27 @@ def task_create(self, args):
arguments = args.arguments
folder_path = args.folder
# generate random date in last year using format 2023-01-01T08:00:00
- new_date = generate_random_date()
- task_xml = f"""
+
+ if args.date:
+ new_date = reformat_datetime(args.date)
+ else:
+ new_date = generate_random_date()
+
+ interval = None
+ if args.interval:
+ #if less than 60, -> PT_M
+ #if greater than 60, -> PT_H
+ if int(args.interval) % 60 == 0:
+ h = int(args.interval) / 60
+ interval = f"PT{h}H"
+ elif int(args.interval) < 60:
+ interval = f"PT{args.interval}M"
+ else:
+ h = round(int(args.interval) / 60)
+ m = int(args.interval) % 60
+ interval = f"PT{h}H{m}M"
+
+ task_xml_once = f"""
{new_date}
@@ -229,21 +248,84 @@ def task_create(self, args):
"""
+
+ task_xml_interval = f"""
+
+
+ {new_date}
+ SYSTEM
+ {folder_path}\{task_name}
+
+
+
+
+ {interval}
+ false
+
+ {new_date}
+ true
+
+ 1
+
+
+
+
+
+ S-1-5-18
+ HighestAvailable
+
+
+
+ IgnoreNew
+ true
+ true
+ true
+ false
+ false
+
+ true
+ false
+
+ true
+ true
+ true
+ false
+ false
+ PT72H
+ 7
+
+
+
+ {program}
+ {xml_escape(arguments)}
+
+
+
+"""
+ task_xml = task_xml_interval if args.interval else task_xml_once
#validate_xml(task_xml)
self.setup_dce_transport()
self.dce_transport._connect('atsvc')
- print_info(f"Creating task '{task_name}' in folder '{folder_path}'")
+
print_info("Using Program: " + program)
print_info("Using Arguments: " + arguments)
- print_info("Task XML:")
- print_log(task_xml)
+ print_info("Using Date: " + new_date)
+ print_info("Using Interval: " + interval if args.interval else "Using Interval: None")
+ print_debug("Task XML:")
+ print_debug(task_xml)
+ abs_path = folder_path + "\\" + task_name
+ abs_path = abs_path.replace(r'\\', chr(92))
+ print_log(f"Creating Task: {abs_path}")
try:
response = self.dce_transport._create_task(task_name, folder_path, task_xml)
except Exception as e:
if "ERROR_ALREADY_EXISTS" in str(e):
print_warning(f"Task '{task_name}' already exists in folder '{folder_path}'")
return
+ else:
+ print_bad(f"Error creating task '{task_name}': {e}")
+ return
if response['ErrorCode'] == 0:
print_log(f"Task '{task_name}' created successfully.")
@@ -313,10 +395,11 @@ def task_show_handler(self, args):
if not self.folder_list_dict and args.taskid:
print_warning("No tasks have been enumerated. Run enumtasks first.")
- else:
+ elif args.task_path:
task_arg = args.taskid if args.taskid else args.task_path
self.view_task_details(task_arg)
-
+ else:
+ print_warning("No task specified. Use taskshow -i or taskshow to specify a task.")
def task_manager(self):
pass
diff --git a/src/slingerpkg/lib/smblib.py b/src/slingerpkg/lib/smblib.py
index 965a19a..0f80d73 100755
--- a/src/slingerpkg/lib/smblib.py
+++ b/src/slingerpkg/lib/smblib.py
@@ -83,6 +83,18 @@ def rm_handler(self, args):
if args.remote_path == "." or args.remote_path == "" or args.remote_path is None:
print_warning("Please specify a file to remove.")
return
+ if args.remote_path == "*":
+ # get file listing
+ list_path = self.relative_path + '\\*' if self.relative_path else '*'
+ files = self.conn.listPath(self.share, list_path)
+ for f in files:
+ if f.is_directory() and f.get_longname() in ['.', '..']:
+ continue
+ path = ntpath.normpath(ntpath.join(self.relative_path, f.get_longname()))
+ print_debug(f"Removing file {path}")
+ self.conn.deleteFile(self.share, path)
+ print_info(f"File Removed {path}")
+ return
path = ntpath.normpath(ntpath.join(self.relative_path, args.remote_path))
if self.check_if_connected():
@@ -141,16 +153,18 @@ def update_current_path(self):
self.current_path = ntpath.normpath(self.share + "\\" + self.relative_path)
# validate the directory exists
- def is_valid_directory(self, path):
+ def is_valid_directory(self, path, print_error=True):
list_path = path + '\\*' if path else '*'
try:
self.conn.listPath(self.share, list_path)
return True
except Exception as e:
if "STATUS_STOPPED_ON_SYMLINK" in str(e):
- print_warning(f"Remote directory {path} is a symlink.")
+ if print_error:
+ print_warning(f"Remote directory {path} is a symlink.")
elif "STATUS_NOT_A_DIRECTORY" in str(e):
- print_warning(f"Remote object {path} is not a directory.")
+ if print_error:
+ print_warning(f"{path} is not a directory.")
print_debug(f"Failed to list directory {path} on share {self.share}: {e}", sys.exc_info())
return False
@@ -328,16 +342,17 @@ def dir_list(self, args=None):
path = os.path.normpath(os.path.join(self.relative_path,path))
- if not self.is_valid_directory(path):
- print_bad(f"Invalid directory: {path}")
- return
+ #if not self.is_valid_directory(path):
+ # print_bad(f"Invalid directory: {path}")
+ #return
dirList = []
- if self.share is None:
- print_warning("No share is connected. Use the 'use' command to connect to a share.")
- return
+
try:
- list_path = path + '\\*' if path else '*'
+ if not self.is_valid_directory(path, print_error=False) and "\\" not in path:
+ list_path = path
+ else:
+ list_path = path + '\\*' if path else '*'
files = self.conn.listPath(self.share, list_path)
for f in files:
creation_time = (datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=f.get_ctime()/10)).replace(microsecond=0)
@@ -358,7 +373,7 @@ def dir_list(self, args=None):
suffix = ""
else:
suffix = path + "\\"
- print_info("Showing directory listing for: " + os.path.normpath(self.share + "\\" + suffix))
+ print_debug("Showing file listing for: " + os.path.normpath(self.share + "\\" + suffix))
# get sort option from arg.sort
sort_option = args.sort
@@ -391,5 +406,8 @@ def dir_list(self, args=None):
print_log(tabulate(dirList, headers=['Attribs', 'Created', 'LastAccess', 'LastWrite', 'Size', 'Name'], tablefmt='psql'))
except Exception as e:
- print_debug(f"Failed to list directory {path} on share {self.share}: {e}", sys.exc_info())
- print_bad(f"Failed to list directory {path} on share {self.share}: {e}")
+ if "STATUS_NO_SUCH_FILE" in str(e):
+ print_warning(f"Invalid directory or file: {path}")
+ else:
+ print_debug(f"Failed to list file or directory {path} on share {self.share}: {e}", sys.exc_info())
+
diff --git a/src/slingerpkg/utils/cli.py b/src/slingerpkg/utils/cli.py
index 8ddec8f..4ee9717 100755
--- a/src/slingerpkg/utils/cli.py
+++ b/src/slingerpkg/utils/cli.py
@@ -210,7 +210,7 @@ def setup_cli_parser(slingerClient):
parser_taskenum = subparsers.add_parser('enumtasks', help='Enumerate scheduled tasks', description='Enumerate scheduled tasks on the remote server', epilog='Example Usage: enumtasks', aliases=['tasksenum','taskenum'])
parser_taskenum.set_defaults(func=slingerClient.enum_task_folders_recursive)
# Subparser for 'tasksshow' command
- parser_taskshow = subparsers.add_parser('tasksshow', help='Show task details', description='Show details of a specific task on the remote server', epilog='Example Usage: tasksshow -i 123', aliases=['taskshow','showtask'])
+ parser_taskshow = subparsers.add_parser('taskshow', help='Show task details', description='Show details of a specific task on the remote server', epilog='Example Usage: tasksshow -i 123', aliases=['tasksshow','showtask'])
taskshowgroup = parser_taskshow.add_mutually_exclusive_group(required=True)
taskshowgroup.add_argument('-i', '--taskid', type=int, help='Specify the ID of the task to show')
taskshowgroup.add_argument('task_path', type=str, nargs='?', help='Specify the full path of the task to show')
@@ -220,9 +220,11 @@ def setup_cli_parser(slingerClient):
# Subparser for 'taskcreate' command
parser_taskcreate = subparsers.add_parser('taskcreate', help='Create a new task', description='Create a new scheduled task on the remote server', epilog="Example Usage: taskcreate -n newtask -p cmd.exe -a '/c ipconfig /all > C:\\test' -f \\\\Windows", aliases=['taskadd'], formatter_class=argparse.RawDescriptionHelpFormatter)
parser_taskcreate.add_argument('-n', '--name', required=True, help='Specify the name of the new task')
- parser_taskcreate.add_argument('-p', '--program', required=True, help='Specify the program to run in the task')
- parser_taskcreate.add_argument('-a', '--arguments', required=True, help='Specify the arguments to pass to the program')
- parser_taskcreate.add_argument('-f', '--folder', required=True, default="\\", help='Specify the folder to create the task in')
+ parser_taskcreate.add_argument('-p', '--program', required=True, help='Specify the program to run (cmd.exe)')
+ parser_taskcreate.add_argument('-a', '--arguments', required=False, help='Specify the arguments to pass to the program')
+ parser_taskcreate.add_argument('-f', '--folder', required=False, default="", help='Specify the folder to create the task in')
+ parser_taskcreate.add_argument('-i', '--interval', required=False, default=None, help='Specify an interval in minutes to run the task')
+ parser_taskcreate.add_argument('-d', '--date', required=False, default=None, help='Specify the date to start the task (2099-12-31 14:01:00)')
parser_taskcreate.set_defaults(func=slingerClient.task_create)
# Subparser for 'taskrun' command
diff --git a/src/slingerpkg/utils/common.py b/src/slingerpkg/utils/common.py
index 8c42a68..4c2e5a3 100755
--- a/src/slingerpkg/utils/common.py
+++ b/src/slingerpkg/utils/common.py
@@ -73,6 +73,18 @@ def generate_random_date(lower_time_bound=None):
random_date = lower_time_bound + datetime.timedelta(seconds=random_second)
return random_date.strftime("%Y-%m-%dT%H:%M:%S")
+def reformat_datetime(datetime_str):
+ original_format = "%Y-%m-%d %H:%M:%S" # Assuming the original format is "%Y-%m-%d %H:%M:%S"
+ new_format = "%Y-%m-%dT%H:%M:%S" # Desired format "%Y-%m-%dT%H:%M:%S"
+
+ # Parse the original datetime string
+ dt = datetime.datetime.strptime(datetime_str, original_format)
+
+ # Convert the datetime object to the desired format
+ formatted_datetime = dt.strftime(new_format)
+
+ return formatted_datetime
+
def xml_escape(data):
replace_table = {
"&": "&",