From 7931c7efe9fd2deb08a635948a5546bf259d0930 Mon Sep 17 00:00:00 2001
From: Stefano Alberto Russo <stefano.russo@gmail.com>
Date: Mon, 29 Mar 2021 19:36:49 +0200
Subject: [PATCH] Added the mkdir. Fixes and imporvements (form ls to stat).

---
 services/webapp/code/rosetta/core_app/api.py | 122 +++++++++++++++----
 1 file changed, 96 insertions(+), 26 deletions(-)

diff --git a/services/webapp/code/rosetta/core_app/api.py b/services/webapp/code/rosetta/core_app/api.py
index f8452c3..e7afc3f 100644
--- a/services/webapp/code/rosetta/core_app/api.py
+++ b/services/webapp/code/rosetta/core_app/api.py
@@ -416,7 +416,7 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):
             raise Exception('No computing user?!')
 
         # Command
-        command = 'ssh -o LogLevel=ERROR -i {} -4 -o StrictHostKeyChecking=no {}@{} {}'.format(user_keys.private_key_file, computing_user, computing_host, command)
+        command = 'ssh -o LogLevel=ERROR -i {} -4 -o StrictHostKeyChecking=no {}@{} "{}"'.format(user_keys.private_key_file, computing_user, computing_host, command)
 
         return command
 
@@ -426,6 +426,12 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):
         path = re.sub(cleaner,'/',path)
         return path
 
+    @staticmethod
+    def sanitize_shell_path(path):
+        path = path.replace(' ', '\ ')
+        cleaner = re.compile('(?:\\\)+')
+        path = re.sub(cleaner,r"\\",path)
+        return path
 
     def get_computing(self, path, request):
         # Get the computing based on the folder name # TODO: this is very weak..
@@ -458,13 +464,22 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):
         # Data container 
         data = []
         
+        path = self.sanitize_shell_path(path)
+        
         # Prepare command
-        command = self.ssh_command('ls -al {}'.format(path), user, computing)
+        # https://askubuntu.com/questions/1116634/ls-command-show-time-only-in-iso-format
+        # https://www.howtogeek.com/451022/how-to-use-the-stat-command-on-linux/
+        command = self.ssh_command('cd {} && stat --printf=\'%F/%n/%s/%Y\\n\' * .*'.format(path), user, computing)
         
         # Execute_command
         out = os_shell(command, capture=True)
         if out.exit_code != 0:
-            raise Exception(out.stderr)
+            
+            # Did we just get a "cannot stat - No such file or directory error?
+            if 'No such file or directory' in out.stderr:
+                pass
+            else:
+                raise Exception(out.stderr)
                             
         # Log        
         #logger.debug('Shell exec output: "{}"'.format(out))
@@ -472,16 +487,17 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):
         out_lines = out.stdout.split('\n')
         
         for line in out_lines:
-
-            # Skip total files summary line at the end
-            if line.startswith('total'):
-                continue
             
+            # Example line: directory/My folder/68/1617030350
+
             # Set name
-            name = line.split(' ')[-1]
+            line_pieces = line.split('/')
+            type = line_pieces[0]
+            name = line_pieces[1]
+            size = line_pieces[2]
+            timestamp = line_pieces[3]
                      
-            # Check against binds if set
-            
+            # Check against binds if set            
             if binds:
                 if not path == '/':
                     full_path = path + '/' + name
@@ -499,11 +515,10 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):
                 # Define and clean listing path:
                 listing_path = '/{}/{}/{}/'.format(computing.name, path, name)
                 listing_path = self.clean_path(listing_path)
-
             
                 # File or directory?
-                if line.startswith('d'):
-                    if line.split(' ')[-1] not in ['.', '..']:
+                if type == 'directory':
+                    if name not in ['.', '..']:
                         data.append({
                                      'id': listing_path,
                                      'type': 'folder',
@@ -537,7 +552,9 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):
 
 
     def delete(self, path, user, computing):
-        
+
+        path = self.sanitize_shell_path(path)
+
         # Prepare command
         command = self.ssh_command('rm -rf {}'.format(path), user, computing)
         
@@ -548,8 +565,24 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):
         return out.stdout
 
 
