Commit 75268a61 authored by Martin Urban's avatar Martin Urban
Browse files

Filesystem: changes for being able to use remote debugging, added some checks for read permission

parent 697e98fd
......@@ -94,8 +94,14 @@ class FSAjaxController(AbstractController):
window_id=self.get('window_id'),
view_id=self.get('view_id'),
watch_id=watch_id)
if count < 0:
if count == -1:
raise MessageException("%s does not exist" % path)
# instead of raising an exception we return -2 as count to account for the fact
# that the user does not have the permission to read the path in the GUI
# elif count == -2:
# raise MessageException("You do not have rights to read %s." % path)
elif type(count) != int:
raise MessageException(count)
else:
return {"count": count}
......
......@@ -21,6 +21,7 @@ import vispa
try:
import Image
import ImageFilter
HAVE_PIL = True
except:
HAVE_PIL = False
......@@ -34,6 +35,7 @@ if not HAVE_PIL:
logger = logging.getLogger(__name__)
def get_file_info(base, name):
root, ext = os.path.splitext(name)
info = {
......@@ -45,7 +47,7 @@ def get_file_info(base, name):
try:
fullpath = os.path.join(base, name)
stats = os.lstat(fullpath)
if stat.S_ISLNK(stats.st_mode):
realfullpath = os.path.realpath(fullpath)
info.update({
......@@ -65,8 +67,8 @@ def get_file_info(base, name):
return info
class FileSystem(object):
class FileSystem(object):
FILE_EXTENSIONS = ["png", "jpg", "jpeg", "bmp", "ps", "eps", "pdf",
"txt", "xml", "py", "c", "cpp", "root", "pxlio"]
BROWSER_EXTENSIONS = ["png", "jpg", "jpeg", "bmp"]
......@@ -83,7 +85,7 @@ class FileSystem(object):
self.watchservice = WatchService()
self._userid = userid
self._workspaceid = workspaceid
def __del__(self):
self.close()
......@@ -103,7 +105,7 @@ class FileSystem(object):
def close(self):
if self.watchservice:
self.watchservice.stop()
def get_mime_type(self, filepath):
filepath = self.expand(filepath)
mime, encoding = guess_type(filepath)
......@@ -149,7 +151,13 @@ class FileSystem(object):
# actual function
path = self.expand(path)
if os.path.exists(path):
return len(os.listdir(path))
# import pydevd
# pydevd.settrace('localhost', port=44847, stdoutToServer=False, stderrToServer=False)
# check if reading the file is allowed
if not self.checkPermissions(path, os.R_OK):
return -2
length = len(os.listdir(path))
return length
else:
return -1
......@@ -165,11 +173,11 @@ class FileSystem(object):
# actual function
filter = re.compile(filter) if filter else None
base = self.expand(path)
filelist = [get_file_info(base, name) for name in os.listdir(base) if
not (hide_hidden and name.startswith('.')) and
(not filter or bool(filter.search(name)) == reverse)]
filelist = [i for i in filelist if 'size' in i] # ignore failed file info (e.g. access error)
not (hide_hidden and name.startswith('.')) and
(not filter or bool(filter.search(name)) == reverse)]
filelist = [i for i in filelist if 'size' in i] # ignore failed file info (e.g. access error)
# Determine the parent
parentpath = os.path.dirname(base)
......@@ -182,7 +190,7 @@ class FileSystem(object):
return json.dumps(data)
else:
return data
def get_suggestions(
self, path, length=1, append_hidden=True, encode_json=True):
suggestions = []
......@@ -323,7 +331,7 @@ class FileSystem(object):
paths.append(fullp)
else:
ap = fullp[
len(path):] if fullp.startswith(path) else fullp
len(path):] if fullp.startswith(path) else fullp
logger.debug(fullp)
archive.write(fullp, ap)
else:
......@@ -341,7 +349,6 @@ class FileSystem(object):
return True
# fulltarget = os.path.join(path, fullsrc.split(os.sep)[-1])
logger.error("")
target = self.handle_file_name_collision(
fullsrc.split(os.sep)[-1], path)
fulltarget = os.path.join(path, target)
......@@ -373,7 +380,7 @@ class FileSystem(object):
shutil.move(fulltarget_temp, orig_fulltarget)
def save_file(self, path, content, force=True, binary=False,
utf8=False, window_id=None, view_id=None, watch_id=None):
utf8=False, window_id=None, view_id=None, watch_id=None):
path = self.expand(path)
# check if file already exists
if os.path.exists(path) and not force:
......@@ -389,29 +396,29 @@ class FileSystem(object):
f.write(content)
mtime = os.path.getmtime(path)
except Exception as e:
mtime = 0
mtime = 0
# inline watch
if window_id and view_id and watch_id:
watch_error = self.watch(path, window_id, view_id, watch_id)
else:
watch_error = ""
return json.dumps({
"mtime": os.path.getmtime(path),
"success": mtime > 0 and self.checkPermissions(path), #save is not successful, if file not writable
"watch_error": watch_error,
"success": mtime > 0 and self.checkPermissions(path), # save is not successful, if file not writable
"watch_error": watch_error,
"path": path
})
def get_file(self, path, binary=False,
utf8=False, window_id=None, view_id=None, watch_id=None):
utf8=False, window_id=None, view_id=None, watch_id=None):
# inline watch
if window_id and view_id and watch_id:
watch_error = self.watch(path, window_id, view_id, watch_id)
else:
watch_error = ""
# actual function
path = self.expand(path)
try:
......@@ -419,34 +426,34 @@ class FileSystem(object):
content = f.read()
if utf8:
content = content.decode('utf8')
#new: check for writing rights
# new: check for writing rights
writable = self.checkPermissions(path)
error = None
mtime = os.path.getmtime(path)
except Exception as e:
mtime = 0
mtime = 0
content = ""
writable = None
error = str(e)
return json.dumps({
"content": content,
"mtime": mtime,
"success": mtime > 0,
"watch_error": watch_error,
"writable": writable,
"writable": writable,
"error": error
})
def checkPermissions(self, path):
return os.access(path, os.W_OK)
def checkPermissions(self, path, permission=os.W_OK):
return os.access(path, permission)
def save_file_content(self, filename, content,
path=None, force=True, append=False):
# check write permissions
# if not self.checkPermission(username, vPath, 'w'):
# return False, "Permission denied!"
# return False, "Permission denied!"
if path:
filename = os.path.join(path, filename)
......@@ -550,10 +557,14 @@ class FileSystem(object):
path = self.expand(path)
if not os.path.exists(path):
return "The file does not exist"
# check if reading the file is allowed
if not self.checkPermissions(path, os.R_OK):
return "Reading the file is not allowed"
self.watchservice.subscribe((window_id, view_id, watch_id), path, pattern, reverse, hide_hidden)
return ""
def unwatch(self, window_id, view_id, watch_id=None):
self.watchservice.unsubscribe((window_id, view_id, watch_id))
return ""
......@@ -563,10 +574,10 @@ class FileSystem(object):
request_dict = json.loads(request)
config = ConfigParser.ConfigParser()
config.read([FileSystem.GLOBAL_WORKSPACE_CONF,
self.expand(FileSystem.PRIVATE_WORKSPACE_CONF)])
self.expand(FileSystem.PRIVATE_WORKSPACE_CONF)])
if self.exists(FileSystem.PRIVATE_WORKSPACE_CONF):
mtime = self.get_mtime(FileSystem.PRIVATE_WORKSPACE_CONF)
self._watch_workspaceini()
self._watch_workspaceini()
else:
mtime = -1
if not isinstance(request_dict, dict):
......@@ -584,20 +595,21 @@ class FileSystem(object):
if config.has_option(section, name):
data[section][name] = config.get(section, name)
elif fail_on_missing:
raise Exception('workspace.ini is missing the option "%s" in section [%s] ' % (name, section))
raise Exception(
'workspace.ini is missing the option "%s" in section [%s] ' % (name, section))
elif fail_on_missing:
raise Exception('workspace.ini is missing the section [%s]' % section)
return json.dumps({
"content": data,
"success": True,
"mtime": mtime
})
})
except Exception as e:
return json.dumps({
"content": "",
"success": False,
"error": str(e)
})
})
def set_workspaceini(self, request):
try:
......@@ -624,6 +636,7 @@ class FileSystem(object):
if self.exists(FileSystem.PRIVATE_WORKSPACE_CONF, 'f'):
self.watchservice.subscribe((self._userid, self._workspaceid), FileSystem.PRIVATE_WORKSPACE_CONF)
class WatchService(object):
def __init__(self):
self.subscriber_buffer = []
......@@ -634,42 +647,42 @@ class WatchService(object):
self.run = True
self.thread = Thread(target=self._worker)
self.thread.start()
def subscribe(self, id, path, pattern=None, reverse=False, hide_hidden=True):
if not path:
return self.unsubscribe(id)
path = os.path.expanduser(os.path.expandvars(path)).encode('utf8')
with self.lock:
if id not in self.subscribers:
WatchSubscriber(self, id)
self.subscribers[id].update(path, pattern, reverse, hide_hidden)
def unsubscribe(self, id):
with self.lock:
if hasattr(id, '__contains__') and None in id:
for subscriber in self.subscribers.values():
if False not in map(lambda e,c: c is None or e == c, subscriber.id, id):
if False not in map(lambda e, c: c is None or e == c, subscriber.id, id):
subscriber.destroy()
elif id in self.subscribers:
self.subscribers[id].destroy()
def stop(self):
self.run = False
def _worker(self):
while self.run:
events = self.monitor.read_events(0.05)
if events:
with self.lock:
for event in events:
if event.action_name in ['delete self','move self']:
if event.action_name in ['delete self', 'move self']:
kind = 'vanish'
elif event.action_name == 'modify':
kind = 'modify'
elif event.watch.isdir and event.action_name in ['create','delete','move from','move to']:
elif event.watch.isdir and event.action_name in ['create', 'delete', 'move from', 'move to']:
kind = 'change'
else:
kind = None
......@@ -681,31 +694,32 @@ class WatchService(object):
event.watch.mtime = -1
for subscriber in event.watch.subscribers[:]:
subscriber.process(kind, event.name)
if self.subscriber_buffer:
with self.lock:
for subscriber in self.subscriber_buffer[:]:
subscriber.flush(False)
for subscriber in self.subscribers.items():
subscriber.destroy()
self.monitor.remove_all_watches()
self.monitor.close()
class WatchSubscriber(object): # this should never be instanced manually
class WatchSubscriber(object): # this should never be instanced manually
EVENT_DELAYS = {
'change': [1.0,0.1],
'modify': [1.0,0.2]
'change': [1.0, 0.1],
'modify': [1.0, 0.2]
}
MAX_INLINE_SUBJECTS = 10
MAX_SUBJECT_NAMES = 25
def __init__(self, service, id):
if not isinstance(service, WatchService):
raise TypeError("No valid WatchService instance was provided")
if id in service.subscribers:
raise RuntimeError("There is already a subscriber with this id: "+str(id))
raise RuntimeError("There is already a subscriber with this id: " + str(id))
self.id = id
self.service = service
self.service.subscribers[self.id] = self
......@@ -715,7 +729,7 @@ class WatchSubscriber(object): # this should never be instanced manually
self.hide_hidden = None
self.event_buffer = {}
self.subject_buffer = {}
def destroy(self):
self.unbind()
if self in self.service.subscriber_buffer:
......@@ -725,30 +739,31 @@ class WatchSubscriber(object): # this should never be instanced manually
del self.watch
del self.event_buffer
del self.subject_buffer
def process(self, event, subject=""):
if self.watch.isdir and subject:
if self.hide_hidden and subject.startswith('.'):
return
if self.pattern and bool(self.pattern.search(subject)) != self.reverse:
return
if event not in self.subject_buffer:
self.subject_buffer[event] = []
if subject not in self.subject_buffer[event]:
self.subject_buffer[event].append(subject)
if event in WatchSubscriber.EVENT_DELAYS:
now = time()
if event in self.event_buffer:
self.event_buffer[event][1] = now + WatchSubscriber.EVENT_DELAYS[event][1]
else:
self.event_buffer[event] = [now + delay for delay in WatchSubscriber.EVENT_DELAYS[event]] #first & last event
self.event_buffer[event] = [now + delay for delay in
WatchSubscriber.EVENT_DELAYS[event]] # first & last event
if self not in self.service.subscriber_buffer:
self.service.subscriber_buffer.append(self)
else:
self.emit(event)
def flush(self, force=False):
now = time()
for event, delays in self.event_buffer.items():
......@@ -759,7 +774,7 @@ class WatchSubscriber(object): # this should never be instanced manually
self.service.subscriber_buffer.remove(self)
def emit(self, event):
if len(self.id) == 3: # window_id, view_id, watch_id
if len(self.id) == 3: # window_id, view_id, watch_id
data = {
'event': event,
'path': self.watch.path,
......@@ -770,42 +785,43 @@ class WatchSubscriber(object): # this should never be instanced manually
subject_count = len(self.subject_buffer[event])
data['subject_count'] = subject_count
if subject_count <= WatchSubscriber.MAX_INLINE_SUBJECTS:
data['subject_infos'] = [get_file_info(self.watch.path, subject) for subject in self.subject_buffer[event]]
data['subject_infos'] = [get_file_info(self.watch.path, subject) for subject in
self.subject_buffer[event]]
elif subject_count <= WatchSubscriber.MAX_SUBJECT_NAMES:
data['subject_names'] = self.subject_buffer[event]
self.subject_buffer[event] = []
else:
data['mtime'] = self.watch.mtime
vispa.remote.send_topic("extension.%s.socket.watch" % self.id[1], window_id=self.id[0], data=data)
elif len(self.id) == 2: # userid, workspaceid
elif len(self.id) == 2: # userid, workspaceid
vispa.remote.send_topic('workspace.ini_modified', user_id=self.id[0], data={
"workspaceId": self.id[1],
"mtime": self.watch.mtime
})
elif hasattr(self.id, '__call__'):
self.id(event, self)
def update(self, path, pattern="", reverse=False, hide_hidden=True):
self.bind(path)
if self.watch.isdir and pattern:
if not self.pattern or self.pattern.pattern != pattern:
self.pattern = re.compile(pattern)
self.reverse = reverse
self.subject_buffer = {event:[
subject for subject in subjects if bool(self.pattern.search(subject)) == self.reverse
] for event, subjects in self.subject_buffer.items()}
self.subject_buffer = {event: [
subject for subject in subjects if bool(self.pattern.search(subject)) == self.reverse
] for event, subjects in self.subject_buffer.items()}
else:
self.pattern = None
self.reverse = None
self.hide_hidden = hide_hidden
def bind(self, path):
if self.watch:
if self.watch.path == path:
return
else:
self.unbind()
if path not in self.service.watches:
if not os.path.exists(path):
raise IOError("File to be watched does not exist: %s" % path)
......@@ -822,24 +838,25 @@ class WatchSubscriber(object): # this should never be instanced manually
self.service.watches[path] = watch
else:
watch = self.service.watches[path]
self.watch = watch
if self not in watch.subscribers:
watch.subscribers.append(self)
self.subject_buffer = {}
def unbind(self):
if not self.watch:
return
self.watch.subscribers.remove(self)
if len(self.watch.subscribers) == 0:
del self.service.watches[self.watch.path]
self.service.monitor.remove_watch(self.watch)
self.watch = None
def string_compare(a, b):
if a == b:
return 0
......@@ -848,6 +865,7 @@ def string_compare(a, b):
else:
return -1
def file_compare(a, b):
if not a.startswith('.') and not b.startswith('.'):
return string_compare(a, b)
......
......@@ -96,7 +96,9 @@ class InMemoryZipImporter:
code = self._get_code(filename)
mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
mod.__file__ = "<%s>" % self.__class__.__name__
# mod.__file__ = "<%s>" % self.__class__.__name__
# mod.__file__ = "zip:/" + fullname.replace(".", "/")
mod.__file__ = fullname.replace(".", "/")
mod.__loader__ = self
if ispkg:
mod.__path__ = []
......@@ -316,7 +318,7 @@ def main_loop(fin, fout):
try:
try:
#vispa.remote.connection.serve_threaded()
# vispa.remote.connection.serve_threaded()
vispa.remote.connection.serve_all()
except EOFError:
# log_exception()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment