blob: c754cf31fd058a167339f4c8d0704b774dc9f78b [file] [log] [blame]
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
''' Endpoints for retrieving directory information from the server. '''
from bottle import Bottle, request, response
import os
import re
from config import WORK_DIR, PATH_LEADER
dir_app = Bottle()
@dir_app.route('/list/')
@dir_app.route('/list/<dir_path:path>')
def get_directory_info(dir_path='/'):
''' Return the listing of a supplied path.
:param dir_path: The directory path to list.
:type dir_path: String
:returns: Dictionary containing the directory listing if possible.
**Example successful JSON return**
.. sourcecode:: javascript
{
'listing': [
'/bar/',
'/baz.txt',
'/test.txt'
]
}
**Example failure JSON return**
.. sourcecode:: javascript
{'listing': []}
'''
dir_info = []
try:
clean_path = _get_clean_directory_path(PATH_LEADER, dir_path)
dir_listing = os.listdir(clean_path)
except:
# ValueError - dir_path couldn't be 'cleaned'
# OSError - clean_path is not a directory
# Either way, we don't have anything to list for the directory!
pass
else:
for obj in dir_listing:
# Ignore hidden files
if obj[0] == '.': continue
# Create a path to the listed object. If it's a directory add a
# trailing slash as a visual clue. Then strip out the path leader.
obj = os.path.join(clean_path, obj)
if os.path.isdir(obj): obj = obj + '/'
dir_info.append(obj.replace(PATH_LEADER, ''))
sorted(dir_info, key=lambda s: s.lower())
if request.query.callback:
return "%s(%s)" % (request.query.callback, {'listing': dir_info})
return {'listing': dir_info}
@dir_app.route('/results/')
def get_result_dir_info():
''' Retrieve results directory information.
The backend's results directory is determined by WORK_DIR. All the
directories there are formatted and returned as results. If WORK_DIR does
not exist, an empty listing will be returned (shown as a 'failure below').
**Successful JSON Response**
.. sourcecode:: javascript
{
'listing': [
'/bar',
'/foo'
]
}
**Failure JSON Response**
.. sourcecode:: javascript
{
'listing': []
}
'''
dir_info = []
try:
dir_listing = os.listdir(WORK_DIR)
except OSError:
# The WORK_DIR hasn't been created, so we don't have any results!
pass
else:
for obj in dir_listing:
# Ignore hidden files
if obj[0] == '.': continue
# Create a path to the listed object and strip the work dir leader.
# If the result isn't a directory, ignore it.
obj = os.path.join(WORK_DIR, obj)
if not os.path.isdir(obj): continue
dir_info.append(obj.replace(WORK_DIR, ''))
sorted(dir_info, key=lambda s: s.lower())
if request.query.callback:
return "%s(%s)" % (request.query.callback, {'listing': dir_info})
return {'listing': dir_info}
@dir_app.route('/results/<dir_path:path>')
def get_results(dir_path):
''' Retrieve specific result files.
:param dir_path: The relative results path to list.
:type dir_path: String
:returns: Dictionary of the requested result's directory listing.
**Successful JSON Response**
.. sourcecode:: javascript
{
'listing': [
'file1',
'file2'
]
}
**Failure JSON Response**
.. sourcecode:: javascript
{
'listing': []
}
'''
dir_info = []
try:
clean_path = _get_clean_directory_path(WORK_DIR, dir_path)
dir_listing = os.listdir(clean_path)
except:
# ValueError - dir_path couldn't be 'cleaned'
# OSError - clean_path is not a directory
# Either way, we don't have any results to return!
pass
else:
for obj in dir_listing:
# Ignore hidden files
if obj[0] == '.': continue
# Create a path to the listed object and strip out the path leader.
obj = os.path.join(clean_path, obj)
dir_info.append(obj.replace(WORK_DIR, ''))
sorted(dir_info, key=lambda s: s.lower())
if request.query.callback:
return "%s(%s)" % (request.query.callback, {'listing': dir_info})
return {'listing': dir_info}
@dir_app.route('/path_leader/')
def get_path_leader():
''' Return the path leader used for clean path creation.
**Example JSON Response**
.. sourcecode:: javascript
{'leader': '/usr/local/ocw'}
'''
return_json = {'leader': PATH_LEADER}
if request.query.callback:
return "%s(%s)" % (request.query.callback, return_json)
return return_json
@dir_app.hook('after_request')
def enable_cors():
''' Allow Cross-Origin Resource Sharing for all URLs. '''
response.headers['Access-Control-Allow-Origin'] = '*'
def _get_clean_directory_path(path_leader, dir_path):
''' Return a cleaned directory path with a defined path prefix.
'Clean' dir_path to remove any relative path components or duplicate
slashes that could cause problems. The final clean path is then the
path_leader + dir_path.
:param path_leader: The path prefix that will be prepended to the cleaned
dir_path.
:type path_leader: String
:param dir_path: The path to clean.
:type path_leader: String
:returns: The cleaned directory path with path_leader prepended.
'''
# Strip out any .. or . relative directories and remove duplicate slashes
dir_path = re.sub('/[\./]*/?', '/', dir_path)
dir_path = re.sub('//+', '/', dir_path)
# Prevents the directory path from being a substring of the path leader.
# os.path.join('/usr/local/ocw', '/usr/local') gives '/usr/local'
# which could allow access to unacceptable paths. This also means that
if dir_path[0] == '/': dir_path = dir_path[1:]
return os.path.join(path_leader, dir_path)