fileBrowser.py 8.96 KB
Newer Older
1
# -*- coding: utf-8 -*-
Marco Mariani's avatar
Marco Mariani committed
2
# vim: set et sts=2:
Marco Mariani's avatar
Marco Mariani committed
3
# pylint: disable-msg=W0311,C0301,C0103,C0111
4 5

import datetime
Marco Mariani's avatar
Marco Mariani committed
6
import md5
Marco Mariani's avatar
Marco Mariani committed
7
import os
8
import re
Marco Mariani's avatar
Marco Mariani committed
9 10
import shutil
import urllib
11 12
import zipfile

Marco Mariani's avatar
Marco Mariani committed
13 14 15 16
import werkzeug
from slapos.runner.utils import realpath, tail, isText


Marco Mariani's avatar
Marco Mariani committed
17
class FileBrowser(object):
Marco Mariani's avatar
Marco Mariani committed
18
  """This class contains all base functions for file browser"""
19 20 21 22

  def __init__(self, config):
    self.config = config

Marco Mariani's avatar
Marco Mariani committed
23 24
  def _realdir(self, dir):
    realdir = realpath(self.config, urllib.unquote(dir))
25 26
    if not realdir:
      raise NameError('Could not load directory %s: Permission denied' % dir)
Marco Mariani's avatar
Marco Mariani committed
27 28 29 30
    return realdir

  def listDirs(self, dir, all=False):
    """List elements of directory 'dir' taken"""
Marco Mariani's avatar
Marco Mariani committed
31 32 33
    html = 'var gsdirs = [], gsfiles = [];'

    dir = urllib.unquote(dir)
Marco Mariani's avatar
Marco Mariani committed
34
    # XXX-Marco 'dir' and 'all' should not shadow builtin names
Marco Mariani's avatar
Marco Mariani committed
35 36 37
    realdir = realpath(self.config, dir)
    if not realdir:
      raise NameError('Could not load directory %s: Permission denied' % dir)
38 39 40

    ldir = sorted(os.listdir(realdir), key=str.lower)
    for f in ldir:
Marco Mariani's avatar
Marco Mariani committed
41
      if f.startswith('.') and not all:  # do not display this file/folder
42
        continue
Marco Mariani's avatar
Marco Mariani committed
43
      ff = os.path.join(dir, f)
44 45
      realfile = os.path.join(realdir, f)
      mdate = datetime.datetime.fromtimestamp(os.path.getmtime(realfile)
Marco Mariani's avatar
Marco Mariani committed
46
                                              ).strftime("%Y-%d-%m %I:%M")
Marco Mariani's avatar
Marco Mariani committed
47
      md5sum = md5.md5(realfile).hexdigest()
48 49
      if not os.path.isdir(realfile):
        size = os.path.getsize(realfile)
Marco Mariani's avatar
Marco Mariani committed
50 51 52 53 54 55
        regex = re.compile("(^.*)\.(.*)", re.VERBOSE)
        ext = regex.sub(r'\2', f)
        if ext == f:
          ext = "unknow"
        else:
          ext = str.lower(ext)
Marco Mariani's avatar
Marco Mariani committed
56
        html += 'gsfiles.push(new gsItem("1", "%s", "%s", "%s", "%s", "%s", "%s"));' % (f, ff, size, md5sum, ext, mdate)
57
      else:
Marco Mariani's avatar
Marco Mariani committed
58
        html += 'gsdirs.push(new gsItem("2", "%s", "%s", "0", "%s", "dir", "%s"));' % (f, ff, md5sum, mdate)
59 60
    return html

61
  def fancylistDirs(self, dir, key, listfiles, all=False):
62 63 64 65 66 67 68 69 70 71 72 73 74 75
    dir = urllib.unquote(dir)
    realdir = realpath(self.config, dir)
    if not realdir:
      raise NameError('Could not load directory %s: Permission denied' % dir)
    fileList = []
    dirList = []
    i = 0
    ldir = sorted(os.listdir(realdir), key=str.lower)
    for f in ldir:
      if f.startswith('.') and not all:  # do not display this file/folder
        continue
      ff = os.path.join(dir, f)
      realfile = os.path.join(realdir, f)
      identity = "%s%s" % (key, i)
76
      if os.path.isdir(realfile):
77 78 79
        dirList.append({"title": f, "key": identity,
                        "folder":True, "lazy":True, "path": ff})
      elif listfiles:
