fileBrowser.py 8 KB
Newer Older
1
# -*- coding: utf-8 -*-
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"""
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
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)
56
        html += 'gsfiles.push(new gsItem("1", "%s", "%s", "%s", "%s", "%s", "%s"));' % (f, ff, size, md5sum, ext, mdate)
57
      else:
58
        html += 'gsdirs.push(new gsItem("2", "%s", "%s", "0", "%s", "dir", "%s"));' % (f, ff, md5sum, mdate)
59 60 61 62
    return html

  def makeDirectory(self, dir, filename):
    """Create a directory"""
Marco Mariani's avatar
Marco Mariani committed
63
    realdir = self._realdir(dir)
64 65 66
    folder = os.path.join(realdir, filename)
    if not os.path.exists(folder):
      os.mkdir(folder, 0744)
67
      return "{result: '1'}"
68
    else:
69
      return "{result: '0'}"
70 71 72

  def makeFile(self, dir, filename):
    """Create a file in a directory dir taken"""
Marco Mariani's avatar
Marco Mariani committed
73
    realdir = self._realdir(dir)
Marco Mariani's avatar
Marco Mariani committed
74 75 76
    fout = os.path.join(realdir, filename)
    if not os.path.exists(fout):
      open(fout, 'w')
77
      return "var responce = {result: '1'}"
78
    else:
79
      return "{result: '0'}"
80 81 82

  def deleteItem(self, dir, files):
    """Delete a list of files or directories"""
Marco Mariani's avatar
Marco Mariani committed
83
    # XXX-Marco do not shadow 'dir'
Marco Mariani's avatar
Marco Mariani committed
84
    realdir = self._realdir(dir)
85 86
    lfiles = urllib.unquote(files).split(',,,')
    try:
Marco Mariani's avatar
Marco Mariani committed
87
      # XXX-Marco do not shadow 'file'
88 89 90
      for file in lfiles:
        file = os.path.join(realdir, file)
        if not os.path.exists(file):
Marco Mariani's avatar
Marco Mariani committed
91
          continue  # silent skip file....
92
        details = file.split('/')
Marco Mariani's avatar
Marco Mariani committed
93
        last = details[-1]
94
        if last and last.startswith('.'):
Marco Mariani's avatar
Marco Mariani committed
95
          continue  # cannot delete this file/directory, to prevent security
96 97 98 99
        if os.path.isdir(file):
          shutil.rmtree(file)
        else:
          os.unlink(file)
100
    except Exception as e:
101
      return str(e)
102
    return "{result: '1'}"
103 104 105

  def copyItem(self, dir, files, del_source=False):
    """Copy a list of files or directory to dir"""
Marco Mariani's avatar
Marco Mariani committed
106
    realdir = self._realdir(dir)
107 108
    lfiles = urllib.unquote(files).split(',,,')
    try:
Marco Mariani's avatar
Marco Mariani committed
109
      # XXX-Marco do not shadow 'file'
110 111 112 113 114 115
      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
116
        dest = os.path.join(realdir, details[-1])
117
        if os.path.exists(dest):
Marco Mariani's avatar
Marco Mariani committed
118
          raise NameError('NOT ALLOWED OPERATION : File or directory already exists')
119 120 121 122 123 124 125 126
        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)
127
    except Exception as e:
128
      return str(e)
129
    return "{result: '1'}"
130 131 132

  def rename(self, dir, filename, newfilename):
    """Rename file or directory to dir/filename"""
Marco Mariani's avatar
Marco Mariani committed
133
    realdir = self._realdir(dir)
134 135 136 137 138 139
    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)
140
      return "{result: '1'}"
Marco Mariani's avatar
Marco Mariani committed
141
    raise NameError('NOT ALLOWED OPERATION : File or directory already exists')
142 143 144

  def copyAsFile(self, dir, filename, newfilename):
    """Copy file or directory to dir/filename"""
Marco Mariani's avatar
Marco Mariani committed
145
    realdir = self._realdir(dir)
146 147 148
    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
149
      raise NameError('NOT ALLOWED OPERATION : File or directory does not exist')
150 151
    if not os.path.exists(tofile):
      shutil.copy(fromfile, tofile)
152
      return "{result: '1'}"
Marco Mariani's avatar
Marco Mariani committed
153
    raise NameError('NOT ALLOWED OPERATION : File or directory already exists')
154 155

  def uploadFile(self, dir, files):
Marco Mariani's avatar
Marco Mariani committed
156
    """Upload a list of files in directory dir"""
Marco Mariani's avatar
Marco Mariani committed
157
    realdir = self._realdir(dir)
158 159
    for file in files:
      if files[file]:
Marco Mariani's avatar
Marco Mariani committed
160
        filename = werkzeug.secure_filename(files[file].filename)
161 162
        if not os.path.exists(os.path.join(dir, filename)):
          files[file].save(os.path.join(realdir, filename))
163
    return "{result: '1'}"
164 165 166

  def downloadFile(self, dir, filename):
    """Download file dir/filename"""
Marco Mariani's avatar
Marco Mariani committed
167
    realdir = self._realdir(dir)
168 169 170 171 172 173 174 175
    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
176
    realdir = self._realdir(dir)
177 178 179
    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
180
      raise NameError('NOT ALLOWED OPERATION : File or directory does not exist')
181 182 183 184
    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
185 186 187 188
        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
189 190 191
      else:
        zip.write(fromzip)
      zip.close()
192
      return "{result: '1'}"
Marco Mariani's avatar
Marco Mariani committed
193
    raise NameError('NOT ALLOWED OPERATION : File or directory already exists')
194 195 196

  def unzipFile(self, dir, filename, newfilename):
    """Extract a zipped archive"""
Marco Mariani's avatar
Marco Mariani committed
197
    realdir = self._realdir(dir)
198 199 200
    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
201
      raise NameError('NOT ALLOWED OPERATION : File or directory does not exist')
202 203 204 205 206 207 208 209
    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)
210
      return "{result: '1'}"
Marco Mariani's avatar
Marco Mariani committed
211
    raise NameError('NOT ALLOWED OPERATION : File or directory already exists')
212

213
  def readFile(self, dir, filename, truncate=False):
214 215 216 217 218 219 220
    """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!"
221
    if not truncate:
222
      return open(realfile).read()
223
    else:
224
      return tail(open(realfile), 0)