+    def mkdir(self, path, user, computing):
+        
+        path = self.sanitize_shell_path(path)
+        
+        # Prepare command
+        command = self.ssh_command('mkdir {}'.format(path), user, computing)
+        
+        # Execute_command
+        out = os_shell(command, capture=True)
+        if out.exit_code != 0:
+            raise Exception(out.stderr)
+        return out.stdout
+
+
     def cat(self, path, user, computing):
         
+        path = self.sanitize_shell_path(path)
+        
         # Prepare command
         command = self.ssh_command('cat {}'.format(path), user, computing)
         
@@ -562,12 +595,13 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):
 
     def rename(self, old, new, user, computing):
         
-        old = old.replace(' ', '\ ')
-        new = new.replace(' ', '\ ')
+        old = self.sanitize_shell_path(old)
+        new = self.sanitize_shell_path(new)
 
-        
         # Prepare command
-        command = self.ssh_command('mv \'{}\' \'{}\''.format(old, new), user, computing)
+        command = self.ssh_command('mv {} {}'.format(old, new), user, computing)
+
+        logger.critical(command)
         
         # Execute_command
         out = os_shell(command, capture=True)
@@ -577,11 +611,12 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):
 
 
     def copy(self, source, target, user, computing):
-        source = source.replace(' ', '\ ')
-        target = target.replace(' ', '\ ')
+
+        source = self.sanitize_shell_path(source)
+        target = self.sanitize_shell_path(target)
 
         # Prepare command
-        command = self.ssh_command('cp -a \'{}\' \'{}\''.format(source, target), user, computing)
+        command = self.ssh_command('cp -a {} {}'.format(source, target), user, computing)
         
         # Execute_command
         out = os_shell(command, capture=True)
@@ -590,13 +625,13 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):
         return out.stdout
 
 
-    def scp(self, source_path, target_path, user, computing, mode='get'):
+    def scp(self, source, target, user, computing, mode='get'):
+
+        source = self.sanitize_shell_path(source)
+        target = self.sanitize_shell_path(target)
 
-        source_path = source_path.replace(' ', '\ ')
-        target_path = target_path.replace(' ', '\ ')
-    
         # Prepare command
-        command = self.scp_command(source_path, target_path, user, computing, mode)
+        command = self.scp_command(source, target, user, computing, mode)
 
         # Execute_command
         out = os_shell(command, capture=True)
@@ -761,6 +796,41 @@ class FileManagerAPI(PrivateGETAPI, PrivatePOSTAPI):
             return Response(data, status=status.HTTP_200_OK)
             
 
+        elif mode == 'addfolder':
+            logger.debug('Deleting "{}"'.format(path))
+            
+            name = request.GET.get('name', None)
+            if not name:
+                raise ValueError('No folder name set')
+            
+            # Set support vars
+            computing = self.get_computing(path, request)
+            path = '/'+'/'.join(path.split('/')[2:]) + name
+
+            # Get file contents
+            data = self.mkdir(path, request.user, computing)
+
+            # Response data
+            data = { 'data': {
+                            'id': '/{}{}'.format(computing.name, path),
+                            'type': 'folder',
+                            'attributes':{
+                                #'created':  1616415170,
+                                #'modified':   1616415170,
+                                'name': name,
+                                'readable': 1,
+                                #'timestamp':   1616415170,
+                                'writable': 1,
+                                'path': '/{}{}'.format(computing.name, path)                            
+                            }
+                        }
+                    }      
+            
+            
+            # Return file contents
+            return Response(data, status=status.HTTP_200_OK)
+
+
         elif mode == 'rename':
             logger.debug('Renaming "{}"'.format(path))
             
-- 
GitLab