80 81 82 83 84
        regex = re.compile("(^.*)\.(.*)", re.VERBOSE)
        ext = regex.sub(r'\2', f)
        if ext == f:
          ext = ""
        fileList.append({"title": f, "key": identity,
85
                        "extraClasses": "ext_"+ext, "path": ff})
86
      i+=1
87
    return (dirList + fileList)
88

89 90
  def makeDirectory(self, dir, filename):
    """Create a directory"""
Marco Mariani's avatar
Marco Mariani committed
91
    realdir = self._realdir(dir)
92 93 94
    folder = os.path.join(realdir, filename)
    if not os.path.exists(folder):
      os.mkdir(folder, 0744)
95
      return "{result: '1'}"
96
    else:
97
      return "{result: '0'}"
98 99 100

  def makeFile(self, dir, filename):
    """Create a file in a directory dir taken"""
Marco Mariani's avatar
Marco Mariani committed
101
    realdir = self._realdir(dir)
Marco Mariani's avatar
Marco Mariani committed
102 103 104
    fout = os.path.join(realdir, filename)
    if not os.path.exists(fout):
      open(fout, 'w')
105
      return "var responce = {result: '1'}"
106
    else:
107
      return "{result: '0'}"
108 109 110

  def deleteItem(self, dir, files):
    """Delete a list of files or directories"""
Marco Mariani's avatar
Marco Mariani committed
111
    # XXX-Marco do not shadow 'dir'
Marco Mariani's avatar
Marco Mariani committed
112
    realdir = self._realdir(dir)
113 114
    lfiles = urllib.unquote(files).split(',,,')
    try:
Marco Mariani's avatar
Marco Mariani committed
115
      # XXX-Marco do not shadow 'file'
116 117 118
      for item in lfiles:
        filepath = os.path.join(realdir, item)
        if not item or not os.path.exists(filepath):
Marco Mariani's avatar
Marco Mariani committed
119
          continue  # silent skip file....
120
        details = filepath.split('/')
Marco Mariani's avatar
Marco Mariani committed
121
        last = details[-1]
122
        if last and last.startswith('.'):
Marco Mariani's avatar
Marco Mariani committed
123
          continue  # cannot delete this file/directory, to prevent security
124 125
        if os.path.isdir(filepath):
          shutil.rmtree(filepath)
126
        else:
127
          os.unlink(filepath)
Marco Mariani's avatar
Marco Mariani committed
128
    except Exception as e:
129
      return str(e)
130
    return "{result: '1'}"
131 132 133

  def copyItem(self, dir, files, del_source=False):
    """Copy a list of files or directory to dir"""
Marco Mariani's avatar
Marco Mariani committed
134
    realdir = self._realdir(dir)
135 136
    lfiles = urllib.unquote(files).split(',,,')
    try:
Marco Mariani's avatar
Marco Mariani committed
137
      # XXX-Marco do not shadow 'file'
138 139 140 141 142 143
      for file in lfiles:
        realfile = realpath(self.config, file)
        if not realfile:
          raise NameError('Could not load file or directory %s: Permission denied' % file)
        #prepare destination file
        details = realfile.split('/')
Marco Mariani's avatar
Marco Mariani committed
144
        dest = os.path.join(realdir, details[-1])
145
        if os.path.exists(dest):
Marco Mariani's avatar
Marco Mariani committed
146
          raise NameError('NOT ALLOWED OPERATION : File or directory already exists')
147 148 149 150 151 152 153 154
        if os.path.isdir(realfile):
          shutil.copytree(realfile, dest)
          if del_source:
            shutil.rmtree(realfile)
        else:
          shutil.copy(realfile, dest)
          if del_source:
            os.unlink(realfile)
Marco Mariani's avatar
Marco Mariani committed
155
    except Exception as e:
156
      return str(e)
157
    return "{result: '1'}"
158 159 160

  def rename(self, dir, filename, newfilename):
    """Rename file or directory to dir/filename"""
Marco Mariani's avatar
Marco Mariani committed
161
    realdir = self._realdir(dir)
162 163 164 165 166 167
    realfile = realpath(self.config, urllib.unquote(filename))
    if not realfile:
      raise NameError('Could not load directory %s: Permission denied' % filename)
    tofile = os.path.join(realdir, newfilename)
    if not os.path.exists(tofile):
      os.rename(realfile, tofile)
168
      return "{result: '1'}"
Marco Mariani's avatar
Marco Mariani committed
169
    raise NameError('NOT ALLOWED OPERATION : File or directory already exists')
170 171 172

  def copyAsFile(self, dir, filename, newfilename):
    """Copy file or directory to dir/filename"""
Marco Mariani's avatar
Marco Mariani committed
173
    realdir = self._realdir(dir)
174 175 176
    fromfile = os.path.join(realdir, filename)
    tofile = os.path.join(realdir, newfilename)
    if not os.path.exists(fromfile):
Marco Mariani's avatar
Marco Mariani committed
177
      raise NameError('NOT ALLOWED OPERATION : File or directory does not exist')
178 179 180 181
    while os.path.exists(tofile):
      tofile += "_1"
    shutil.copy(fromfile, tofile)
    return "{result: '1'}"
182 183

  def uploadFile(self, dir, files):
Marco Mariani's avatar
Marco Mariani committed
184
    """Upload a list of files in directory dir"""
Marco Mariani's avatar
Marco Mariani committed
185
    realdir = self._realdir(dir)
186 187
    for file in files:
      if files[file]:
Marco Mariani's avatar
Marco Mariani committed
188
        filename = werkzeug.secure_filename(files[file].filename)
189 190
        if not os.path.exists(os.path.join(dir, filename)):
          files[file].save(os.path.join(realdir, filename))
191
    return "{result: '1'}"
192 193 194

  def downloadFile(self, dir, filename):
    """Download file dir/filename"""
Marco Mariani's avatar
Marco Mariani committed
195
    realdir = self._realdir(dir)
196 197 198 199 200 201 202 203
    file = os.path.join(realdir, urllib.unquote(filename))
    if not os.path.exists(file):
      raise NameError('NOT ALLOWED OPERATION : File or directory does not exist %s'
                      % os.path.join(dir, filename))
    return file

  def zipFile(self, dir, filename, newfilename):
    """Add filename to archive as newfilename"""
Marco Mariani's avatar
Marco Mariani committed
204
    realdir = self._realdir(dir)
205 206 207
    tozip = os.path.join(realdir, newfilename)
    fromzip = os.path.join(realdir, filename)
    if not os.path.exists(fromzip):
Marco Mariani's avatar
Marco Mariani committed
208
      raise NameError('NOT ALLOWED OPERATION : File or directory does not exist')
209 210 211 212
    if not os.path.exists(tozip):
      zip = zipfile.ZipFile(tozip, 'w', zipfile.ZIP_DEFLATED)
      if os.path.isdir(fromzip):
        rootlen = len(fromzip) + 1
Marco Mariani's avatar
Marco Mariani committed
213 214 215 216
        for base, _, files in os.walk(fromzip):
          for filename in files:
            fn = os.path.join(base, filename).encode("utf-8")
            zip.write(fn, fn[rootlen:])       # XXX can fail if 'fromzip' contains multibyte characters
217 218 219
      else:
        zip.write(fromzip)
      zip.close()
220
      return "{result: '1'}"
Marco Mariani's avatar
Marco Mariani committed
221
    raise NameError('NOT ALLOWED OPERATION : File or directory already exists')
222 223 224

  def unzipFile(self, dir, filename, newfilename):
    """Extract a zipped archive"""
Marco Mariani's avatar
Marco Mariani committed
225
    realdir = self._realdir(dir)
226 227 228
    target = os.path.join(realdir, newfilename)
    archive = os.path.join(realdir, filename)
    if not os.path.exists(archive):
Marco Mariani's avatar
Marco Mariani committed
229
      raise NameError('NOT ALLOWED OPERATION : File or directory does not exist')
230 231 232 233 234 235 236 237
    if not os.path.exists(target):
      zip = zipfile.ZipFile(archive)
      #member = zip.namelist()
      zip.extractall(target)
      #if len(member) > 1:
      #  zip.extractall(target)
      #else:
      #  zip.extract(member[0], newfilename)
238
      return "{result: '1'}"
Marco Mariani's avatar
Marco Mariani committed
239
    raise NameError('NOT ALLOWED OPERATION : File or directory already exists')
240

Alain Takoudjou's avatar
Alain Takoudjou committed
241
  def readFile(self, dir, filename, truncate=False):
242 243 244 245 246 247 248
    """Read file dir/filename and return content"""
    realfile = realpath(self.config, os.path.join(urllib.unquote(dir),
                        urllib.unquote(filename)))
    if not realfile:
      raise NameError('Could not load directory %s: Permission denied' % dir)
    if not isText(realfile):
      return "FILE ERROR: Cannot display binary file, please open a text file only!"
Alain Takoudjou's avatar
Alain Takoudjou committed
249
    if not truncate:
250
      return open(realfile).read()
251
    else:
252
      return tail(open(realfile), 0)