Mini Shell
#!/usr/bin/env python
#
# Connector for elFinder File Manager
# author Troex Nevelin <troex@fury.scancode.ru>
import cgi
import hashlib
import json
import mimetypes
import os
import os.path
import re
import shutil
import sys
import time
from datetime import datetime
class connector():
"""Connector for elFinder"""
_options = {
'root': '',
'URL': '',
'rootAlias': 'Home',
'dotFiles': False,
'dirSize': True,
'fileMode': 0644,
'dirMode': 0755,
'imgLib': 'auto',
'tmbDir': '.tmb',
'tmbAtOnce': 5,
'tmbSize': 48,
'fileURL': True,
'uploadMaxSize': 256,
'uploadWriteChunk': 8192,
'uploadAllow': [],
'uploadDeny': [],
'uploadOrder': ['deny', 'allow'],
# 'aclObj': None, # TODO
# 'aclRole': 'user', # TODO
'defaults': {
'read': True,
'write': True,
'rm': True
},
'perms': {},
'archiveMimes': {},
'archivers': {},
'disabled': [],
'debug': False
}
_commands = {
'open': '__open',
'reload': '__reload',
'mkdir': '__mkdir',
'mkfile': '__mkfile',
'rename': '__rename',
'upload': '__upload',
'paste': '__paste',
'rm': '__rm',
'duplicate': '__duplicate',
'read': '__read',
'edit': '__edit',
'extract': '__extract',
'archive': '__archive',
'resize': '__resize',
'tmb': '__thumbnails',
'ping': '__ping'
}
_mimeType = {
# text
'txt': 'text/plain',
'php': 'text/x-php',
'html': 'text/html',
'htm': 'text/html',
'js' : 'text/javascript',
'css': 'text/css',
'rtf': 'text/rtf',
'rtfd': 'text/rtfd',
'py' : 'text/x-python',
'java': 'text/x-java-source',
'rb' : 'text/x-ruby',
'sh' : 'text/x-shellscript',
'pl' : 'text/x-perl',
'sql': 'text/x-sql',
# apps
'doc': 'application/msword',
'ogg': 'application/ogg',
'7z': 'application/x-7z-compressed',
# video
'ogm': 'appllication/ogm',
'mkv': 'video/x-matroska'
}
_time = 0
_request = {}
_response = {}
_errorData = {}
_form = {}
_im = None
_sp = None
_today = 0
_yesterday = 0
def __init__(self, opts):
self._response['debug'] = {}
if self._options['debug']:
self._time = time.time()
t = datetime.fromtimestamp(self._time)
self._today = time.mktime(datetime(t.year, t.month, t.day).timetuple())
self._yesterday = self._today - 86400
import cgitb
cgitb.enable()
for opt in opts:
self._options[opt] = opts.get(opt)
self._options['URL'] = self._options['URL'].rstrip('/')
self._options['root'] = self._options['root'].rstrip(os.sep)
self.__debug('URL', self._options['URL'])
self.__debug('root', self._options['root'])
for cmd in self._options['disabled']:
if cmd in self._commands:
del self._commands[cmd]
if self._options['tmbDir']:
self._options['tmbDir'] = os.path.join(self._options['root'], self._options['tmbDir'])
if not os.path.exists(self._options['tmbDir']):
self._options['tmbDir'] = False
def run(self):
rootOk = True
if not os.path.exists(self._options['root']) or self._options['root'] == '':
rootOk = False
self._response['error'] = 'Invalid backend configuration'
elif not self.__isAllowed(self._options['root'], 'read'):
rootOk = False
self._response['error'] = 'Access denied'
possible_fields = ['cmd', 'target', 'targets[]', 'current', 'tree', 'name',
'content', 'src', 'dst', 'cut', 'init', 'type', 'width', 'height']
self._form = cgi.FieldStorage()
for field in possible_fields:
if field in self._form:
self._request[field] = self._form.getvalue(field)
if rootOk is True:
if 'cmd' in self._request:
if self._request['cmd'] in self._commands:
cmd = self._commands[self._request['cmd']]
func = getattr(self, '_' + self.__class__.__name__ + cmd, None)
if callable(func):
try:
func()
except Exception, e:
self._response['error'] = 'Command Failed'
self.__debug('exception', e)
else:
self._response['error'] = 'Unknown command'
else:
self.__open()
if 'init' in self._request:
self.__checkArchivers()
self._response['disabled'] = self._options['disabled']
if not self._options['fileURL']:
url = ''
else:
url = self._options['URL']
self._response['params'] = {
'dotFiles': self._options['dotFiles'],
'uplMaxSize': str(self._options['uploadMaxSize']) + 'M',
'archives': self._options['archiveMimes'],
'extract': self._options['archivers']['extract'].keys(),
'url': url
}
if self._errorData:
self._response['errorData'] = self._errorData
if self._options['debug']:
self.__debug('time', (time.time() - self._time))
else:
if 'debug' in self._response:
del self._response['debug']
if ('cmd' in self._request and self._request['cmd'] == 'upload') or self._options['debug']:
print 'Content-type: text/html\n'
else:
print 'Content-type: application/json\n'
print json.dumps(self._response, indent = bool(self._options['debug']))
return # sys.exit(0)
def __open(self):
"""Open file or directory"""
# try to open file
if 'current' in self._request:
curDir = self.__findDir(self._request['current'], None)
curFile = self.__find(self._request['target'], curDir)
if not curDir or not curFile or os.path.isdir(curFile):
print 'HTTP/1.x 404 Not Found\n\n'
sys.exit('File not found')
if not self.__isAllowed(curDir, 'read') or not self.__isAllowed(curFile, 'read'):
print 'HTTP/1.x 403 Access Denied\n\n'
sys.exit('Access denied')
if os.path.islink(curFile):
curFile = self.__readlink(curFile)
if not curFile or os.path.isdir(curFile):
print 'HTTP/1.x 404 Not Found\n\n'
sys.exit('File not found')
if (
not self.__isAllowed(os.path.dirname(curFile), 'read')
or not self.__isAllowed(curFile, 'read')
):
print 'HTTP/1.x 403 Access Denied\n\n'
sys.exit('Access denied')
mime = self.__mimetype(curFile)
parts = mime.split('/', 2)
if parts[0] == 'image': disp = 'image'
elif parts[0] == 'text': disp = 'inline'
else: disp = 'attachments'
print 'Content-Type: ' + mime
print 'Content-Disposition: ' + disp + '; filename=' + os.path.basename(curFile)
print 'Content-Location: ' + curFile.replace(self._options['root'], '')
print 'Content-Transfer-Encoding: binary'
print 'Content-Length: ' + str(os.lstat(curFile).st_size)
print 'Connection: close\n'
print open(curFile, 'r').read()
sys.exit(0);
# try dir
else:
path = self._options['root']
if 'target' in self._request:
target = self.__findDir(self._request['target'], None)
if not target:
self._response['error'] = 'Invalid parameters'
elif not self.__isAllowed(target, 'read'):
self._response['error'] = 'Access denied'
else:
path = target
self.__content(path, 'tree' in self._request)
pass
def __rename(self):
"""Rename file or dir"""
current = name = target = None
curDir = curName = newName = None
if 'name' in self._request and 'current' in self._request and 'target' in self._request:
name = self._request['name']
current = self._request['current']
target = self._request['target']
curDir = self.__findDir(current, None)
curName = self.__find(target, curDir)
newName = os.path.join(curDir, name)
if not curDir or not curName:
self._response['error'] = 'File not found'
elif not self.__isAllowed(curDir, 'write') and self.__isAllowed(curName, 'rm'):
self._response['error'] = 'Access denied'
elif not self.__checkName(name):
self._response['error'] = 'Invalid name'
elif os.path.exists(newName):
self._response['error'] = 'File or folder with the same name already exists'
else:
self.__rmTmb(curName)
try:
os.rename(curName, newName)
self._response['select'] = [self.__hash(newName)]
self.__content(curDir, os.path.isdir(newName))
except:
self._response['error'] = 'Unable to rename file'
def __mkdir(self):
"""Create new directory"""
current = None
path = None
newDir = None
if 'name' in self._request and 'current' in self._request:
name = self._request['name']
current = self._request['current']
path = self.__findDir(current, None)
newDir = os.path.join(path, name)
if not path:
self._response['error'] = 'Invalid parameters'
elif not self.__isAllowed(path, 'write'):
self._response['error'] = 'Access denied'
elif not self.__checkName(name):
self._response['error'] = 'Invalid name'
elif os.path.exists(newDir):
self._response['error'] = 'File or folder with the same name already exists'
else:
try:
os.mkdir(newDir, int(self._options['dirMode']))
self._response['select'] = [self.__hash(newDir)]
self.__content(path, True)
except:
self._response['error'] = 'Unable to create folder'
def __mkfile(self):
"""Create new file"""
name = current = None
curDir = newFile = None
if 'name' in self._request and 'current' in self._request:
name = self._request['name']
current = self._request['current']
curDir = self.__findDir(current, None)
newFile = os.path.join(curDir, name)
if not curDir or not name:
self._response['error'] = 'Invalid parameters'
elif not self.__isAllowed(curDir, 'write'):
self._response['error'] = 'Access denied'
elif not self.__checkName(name):
self._response['error'] = 'Invalid name'
elif os.path.exists(newFile):
self._response['error'] = 'File or folder with the same name already exists'
else:
try:
open(newFile, 'w').close()
self._response['select'] = [self.__hash(newFile)]
self.__content(curDir, False)
except:
self._response['error'] = 'Unable to create file'
def __rm(self):
current = rmList = None
curDir = rmFile = None
if 'current' in self._request and 'targets[]' in self._request:
current = self._request['current']
rmList = self._request['targets[]']
curDir = self.__findDir(current, None)
if not rmList or not curDir:
self._response['error'] = 'Invalid parameters'
return False
if not isinstance(rmList, list):
rmList = [rmList]
for rm in rmList:
rmFile = self.__find(rm, curDir)
if not rmFile: continue
self.__remove(rmFile)
# TODO if errorData not empty return error
self.__content(curDir, True)
def __upload(self):
try: # Windows needs stdio set for binary mode.
import msvcrt
msvcrt.setmode (0, os.O_BINARY) # stdin = 0
msvcrt.setmode (1, os.O_BINARY) # stdout = 1
except ImportError:
pass
if 'current' in self._request:
curDir = self.__findDir(self._request['current'], None)
if not curDir:
self._response['error'] = 'Invalid parameters'
return
if not self.__isAllowed(curDir, 'write'):
self._response['error'] = 'Access denied'
return
if not 'upload[]' in self._form:
self._response['error'] = 'No file to upload'
return
upFiles = self._form['upload[]']
if not isinstance(upFiles, list):
upFiles = [upFiles]
self._response['select'] = []
total = 0
upSize = 0
maxSize = self._options['uploadMaxSize'] * 1024 * 1024
for up in upFiles:
name = up.filename
if name:
total += 1
name = os.path.basename(name)
if not self.__checkName(name):
self.__errorData(name, 'Invalid name')
else:
name = os.path.join(curDir, name)
try:
f = open(name, 'wb', self._options['uploadWriteChunk'])
for chunk in self.__fbuffer(up.file):
f.write(chunk)
f.close()
upSize += os.lstat(name).st_size
if self.__isUploadAllow(name):
os.chmod(name, self._options['fileMode'])
self._response['select'].append(self.__hash(name))
else:
self.__errorData(name, 'Not allowed file type')
try:
os.unlink(name)
except:
pass
except:
self.__errorData(name, 'Unable to save uploaded file')
if upSize > maxSize:
try:
os.unlink(name)
self.__errorData(name, 'File exceeds the maximum allowed filesize')
except:
pass
# TODO ?
self.__errorData(name, 'File was only partially uploaded')
break
if self._errorData:
if len(self._errorData) == total:
self._response['error'] = 'Unable to upload files'
else:
self._response['error'] = 'Some files was not uploaded'
self.__content(curDir, False)
return
def __paste(self):
if 'current' in self._request and 'src' in self._request and 'dst' in self._request:
curDir = self.__findDir(self._request['current'], None)
src = self.__findDir(self._request['src'], None)
dst = self.__findDir(self._request['dst'], None)
if not curDir or not src or not dst or not 'targets[]' in self._request:
self._response['error'] = 'Invalid parameters'
return
files = self._request['targets[]']
if not isinstance(files, list):
files = [files]
cut = False
if 'cut' in self._request:
if self._request['cut'] == '1':
cut = True
if not self.__isAllowed(src, 'read') or not self.__isAllowed(dst, 'write'):
self._response['error'] = 'Access denied'
return
for fhash in files:
f = self.__find(fhash, src)
if not f:
self._response['error'] = 'File not found'
return
newDst = os.path.join(dst, os.path.basename(f))
if dst.find(f) == 0:
self._response['error'] = 'Unable to copy into itself'
return
if cut:
if not self.__isAllowed(f, 'rm'):
self._response['error'] = 'Move failed'
self._errorData(f, 'Access denied')
self.__content(curDir, True)
return
# TODO thumbs
if os.path.exists(newDst):
self._response['error'] = 'Unable to move files'
self._errorData(f, 'File or folder with the same name already exists')
self.__content(curDir, True)
return
try:
os.rename(f, newDst)
self.__rmTmb(f)
continue
except:
self._response['error'] = 'Unable to move files'
self._errorData(f, 'Unable to move')
self.__content(curDir, True)
return
else:
if not self.__copy(f, newDst):
self._response['error'] = 'Unable to copy files'
self.__content(curDir, True)
return
continue
self.__content(curDir, True)
else:
self._response['error'] = 'Invalid parameters'
return
def __duplicate(self):
if 'current' in self._request and 'target' in self._request:
curDir = self.__findDir(self._request['current'], None)
target = self.__find(self._request['target'], curDir)
if not curDir or not target:
self._response['error'] = 'Invalid parameters'
return
if not self.__isAllowed(target, 'read') or not self.__isAllowed(curDir, 'write'):
self._response['error'] = 'Access denied'
newName = self.__uniqueName(target)
if not self.__copy(target, newName):
self._response['error'] = 'Unable to create file copy'
return
self.__content(curDir, True)
return
def __resize(self):
if not (
'current' in self._request and 'target' in self._request
and 'width' in self._request and 'height' in self._request
):
self._response['error'] = 'Invalid parameters'
return
width = int(self._request['width'])
height = int(self._request['height'])
curDir = self.__findDir(self._request['current'], None)
curFile = self.__find(self._request['target'], curDir)
if width < 1 or height < 1 or not curDir or not curFile:
self._response['error'] = 'Invalid parameters'
return
if not self.__isAllowed(curFile, 'write'):
self._response['error'] = 'Access denied'
return
if not self.__mimetype(curFile).find('image') == 0:
self._response['error'] = 'File is not an image'
return
self.__debug('resize ' + curFile, str(width) + ':' + str(height))
self.__initImgLib()
try:
im = self._im.open(curFile)
imResized = im.resize((width, height), self._im.ANTIALIAS)
imResized.save(curFile)
except Exception, e:
self.__debug('resizeFailed_' + path, str(e))
self._response['error'] = 'Unable to resize image'
return
self._response['select'] = [self.__hash(curFile)]
self.__content(curDir, False)
return
def __thumbnails(self):
if 'current' in self._request:
curDir = self.__findDir(self._request['current'], None)
if not curDir or curDir == self._options['tmbDir']:
return False
else:
return False
self.__initImgLib()
if self.__canCreateTmb():
if self._options['tmbAtOnce'] > 0:
tmbMax = self._options['tmbAtOnce']
else:
tmbMax = 5
self._response['current'] = self.__hash(curDir)
self._response['images'] = {}
i = 0
for f in os.listdir(curDir):
path = os.path.join(curDir, f)
fhash = self.__hash(path)
if self.__canCreateTmb(path) and self.__isAllowed(path, 'read'):
tmb = os.path.join(self._options['tmbDir'], fhash + '.png')
if not os.path.exists(tmb):
if self.__tmb(path, tmb):
self._response['images'].update({
fhash: self.__path2url(tmb)
})
i += 1
if i >= tmbMax:
self._response['tmb'] = True
break
else:
return False
return
def __content(self, path, tree):
"""CWD + CDC + maybe(TREE)"""
self.__cwd(path)
self.__cdc(path)
if tree:
self._response['tree'] = self.__tree(self._options['root'])
def __cwd(self, path):
"""Current Working Directory"""
name = os.path.basename(path)
if path == self._options['root']:
name = self._options['rootAlias']
root = True
else:
root = False
if self._options['rootAlias']:
basename = self._options['rootAlias']
else:
basename = os.path.basename(self._options['root'])
rel = basename + path[len(self._options['root']):]
self._response['cwd'] = {
'hash': self.__hash(path),
'name': name,
'mime': 'directory',
'rel': rel,
'size': 0,
'date': datetime.fromtimestamp(os.stat(path).st_mtime).strftime("%d %b %Y %H:%M"),
'read': True,
'write': self.__isAllowed(path, 'write'),
'rm': not root and self.__isAllowed(path, 'rm')
}
def __cdc(self, path):
"""Current Directory Content"""
files = []
dirs = []
for f in sorted(os.listdir(path)):
if not self.__isAccepted(f): continue
pf = os.path.join(path, f)
info = {}
info = self.__info(pf)
info['hash'] = self.__hash(pf)
if info['mime'] == 'directory':
dirs.append(info)
else:
files.append(info)
dirs.extend(files)
self._response['cdc'] = dirs
def __info(self, path):
mime = ''
filetype = 'file'
if os.path.isfile(path): filetype = 'file'
if os.path.isdir(path): filetype = 'dir'
if os.path.islink(path): filetype = 'link'
stat = os.lstat(path)
statDate = datetime.fromtimestamp(stat.st_mtime)
fdate = ''
if stat.st_mtime >= self._today:
fdate = 'Today ' + statDate.strftime("%H:%M")
elif stat.st_mtime >= self._yesterday and stat.st_mtime < self._today:
fdate = 'Yesterday ' + statDate.strftime("%H:%M")
else:
fdate = statDate.strftime("%d %b %Y %H:%M")
info = {
'name': os.path.basename(path),
'hash': self.__hash(path),
'mime': 'directory' if filetype == 'dir' else self.__mimetype(path),
'date': fdate,
'size': self.__dirSize(path) if filetype == 'dir' else stat.st_size,
'read': self.__isAllowed(path, 'read'),
'write': self.__isAllowed(path, 'write'),
'rm': self.__isAllowed(path, 'rm')
}
if filetype == 'link':
lpath = self.__readlink(path)
if not lpath:
info['mime'] = 'symlink-broken'
return info
if os.path.isdir(lpath):
info['mime'] = 'directory'
else:
info['parent'] = self.__hash(os.path.dirname(lpath))
info['mime'] = self.__mimetype(lpath)
if self._options['rootAlias']:
basename = self._options['rootAlias']
else:
basename = os.path.basename(self._options['root'])
info['link'] = self.__hash(lpath)
info['linkTo'] = basename + lpath[len(self._options['root']):]
info['read'] = info['read'] and self.__isAllowed(lpath, 'read')
info['write'] = info['write'] and self.__isAllowed(lpath, 'write')
info['rm'] = self.__isAllowed(lpath, 'rm')
else:
lpath = False
if not info['mime'] == 'directory':
if self._options['fileURL'] and info['read'] is True:
if lpath:
info['url'] = self.__path2url(lpath)
else:
info['url'] = self.__path2url(path)
if info['mime'][0:5] == 'image':
if self.__canCreateTmb():
dim = self.__getImgSize(path)
if dim:
info['dim'] = dim
info['resize'] = True
# if we are in tmb dir, files are thumbs itself
if os.path.dirname(path) == self._options['tmbDir']:
info['tmb'] = self.__path2url(path)
return info
tmb = os.path.join(self._options['tmbDir'], info['hash'] + '.png')
if os.path.exists(tmb):
tmbUrl = self.__path2url(tmb)
info['tmb'] = tmbUrl
else:
self._response['tmb'] = True
return info
def __tree(self, path):
"""Return directory tree starting from path"""
if not os.path.isdir(path): return ''
if os.path.islink(path): return ''
if path == self._options['root'] and self._options['rootAlias']:
name = self._options['rootAlias']
else:
name = os.path.basename(path)
tree = {
'hash': self.__hash(path),
'name': name,
'read': self.__isAllowed(path, 'read'),
'write': self.__isAllowed(path, 'write'),
'dirs': []
}
if self.__isAllowed(path, 'read'):
for d in sorted(os.listdir(path)):
pd = os.path.join(path, d)
if os.path.isdir(pd) and not os.path.islink(pd) and self.__isAccepted(d):
tree['dirs'].append(self.__tree(pd))
return tree
def __uniqueName(self, path, copy = ' copy'):
curDir = os.path.dirname(path)
curName = os.path.basename(path)
lastDot = curName.rfind('.')
ext = newName = ''
if not os.path.isdir(path) and re.search(r'\..{3}\.(gz|bz|bz2)$', curName):
pos = -7
if curName[-1:] == '2':
pos -= 1
ext = curName[pos:]
oldName = curName[0:pos]
newName = oldName + copy
elif os.path.isdir(path) or lastDot <= 0:
oldName = curName
newName = oldName + copy
pass
else:
ext = curName[lastDot:]
oldName = curName[0:lastDot]
newName = oldName + copy
pos = 0
if oldName[-len(copy):] == copy:
newName = oldName
elif re.search(r'' + copy +'\s\d+$', oldName):
pos = oldName.rfind(copy) + len(copy)
newName = oldName[0:pos]
else:
newPath = os.path.join(curDir, newName + ext)
if not os.path.exists(newPath):
return newPath
# if we are here then copy already exists or making copy of copy
# we will make new indexed copy *black magic*
idx = 1
if pos > 0: idx = int(oldName[pos:])
while True:
idx += 1
newNameExt = newName + ' ' + str(idx) + ext
newPath = os.path.join(curDir, newNameExt)
if not os.path.exists(newPath):
return newPath
# if idx >= 1000: break # possible loop
return
def __remove(self, target):
if not self.__isAllowed(target, 'rm'):
self.__errorData(target, 'Access denied')
if not os.path.isdir(target):
try:
os.unlink(target)
return True
except:
self.__errorData(target, 'Remove failed')
return False
else:
for i in os.listdir(target):
if self.__isAccepted(i):
self.__remove(os.path.join(target, i))
try:
os.rmdir(target)
return True
except:
self.__errorData(target, 'Remove failed')
return False
pass
def __copy(self, src, dst):
dstDir = os.path.dirname(dst)
if not self.__isAllowed(src, 'read'):
self.__errorData(src, 'Access denied')
return False
if not self.__isAllowed(dstDir, 'write'):
self.__errorData(dstDir, 'Access denied')
return False
if os.path.exists(dst):
self.__errorData(dst, 'File or folder with the same name already exists')
return False
if not os.path.isdir(src):
try:
shutil.copyfile(src, dst)
shutil.copymode(src, dst)
return True
except:
self.__errorData(src, 'Unable to copy files')
return False
else:
try:
os.mkdir(dst)
shutil.copymode(src, dst)
except:
self.__errorData(src, 'Unable to copy files')
return False
for i in os.listdir(src):
newSrc = os.path.join(src, i)
newDst = os.path.join(dst, i)
if not self.__copy(newSrc, newDst):
self.__errorData(newSrc, 'Unable to copy files')
return False
return True
def __checkName(self, name):
"""Check for valid file/dir name"""
pattern = r'[\/\\\:\<\>]'
if re.search(pattern, name):
return False
return True
def __findDir(self, fhash, path):
"""Find directory by hash"""
fhash = str(fhash)
if not path:
path = self._options['root']
if fhash == self.__hash(path):
return path
if not os.path.isdir(path):
return None
for d in os.listdir(path):
pd = os.path.join(path, d)
if os.path.isdir(pd) and not os.path.islink(pd):
if fhash == self.__hash(pd):
return pd
else:
ret = self.__findDir(fhash, pd)
if ret:
return ret
return None
def __find(self, fhash, parent):
"""Find file/dir by hash"""
fhash = str(fhash)
if os.path.isdir(parent):
for i in os.listdir(parent):
path = os.path.join(parent, i)
if fhash == self.__hash(path):
return path
return None
def __read(self):
if 'current' in self._request and 'target' in self._request:
curDir = self.__findDir(self._request['current'], None)
curFile = self.__find(self._request['target'], curDir)
if curDir and curFile:
if self.__isAllowed(curFile, 'read'):
self._response['content'] = open(curFile, 'r').read()
else:
self._response['error'] = 'Access denied'
return
self._response['error'] = 'Invalid parameters'
return
def __edit(self):
error = ''
if 'current' in self._request and 'target' in self._request and 'content' in self._request:
curDir = self.__findDir(self._request['current'], None)
curFile = self.__find(self._request['target'], curDir)
error = curFile
if curFile and curDir:
if self.__isAllowed(curFile, 'write'):
try:
f = open(curFile, 'w+')
f.write(self._request['content'])
f.close()
self._response['target'] = self.__info(curFile)
except:
self._response['error'] = 'Unable to write to file'
else:
self._response['error'] = 'Access denied'
return
self._response['error'] = 'Invalid parameters'
return
def __archive(self):
self.__checkArchivers()
if (
not self._options['archivers']['create'] or not 'type' in self._request
or not 'current' in self._request
or not 'targets[]' in self._request
or not 'name' in self._request
):
self._response['error'] = 'Invalid parameters'
return
curDir = self.__findDir(self._request['current'], None)
archiveType = self._request['type']
if (
not archiveType in self._options['archivers']['create']
or not archiveType in self._options['archiveMimes']
or not curDir
or not self.__isAllowed(curDir, 'write')
):
self._response['error'] = 'Unable to create archive'
return
files = self._request['targets[]']
if not isinstance(files, list):
files = [files]
realFiles = []
for fhash in files:
curFile = self.__find(fhash, curDir)
if not curFile:
self._response['error'] = 'File not found'
return
realFiles.append(os.path.basename(curFile))
arc = self._options['archivers']['create'][archiveType]
if len(realFiles) > 1:
archiveName = self._request['name']
else:
archiveName = realFiles[0]
archiveName += '.' + arc['ext']
archiveName = self.__uniqueName(archiveName, '')
archivePath = os.path.join(curDir, archiveName)
cmd = [arc['cmd']]
for a in arc['argc'].split():
cmd.append(a)
cmd.append(archiveName)
for f in realFiles:
cmd.append(f)
curCwd = os.getcwd()
os.chdir(curDir)
self.__runSubProcess(cmd)
os.chdir(curCwd)
if os.path.exists(archivePath):
self.__content(curDir, False)
self._response['select'] = [self.__hash(archivePath)]
else:
self._response['error'] = 'Unable to create archive'
return
def __extract(self):
if not 'current' in self._request or not 'target' in self._request:
self._response['error'] = 'Invalid parameters'
return
curDir = self.__findDir(self._request['current'], None)
curFile = self.__find(self._request['target'], curDir)
mime = self.__mimetype(curFile)
self.__checkArchivers()
if (
not mime in self._options['archivers']['extract']
or not curDir
or not curFile
or not self.__isAllowed(curDir, 'write')
):
self._response['error'] = 'Invalid parameters'
return
arc = self._options['archivers']['extract'][mime]
cmd = [arc['cmd']]
for a in arc['argc'].split():
cmd.append(a)
cmd.append(curFile)
curCwd = os.getcwd()
os.chdir(curDir)
ret = self.__runSubProcess(cmd)
os.chdir(curCwd)
if ret:
self.__content(curDir, True)
else:
self._response['error'] = 'Unable to extract files from archive'
return
def __ping(self):
"""Workaround for Safari"""
print 'Connection: close\n'
sys.exit(0)
def __mimetype(self, path):
mime = mimetypes.guess_type(path)[0] or 'unknown'
ext = path[path.rfind('.') + 1:]
if mime == 'unknown' and ('.' + ext) in mimetypes.types_map:
mime = mimetypes.types_map['.' + ext]
if mime == 'text/plain' and ext == 'pl':
mime = self._mimeType[ext]
if mime == 'application/vnd.ms-office' and ext == 'doc':
mime = self._mimeType[ext]
if mime == 'unknown':
if os.path.basename(path) in ['README', 'ChangeLog']:
mime = 'text/plain'
else:
if ext in self._mimeType:
mime = self._mimeType[ext]
# self.__debug('mime ' + os.path.basename(path), ext + ' ' + mime)
return mime
def __tmb(self, path, tmb):
try:
im = self._im.open(path).copy()
size = self._options['tmbSize'], self._options['tmbSize']
box = self.__cropTuple(im.size)
if box:
im = im.crop(box)
im.thumbnail(size, self._im.ANTIALIAS)
im.save(tmb, 'PNG')
except Exception, e:
self.__debug('tmbFailed_' + path, str(e))
return False
return True
def __rmTmb(self, path):
tmb = self.__tmbPath(path)
if self._options['tmbDir']:
if os.path.exists(tmb):
try:
os.unlink(tmb)
except:
pass
def __cropTuple(self, size):
w, h = size
if w > h: # landscape
l = int((w - h) / 2)
u = 0
r = l + h
d = h
return (l, u, r, d)
elif h > w: # portrait
l = 0
u = int((h - w) / 2)
r = w
d = u + w
return (l, u, r, d)
else: # cube
pass
return False
def __readlink(self, path):
"""Read link and return real path if not broken"""
target = os.readlink(path);
if not target[0] == '/':
target = os.path.join(os.path.dirname(path), target)
target = os.path.normpath(target)
if os.path.exists(target):
if not target.find(self._options['root']) == -1:
return target
return False
def __dirSize(self, path):
total_size = 0
if self._options['dirSize']:
for dirpath, dirnames, filenames in os.walk(path):
for f in filenames:
fp = os.path.join(dirpath, f)
if os.path.exists(fp):
total_size += os.stat(fp).st_size
else:
total_size = os.lstat(path).st_size
return total_size
def __fbuffer(self, f, chunk_size = _options['uploadWriteChunk']):
while True:
chunk = f.read(chunk_size)
if not chunk: break
yield chunk
def __canCreateTmb(self, path = None):
if self._options['imgLib'] and self._options['tmbDir']:
if path is not None:
mime = self.__mimetype(path)
if not mime[0:5] == 'image':
return False
return True
else:
return False
def __tmbPath(self, path):
tmb = False
if self._options['tmbDir']:
if not os.path.dirname(path) == self._options['tmbDir']:
tmb = os.path.join(self._options['tmbDir'], self.__hash(path) + '.png')
return tmb
def __isUploadAllow(self, name):
allow = False
deny = False
mime = self.__mimetype(name)
if 'all' in self._options['uploadAllow']:
allow = True
else:
for a in self._options['uploadAllow']:
if mime.find(a) == 0:
allow = True
if 'all' in self._options['uploadDeny']:
deny = True
else:
for d in self._options['uploadDeny']:
if mime.find(d) == 0:
deny = True
if self._options['uploadOrder'][0] == 'allow': # ,deny
if deny is True:
return False
elif allow is True:
return True
else:
return False
else: # deny,allow
if allow is True:
return True
elif deny is True:
return False
else:
return True
def __isAccepted(self, target):
if target == '.' or target == '..':
return False
if target[0:1] == '.' and not self._options['dotFiles']:
return False
return True
def __isAllowed(self, path, access):
if not os.path.exists(path):
return False
if access == 'read':
if not os.access(path, os.R_OK):
self.__errorData(path, access)
return False
elif access == 'write':
if not os.access(path, os.W_OK):
self.__errorData(path, access)
return False
elif access == 'rm':
if not os.access(os.path.dirname(path), os.W_OK):
self.__errorData(path, access)
return False
else:
return False
path = path[len(os.path.normpath(self._options['root'])):]
for ppath in self._options['perms']:
regex = r'' + ppath
if re.search(regex, path) and access in self._options['perms'][ppath]:
return self._options['perms'][ppath][access]
return self._options['defaults'][access]
def __hash(self, path):
"""Hash of the path"""
m = hashlib.md5()
m.update(path)
return str(m.hexdigest())
def __path2url(self, path):
curDir = path
length = len(self._options['root'])
url = str(self._options['URL'] + curDir[length:]).replace(os.sep, '/')
try:
import urllib
url = urllib.quote(url, '/:~')
except:
pass
return url
def __errorData(self, path, msg):
"""Collect error/warning messages"""
self._errorData[path] = msg
def __initImgLib(self):
if not self._options['imgLib'] is False and self._im is None:
try:
import Image
Image
self._im = Image
self._options['imgLib'] = 'PIL'
except:
self._options['imgLib'] = False
self._im = False
self.__debug('imgLib', self._options['imgLib'])
return self._options['imgLib']
def __getImgSize(self, path):
self.__initImgLib();
if self.__canCreateTmb():
try:
im = self._im.open(path)
return str(im.size[0]) + 'x' + str(im.size[1])
except:
pass
return False
def __debug(self, k, v):
if self._options['debug']:
self._response['debug'].update({k: v})
return
def __checkArchivers(self):
# import subprocess
# sp = subprocess.Popen(['tar', '--version'], shell = False,
# stdout = subprocess.PIPE, stderr=subprocess.PIPE)
# out, err = sp.communicate()
# print 'out:', out, '\nerr:', err, '\n'
archive = { 'create': {}, 'extract': {} }
c = archive['create']
e = archive['extract']
tar = self.__runSubProcess(['tar', '--version'])
gzip = self.__runSubProcess(['gzip', '--version'])
bzip2 = self.__runSubProcess(['bzip2', '--version'])
zipc = self.__runSubProcess(['zip', '--version'])
unzip = self.__runSubProcess(['unzip', '--help'])
rar = self.__runSubProcess(['rar', '--version'], validReturn = [0, 7])
unrar = self.__runSubProcess(['unrar'], validReturn = [0, 7])
p7z = self.__runSubProcess(['7z', '--help'])
p7za = self.__runSubProcess(['7za', '--help'])
p7zr = self.__runSubProcess(['7zr', '--help'])
# tar = False
tar = gzip = bzip2 = zipc = unzip = rar = unrar = False
# print tar, gzip, bzip2, zipc, unzip, rar, unrar, p7z, p7za, p7zr
if tar:
mime = 'application/x-tar'
c.update({mime: {'cmd': 'tar', 'argc': '-cf', 'ext': 'tar'}})
e.update({mime: {'cmd': 'tar', 'argc': '-xf', 'ext': 'tar'}})
if tar and gzip:
mime = 'application/x-gzip'
c.update({mime: {'cmd': 'tar', 'argc': '-czf', 'ext': 'tar.gz'}})
e.update({mime: {'cmd': 'tar', 'argc': '-xzf', 'ext': 'tar.gz'}})
if tar and bzip2:
mime = 'application/x-bzip2'
c.update({mime: {'cmd': 'tar', 'argc': '-cjf', 'ext': 'tar.bz2'}})
e.update({mime: {'cmd': 'tar', 'argc': '-xjf', 'ext': 'tar.bz2'}})
mime = 'application/zip'
if zipc:
c.update({mime: {'cmd': 'zip', 'argc': '-r9', 'ext': 'zip'}})
if unzip:
e.update({mime: {'cmd': 'unzip', 'argc': '', 'ext': 'zip'}})
mime = 'application/x-rar'
if rar:
c.update({mime: {'cmd': 'rar', 'argc': 'a', 'ext': 'rar'}})
e.update({mime: {'cmd': 'rar', 'argc': 'x', 'ext': 'rar'}})
elif unrar:
e.update({mime: {'cmd': 'unrar', 'argc': 'x', 'ext': 'rar'}})
p7zip = None
if p7z:
p7zip = '7z'
elif p7za:
p7zip = '7za'
elif p7zr:
p7zip = '7zr'
if p7zip:
mime = 'application/x-7z-compressed'
c.update({mime: {'cmd': p7zip, 'argc': 'a -t7z', 'ext': '7z'}})
e.update({mime: {'cmd': p7zip, 'argc': 'e -y', 'ext': '7z'}})
mime = 'application/x-tar'
if not mime in c:
c.update({mime: {'cmd': p7zip, 'argc': 'a -ttar', 'ext': 'tar'}})
if not mime in e:
e.update({mime: {'cmd': p7zip, 'argc': 'e -y', 'ext': 'tar'}})
mime = 'application/x-gzip'
if not mime in c:
c.update({mime: {'cmd': p7zip, 'argc': 'a -tgzip', 'ext': 'gz'}})
if not mime in e:
e.update({mime: {'cmd': p7zip, 'argc': 'e -y', 'ext': 'tar.gz'}})
mime = 'application/x-bzip2'
if not mime in c:
c.update({mime: {'cmd': p7zip, 'argc': 'a -tbzip2', 'ext': 'bz2'}})
if not mime in e:
e.update({mime: {'cmd': p7zip, 'argc': 'e -y', 'ext': 'tar.bz2'}})
mime = 'application/zip'
if not mime in c:
c.update({mime: {'cmd': p7zip, 'argc': 'a -tzip', 'ext': 'zip'}})
if not mime in e:
e.update({mime: {'cmd': p7zip, 'argc': 'e -y', 'ext': 'zip'}})
if not self._options['archiveMimes']:
self._options['archiveMimes'] = c.keys()
else:
pass
self._options['archivers'] = archive
pass
def __runSubProcess(self, cmd, validReturn = [0]):
if self._sp is None:
import subprocess
self._sp = subprocess
try:
sp = self._sp.Popen(cmd, shell = False, stdout = self._sp.PIPE, stderr = self._sp.PIPE)
out, err = sp.communicate()
ret = sp.returncode
# print cmd, ret, out, err
except:
return False
if not ret in validReturn:
return False
return True
Zerion Mini Shell 1.0