1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-06-25 14:43:15 +00:00
seahub/api2/views.py

964 lines
35 KiB
Python
Raw Normal View History

2012-12-14 12:53:36 +00:00
# encoding: utf-8
import os
import stat
2012-12-16 14:34:52 +00:00
import simplejson as json
2012-12-14 12:53:36 +00:00
from urllib2 import unquote, quote
import seahub.settings as settings
from rest_framework import parsers
from rest_framework import status
from rest_framework import renderers
from rest_framework.permissions import IsAuthenticated
2012-12-26 03:03:38 +00:00
from rest_framework.reverse import reverse
2012-12-14 12:53:36 +00:00
from rest_framework.response import Response
2012-12-26 03:03:38 +00:00
from rest_framework.throttling import AnonRateThrottle, UserRateThrottle
2012-12-14 12:53:36 +00:00
from rest_framework.views import APIView
from django.contrib.sites.models import RequestSite
2012-12-16 14:34:52 +00:00
from django.http import HttpResponse
2012-12-14 12:53:36 +00:00
from models import Token
from authentication import TokenAuthentication
2013-02-20 12:51:37 +00:00
from permissions import IsRepoWritable, IsRepoAccessible, IsRepoOwner
2012-12-14 12:53:36 +00:00
from serializers import AuthTokenSerializer
from base.accounts import User
from share.models import FileShare
from seahub.views import access_to_repo, validate_owner
from seahub.utils import gen_file_get_url, gen_token, gen_file_upload_url, \
2012-12-15 06:53:15 +00:00
check_filename_with_rename, get_starred_files, get_ccnetapplet_root, \
2012-12-23 14:27:16 +00:00
get_ccnet_server_addr_port, star_file, unstar_file, string2list
2013-02-20 12:51:37 +00:00
try:
from seahub.settings import CLOUD_MODE
except ImportError:
CLOUD_MODE = False
2012-12-14 12:53:36 +00:00
2013-02-20 12:51:37 +00:00
from pysearpc import SearpcError, SearpcObjEncoder
from seaserv import seafserv_rpc, seafserv_threaded_rpc, server_repo_size, \
2012-12-15 06:53:15 +00:00
get_personal_groups_by_user, get_session_info, get_repo_token_nonnull, \
get_group_repos, get_repo, check_permission, get_commits, is_passwd_set,\
2013-02-20 12:51:37 +00:00
list_personal_repos_by_owner, list_personal_shared_repos, check_quota, \
list_share_repos, get_group_repos_by_owner, list_inner_pub_repos_by_owner,\
2013-03-02 08:58:37 +00:00
remove_share, unshare_group_repo, unset_inner_pub_repo, get_user_quota, \
get_user_share_usage, get_user_quota_usage, CALC_SHARE_USAGE
2012-12-14 12:53:36 +00:00
2012-12-16 14:34:52 +00:00
json_content_type = 'application/json; charset=utf-8'
2012-12-23 14:27:16 +00:00
# Define custom HTTP status code. 4xx starts from 440, 5xx starts from 520.
HTTP_440_REPO_PASSWD_REQUIRED = 440
HTTP_520_OPERATION_FAILED = 520
2012-12-14 12:53:36 +00:00
class Ping(APIView):
"""
Returns a simple `pong` message when client calls `api2/ping/`.
For example:
curl http://127.0.0.1:8000/api2/ping/
"""
def get(self, request, format=None):
return Response('pong')
2012-12-15 06:53:15 +00:00
def head(self, request, format=None):
return Response(headers={'foo': 'bar',})
2012-12-14 12:53:36 +00:00
class AuthPing(APIView):
"""
Returns a simple `pong` message when client provided an auth token.
For example:
curl -H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" http://127.0.0.1:8000/api2/auth/ping/
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
def get(self, request, format=None):
return Response('pong')
class ObtainAuthToken(APIView):
"""
Returns auth token if username and password are valid.
For example:
2012-12-29 02:31:23 +00:00
curl -d "username=foo@example.com&password=123456" http://127.0.0.1:8000/api2/auth-token/
2012-12-14 12:53:36 +00:00
"""
2012-12-26 03:03:38 +00:00
throttle_classes = (AnonRateThrottle, )
2012-12-14 12:53:36 +00:00
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
model = Token
def post(self, request):
serializer = AuthTokenSerializer(data=request.DATA)
if serializer.is_valid():
token, created = Token.objects.get_or_create(user=serializer.object['user'].username)
return Response({'token': token.key})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
2012-12-23 14:27:16 +00:00
def api_error(code, msg):
err_resp = {'error_msg': msg}
2012-12-14 12:53:36 +00:00
return Response(err_resp, status=code)
class Account(APIView):
"""
Show account info.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
2012-12-26 03:03:38 +00:00
throttle_classes = (UserRateThrottle, )
2012-12-14 12:53:36 +00:00
def get(self, request, format=None):
info = {}
email = request.user.username
info['email'] = email
2013-03-02 08:58:37 +00:00
info['total'] = get_user_quota(email)
if CALC_SHARE_USAGE:
my_usage = get_user_quota_usage(email)
share_usage = get_user_share_usage(email)
info['usage'] = my_usage + share_usage
else:
info['usage'] = get_user_quota_usage(email)
2012-12-14 12:53:36 +00:00
return Response(info)
def calculate_repo_info(repo_list, username):
"""
Get some info for repo.
"""
for repo in repo_list:
commit = get_commits(repo.id, 0, 1)[0]
if not commit:
continue
repo.latest_modify = commit.ctime
repo.root = commit.root_id
repo.size = server_repo_size(repo.id)
2012-12-14 12:53:36 +00:00
class Repos(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
2012-12-26 03:03:38 +00:00
throttle_classes = (UserRateThrottle, )
2012-12-14 12:53:36 +00:00
def get(self, request, format=None):
email = request.user.username
repos_json = []
2012-12-14 12:53:36 +00:00
owned_repos = list_personal_repos_by_owner(email)
calculate_repo_info(owned_repos, email)
2012-12-14 12:53:36 +00:00
owned_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify))
for r in owned_repos:
repo = {
"type":"repo",
"id":r.id,
"owner":email,
"name":r.name,
"desc":r.desc,
"mtime":r.latest_modify,
"root":r.root,
"size":r.size,
"encrypted":r.encrypted,
"permission": 'rw', # Always have read-write permission to owned repo
2012-12-14 12:53:36 +00:00
}
repos_json.append(repo)
shared_repos = list_personal_shared_repos(email, 'to_email', -1, -1)
for r in shared_repos:
commit = get_commits(r.repo_id, 0, 1)[0]
if not commit:
continue
r.latest_modify = commit.ctime
r.root = commit.root_id
r.size = server_repo_size(r.repo_id)
r.password_need = is_passwd_set(r.repo_id, email)
r.permission = check_permission(r.repo_id, email)
2012-12-14 12:53:36 +00:00
repo = {
"type":"srepo",
"id":r.repo_id,
"owner":r.user,
"name":r.repo_name,
"desc":r.repo_desc,
2012-12-14 12:53:36 +00:00
"mtime":r.latest_modify,
"root":r.root,
"size":r.size,
"encrypted":r.encrypted,
"permission": r.permission,
2012-12-14 12:53:36 +00:00
}
repos_json.append(repo)
groups = get_personal_groups_by_user(email)
for group in groups:
g_repos = get_group_repos(group.id, email)
calculate_repo_info (g_repos, email)
g_repos.sort(lambda x, y: cmp(y.latest_modify, x.latest_modify))
2012-12-14 12:53:36 +00:00
for r in g_repos:
repo = {
"type":"grepo",
"id":r.id,
"owner":group.group_name,
"name":r.name,
"desc":r.desc,
"mtime":r.latest_modify,
"root":r.root,
"size":r.size,
"encrypted":r.encrypted,
"permission": check_permission(r.id, email),
2012-12-14 12:53:36 +00:00
}
repos_json.append(repo)
return Response(repos_json)
def can_access_repo(request, repo_id):
if not check_permission(repo_id, request.user.username):
return False
return True
2012-12-23 14:27:16 +00:00
2012-12-25 14:30:18 +00:00
def set_repo_password(request, repo, password):
assert password, 'password must not be none'
try:
seafserv_threaded_rpc.set_passwd(repo.id, request.user.username, password)
except SearpcError, e:
if e.msg == 'Bad arguments':
2012-12-26 02:26:41 +00:00
return api_error(status.HTTP_400_BAD_REQUEST, e.msg)
2012-12-25 14:30:18 +00:00
elif e.msg == 'Repo is not encrypted':
2012-12-26 02:26:41 +00:00
return api_error(status.HTTP_409_CONFLICT, e.msg)
2012-12-25 14:30:18 +00:00
elif e.msg == 'Incorrect password':
2012-12-26 02:26:41 +00:00
return api_error(status.HTTP_400_BAD_REQUEST, e.msg)
2012-12-25 14:30:18 +00:00
elif e.msg == 'Internal server error':
2012-12-26 02:26:41 +00:00
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, e.msg)
2012-12-25 14:30:18 +00:00
else:
2012-12-26 02:26:41 +00:00
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, e.msg)
2012-12-25 14:30:18 +00:00
2012-12-23 14:27:16 +00:00
def check_repo_access_permission(request, repo):
if not can_access_repo(request, repo.id):
return api_error(status.HTTP_403_FORBIDDEN, 'Forbid to access this repo.')
password_set = False
if repo.encrypted:
try:
ret = seafserv_rpc.is_passwd_set(repo.id, request.user.username)
if ret == 1:
password_set = True
except SearpcError, e:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
"SearpcError:" + e.msg)
if not password_set:
password = request.REQUEST.get('password', default=None)
if not password:
return api_error(HTTP_440_REPO_PASSWD_REQUIRED,
'Repo password is needed.')
return set_repo_password(request, repo, password)
2012-12-14 12:53:36 +00:00
class Repo(APIView):
authentication_classes = (TokenAuthentication, )
2012-12-25 03:09:34 +00:00
permission_classes = (IsAuthenticated, IsRepoAccessible, )
2012-12-26 03:03:38 +00:00
throttle_classes = (UserRateThrottle, )
2012-12-15 06:53:15 +00:00
2012-12-14 12:53:36 +00:00
def get(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
2012-12-23 14:27:16 +00:00
return api_error(status.HTTP_404_NOT_FOUND, 'Repo not found.')
2012-12-14 12:53:36 +00:00
2012-12-25 03:09:34 +00:00
# check whether user is repo owner
2012-12-14 12:53:36 +00:00
if validate_owner(request, repo_id):
owner = "self"
else:
owner = "share"
2012-12-23 14:27:16 +00:00
last_commit = get_commits(repo.id, 0, 1)[0]
repo.latest_modify = last_commit.ctime if last_commit else None
2012-12-14 12:53:36 +00:00
# query repo infomation
repo.size = seafserv_threaded_rpc.server_repo_size(repo_id)
current_commit = get_commits(repo_id, 0, 1)[0]
root_id = current_commit.root_id if current_commit else None
2012-12-15 06:53:15 +00:00
2012-12-14 12:53:36 +00:00
repo_json = {
"type":"repo",
"id":repo.id,
"owner":owner,
"name":repo.name,
"desc":repo.desc,
2012-12-24 08:53:32 +00:00
"mtime":repo.latest_modify,
2012-12-14 12:53:36 +00:00
"size":repo.size,
"encrypted":repo.encrypted,
"root":root_id,
2012-12-14 12:53:36 +00:00
}
return Response(repo_json)
def post(self, request, repo_id, format=None):
2012-12-25 03:09:34 +00:00
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Repo not found.')
resp = check_repo_access_permission(request, repo)
2012-12-14 12:53:36 +00:00
if resp:
return resp
op = request.GET.get('op', 'setpassword')
if op == 'setpassword':
return Response("success")
return Response("unsupported operation")
2012-12-23 14:27:16 +00:00
class DownloadRepo(APIView):
authentication_classes = (TokenAuthentication, )
2012-12-25 03:09:34 +00:00
permission_classes = (IsAuthenticated, IsRepoAccessible, )
2012-12-26 03:03:38 +00:00
throttle_classes = (UserRateThrottle, )
2012-12-14 12:53:36 +00:00
2012-12-23 14:27:16 +00:00
def get(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Repo not found.')
2012-12-14 12:53:36 +00:00
2012-12-23 14:27:16 +00:00
# generate download url for client
ccnet_applet_root = get_ccnetapplet_root()
relay_id = get_session_info().id
addr, port = get_ccnet_server_addr_port ()
2012-12-25 03:09:34 +00:00
email = request.user.username
2012-12-23 14:27:16 +00:00
token = get_repo_token_nonnull(repo_id, request.user.username)
2012-12-25 03:09:34 +00:00
repo_name = repo.name
2012-12-23 14:27:16 +00:00
enc = 1 if repo.encrypted else ''
magic = repo.magic if repo.encrypted else ''
2012-12-14 12:53:36 +00:00
2012-12-23 14:27:16 +00:00
info_json = {
'relay_id': relay_id,
'relay_addr': addr,
'relay_port': port,
'email': email,
'token': token,
'repo_id': repo_id,
2012-12-25 03:09:34 +00:00
'repo_name': repo_name,
2012-12-23 14:27:16 +00:00
'encrypted': enc,
'magic': magic,
2012-12-23 14:27:16 +00:00
}
return Response(info_json)
2012-12-25 03:09:34 +00:00
class UploadLinkView(APIView):
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
2012-12-26 03:03:38 +00:00
throttle_classes = (UserRateThrottle, )
2012-12-25 03:09:34 +00:00
def get(self, request, repo_id, format=None):
repo = get_repo(repo_id)
if check_permission(repo_id, request.user.username) == 'rw':
token = seafserv_rpc.web_get_access_token(repo_id,
'dummy',
'upload',
request.user.username)
else:
return api_error(status.HTTP_403_FORBIDDEN, "Can not access repo")
2013-01-08 08:35:34 +00:00
if check_quota(repo_id) < 0:
2013-03-02 08:58:37 +00:00
return api_error(HTTP_520_OPERATION_FAILED, 'Above quota')
2012-12-25 03:09:34 +00:00
2013-01-05 03:54:15 +00:00
upload_url = gen_file_upload_url(token, 'upload-api')
2012-12-25 03:09:34 +00:00
return Response(upload_url)
2012-12-23 14:27:16 +00:00
2012-12-14 12:53:36 +00:00
def get_file_size (id):
size = seafserv_threaded_rpc.get_file_size(id)
return size if size else 0
def get_dir_entrys_by_id(request, dir_id):
try:
dirs = seafserv_threaded_rpc.list_dir(dir_id)
except SearpcError, e:
2012-12-23 14:27:16 +00:00
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to list dir.")
2012-12-14 12:53:36 +00:00
dir_list, file_list = [], []
2012-12-14 12:53:36 +00:00
for dirent in dirs:
dtype = "file"
entry={}
if stat.S_ISDIR(dirent.mode):
dtype = "dir"
else:
try:
entry["size"] = get_file_size(dirent.obj_id)
except Exception, e:
entry["size"]=0
entry["type"]=dtype
entry["name"]=dirent.obj_name
entry["id"]=dirent.obj_id
if dtype == 'dir':
dir_list.append(entry)
else:
file_list.append(entry)
dir_list.sort(lambda x, y : cmp(x['name'].lower(),y['name'].lower()))
file_list.sort(lambda x, y : cmp(x['name'].lower(),y['name'].lower()))
dentrys = dir_list + file_list
2012-12-16 14:34:52 +00:00
response = HttpResponse(json.dumps(dentrys), status=200,
content_type=json_content_type)
response["oid"] = dir_id
return response
2012-12-14 12:53:36 +00:00
def get_shared_link(request, repo_id, path):
l = FileShare.objects.filter(repo_id=repo_id).filter(\
username=request.user.username).filter(path=path)
token = None
if len(l) > 0:
fileshare = l[0]
token = fileshare.token
else:
token = gen_token(max_length=10)
fs = FileShare()
fs.username = request.user.username
fs.repo_id = repo_id
fs.path = path
fs.token = token
try:
fs.save()
except IntegrityError, e:
2012-12-23 14:27:16 +00:00
return api_err(status.HTTP_500_INTERNAL_SERVER_ERROR, e.msg)
2012-12-14 12:53:36 +00:00
2012-12-23 14:27:16 +00:00
http_or_https = request.is_secure() and 'https' or 'http'
2012-12-14 12:53:36 +00:00
domain = RequestSite(request).domain
2012-12-23 14:27:16 +00:00
file_shared_link = '%s://%s%sf/%s/' % (http_or_https, domain,
settings.SITE_ROOT, token)
2012-12-14 12:53:36 +00:00
return Response(file_shared_link)
def get_repo_file(request, repo_id, file_id, file_name, op):
if op == 'download':
token = seafserv_rpc.web_get_access_token(repo_id, file_id,
op, request.user.username)
redirect_url = gen_file_get_url(token, file_name)
2012-12-16 14:34:52 +00:00
#return Response(redirect_url)
response = HttpResponse(json.dumps(redirect_url), status=200,
content_type=json_content_type)
response["oid"] = file_id
return response
2012-12-14 12:53:36 +00:00
if op == 'sharelink':
path = request.GET.get('p', None)
assert path, 'path must be passed in the url'
return get_shared_link(request, repo_id, path)
2012-12-23 14:27:16 +00:00
def reloaddir(request, repo_id, parent_dir):
2012-12-14 12:53:36 +00:00
current_commit = get_commits(repo_id, 0, 1)[0]
if not current_commit:
2012-12-23 14:27:16 +00:00
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
'Failed to get current commit of repo %s.' % repo_id)
2012-12-14 12:53:36 +00:00
try:
dir_id = seafserv_threaded_rpc.get_dirid_by_path(current_commit.id,
parent_dir.encode('utf-8'))
except SearpcError, e:
2012-12-23 14:27:16 +00:00
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to get dir id by path")
2012-12-14 12:53:36 +00:00
if not dir_id:
2012-12-23 14:27:16 +00:00
return api_error(status.HTTP_404_NOT_FOUND, "Path does not exist")
2012-12-14 12:53:36 +00:00
return get_dir_entrys_by_id(request, dir_id)
2012-12-23 14:27:16 +00:00
def reloaddir_if_neccessary (request, repo_id, parent_dir):
reload_dir = False
2012-12-23 14:27:16 +00:00
s = request.GET.get('reloaddir', None)
if s and s.lower() == 'true':
reload_dir = True
2012-12-23 14:27:16 +00:00
if not reload_dir:
2012-12-23 14:27:16 +00:00
return Response('success')
return reloaddir(request, repo_id, parent_dir)
2012-12-23 14:27:16 +00:00
2012-12-29 02:31:23 +00:00
# deprecated
2012-12-14 12:53:36 +00:00
class OpDeleteView(APIView):
"""
Delete a file.
"""
authentication_classes = (TokenAuthentication, )
2012-12-19 08:39:50 +00:00
permission_classes = (IsAuthenticated, IsRepoWritable, )
2012-12-14 12:53:36 +00:00
def post(self, request, repo_id, format=None):
2012-12-25 03:09:34 +00:00
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Repo not found.')
resp = check_repo_access_permission(request, repo)
2012-12-14 12:53:36 +00:00
if resp:
return resp
parent_dir = request.GET.get('p', '/')
file_names = request.POST.get("file_names")
if not parent_dir or not file_names:
2012-12-23 14:27:16 +00:00
return api_error(status.HTTP_404_NOT_FOUND,
'File or directory not found.')
2012-12-14 12:53:36 +00:00
for file_name in file_names.split(':'):
file_name = unquote(file_name.encode('utf-8'))
2012-12-14 12:53:36 +00:00
try:
seafserv_threaded_rpc.del_file(repo_id, parent_dir,
file_name, request.user.username)
except SearpcError,e:
2012-12-23 14:27:16 +00:00
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to delete file.")
2012-12-14 12:53:36 +00:00
return reloaddir_if_neccessary (request, repo_id, parent_dir)
def append_starred_files(array, files):
for f in files:
sfile = {'org' : f.org_id,
'repo' : f.repo.id,
'path' : f.path,
'mtime' : f.last_modified,
'dir' : f.is_dir,
'size' : f.size
}
array.append(sfile)
def api_starred_files(request):
starred_files = []
personal_files = get_starred_files(request.user.username, -1)
append_starred_files (starred_files, personal_files)
return Response(starred_files)
class StarredFileView(APIView):
"""
2012-12-23 14:27:16 +00:00
Support uniform interface for starred file operation,
including add/delete/list starred files.
2012-12-14 12:53:36 +00:00
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated,)
2012-12-26 03:03:38 +00:00
throttle_classes = (UserRateThrottle, )
2012-12-14 12:53:36 +00:00
def get(self, request, format=None):
2012-12-23 14:27:16 +00:00
# list starred files
2012-12-14 12:53:36 +00:00
return api_starred_files(request)
2012-12-23 14:27:16 +00:00
def post(self, request, format=None):
# add starred file
repo_id = request.POST.get('repo_id', '')
2013-02-25 08:39:20 +00:00
path = unquote(request.POST.get('p', '').encode('utf-8'))
2012-12-23 14:27:16 +00:00
if not (repo_id and path):
return api_error(status.HTTP_400_BAD_REQUEST,
'Repo_id or path is missing.')
if path[-1] == '/': # Should not contain '/' at the end of path.
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid file path.')
star_file(request.user.username, repo_id, path, is_dir=False,
org_id=-1)
resp = Response('success', status=status.HTTP_201_CREATED)
resp['Location'] = reverse('starredfiles')
return resp
def delete(self, request, format=None):
# remove starred file
repo_id = request.GET.get('repo_id', '')
path = request.GET.get('p', '')
if not (repo_id and path):
return api_error(status.HTTP_400_BAD_REQUEST,
'Repo_id or path is missing.')
if path[-1] == '/': # Should not contain '/' at the end of path.
return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid file path.')
unstar_file(request.user.username, repo_id, path)
return Response('success', status=status.HTTP_200_OK)
class FileView(APIView):
"""
Support uniform interface for file related operations,
including create/delete/rename/view, etc.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, IsRepoWritable, )
2012-12-26 03:03:38 +00:00
throttle_classes = (UserRateThrottle, )
2012-12-23 14:27:16 +00:00
def get(self, request, repo_id, format=None):
# view file
repo = get_repo(repo_id)
2012-12-25 03:09:34 +00:00
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Repo not found.')
2012-12-23 14:27:16 +00:00
resp = check_repo_access_permission(request, repo)
if resp:
return resp
path = request.GET.get('p', None)
if not path:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
file_name = os.path.basename(path)
file_id = None
try:
2012-12-24 08:53:32 +00:00
file_id = seafserv_threaded_rpc.get_file_id_by_path(repo_id,
2012-12-23 14:27:16 +00:00
path.encode('utf-8'))
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to get file id by path.")
if not file_id:
return api_error(status.HTTP_404_NOT_FOUND, "File not found")
return get_repo_file(request, repo_id, file_id, file_name, 'download')
def post(self, request, repo_id, format=None):
# rename or move file
2012-12-25 03:09:34 +00:00
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Repo not found.')
resp = check_repo_access_permission(request, repo)
2012-12-23 14:27:16 +00:00
if resp:
return resp
path = request.GET.get('p', '')
if not path or path[0] != '/':
return api_error(status.HTTP_400_BAD_REQUEST,
'Path is missing or invalid.')
operation = request.POST.get('operation', '')
if operation.lower() == 'rename':
newname = request.POST.get('newname', '')
if not newname:
return api_error(status.HTTP_400_BAD_REQUEST,
'Newname is missing')
2013-02-25 07:04:13 +00:00
newname = unquote(newname.encode('utf-8'))
2012-12-23 14:27:16 +00:00
if len(newname) > settings.MAX_UPLOAD_FILE_NAME_LEN:
return api_error(status.HTTP_400_BAD_REQUEST, 'Newname too long')
parent_dir = os.path.dirname(path)
2013-02-25 07:04:13 +00:00
parent_dir_utf8 = parent_dir.encode('utf-8')
2012-12-23 14:27:16 +00:00
oldname = os.path.basename(path)
2013-02-25 07:04:13 +00:00
oldname_utf8 = oldname.encode('utf-8')
2012-12-23 14:27:16 +00:00
if oldname == newname:
return api_error(status.HTTP_409_CONFLICT,
'The new name is the same to the old')
newname = check_filename_with_rename(repo_id, parent_dir, newname)
2013-02-25 08:39:20 +00:00
newname_utf8 = newname.encode('utf-8')
2012-12-23 14:27:16 +00:00
try:
seafserv_threaded_rpc.rename_file (repo_id, parent_dir_utf8,
oldname_utf8, newname,
request.user.username)
except SearpcError,e:
return api_error(HTTP_520_OPERATION_FAILED,
2013-02-25 07:04:13 +00:00
"Failed to rename file: %s" % e)
2012-12-23 14:27:16 +00:00
if request.GET.get('reloaddir', '').lower() == 'true':
reloaddir(request, repo_id, parent_dir)
else:
resp = Response('success', status=status.HTTP_301_MOVED_PERMANENTLY)
uri = reverse('FileView', args=[repo_id], request=request)
2013-02-25 08:39:20 +00:00
resp['Location'] = uri + '?p=' + quote(parent_dir_utf8) + quote(newname_utf8)
2012-12-23 14:27:16 +00:00
return resp
elif operation.lower() == 'move':
src_dir = os.path.dirname(path)
2013-02-25 07:04:13 +00:00
src_dir_utf8 = src_dir.encode('utf-8')
2012-12-23 14:27:16 +00:00
src_repo_id = repo_id
dst_repo_id = request.POST.get('dst_repo', '')
2013-02-25 07:04:13 +00:00
dst_dir = request.POST.get('dst_dir', '')
dst_dir_utf8 = dst_dir.encode('utf-8')
2012-12-23 14:27:16 +00:00
if dst_dir[-1] != '/': # Append '/' to the end of directory if necessary
dst_dir += '/'
# obj_names = request.POST.get('obj_names', '')
if not (dst_repo_id and dst_dir):
return api_error(status.HTTP_400_BAD_REQUEST, 'Missing arguments.')
if src_repo_id == dst_repo_id and src_dir == dst_dir:
return Response('success', status=status.HTTP_200_OK)
# names = obj_names.split(':')
# names = map(lambda x: unquote(x).decode('utf-8'), names)
# if dst_dir.startswith(src_dir):
# for obj_name in names:
# if dst_dir.startswith('/'.join([src_dir, obj_name])):
# return api_error(status.HTTP_409_CONFLICT,
# 'Can not move a dirctory to its subdir')
filename = os.path.basename(path)
2013-02-25 07:04:13 +00:00
filename_utf8 = filename.encode('utf-8')
2012-12-23 14:27:16 +00:00
new_filename = check_filename_with_rename(dst_repo_id, dst_dir,
filename)
2013-02-25 07:04:13 +00:00
new_filename_utf8 = new_filename.encode('utf-8')
2012-12-23 14:27:16 +00:00
try:
seafserv_threaded_rpc.move_file(src_repo_id, src_dir_utf8,
filename_utf8, dst_repo_id,
dst_dir_utf8, new_filename_utf8,
request.user.username)
except SearpcError, e:
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
"SearpcError:" + e.msg)
if request.GET.get('reloaddir', '').lower() == 'true':
reloaddir(request, dst_repo_id, dst_dir)
else:
resp = Response('success', status=status.HTTP_301_MOVED_PERMANENTLY)
uri = reverse('FileView', args=[repo_id], request=request)
resp['Location'] = uri + '?p=' + quote(dst_dir_utf8) + quote(new_filename_utf8)
return resp
else:
return api_error(status.HTTP_400_BAD_REQUEST,
"Operation can only be rename or move.")
def put(self, request, repo_id, format=None):
# update file
# TODO
pass
def delete(self, request, repo_id, format=None):
# delete file
repo = get_repo(repo_id)
2012-12-25 03:09:34 +00:00
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Repo not found.')
2012-12-23 14:27:16 +00:00
resp = check_repo_access_permission(request, repo)
if resp:
return resp
path = request.GET.get('p', None)
if not path:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
parent_dir = os.path.dirname(path)
2013-02-25 07:04:13 +00:00
parent_dir_utf8 = os.path.dirname(path).encode('utf-8')
file_name_utf8 = os.path.basename(path).encode('utf-8')
2012-12-23 14:27:16 +00:00
try:
seafserv_threaded_rpc.del_file(repo_id, parent_dir_utf8,
file_name_utf8,
request.user.username)
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to delete file.")
return reloaddir_if_neccessary(request, repo_id, parent_dir)
class FileSharedLinkView(APIView):
"""
Support uniform interface for file shared link.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
2012-12-26 03:03:38 +00:00
throttle_classes = (UserRateThrottle, )
2012-12-23 14:27:16 +00:00
def put(self, request, repo_id, format=None):
# generate file shared link
2013-02-25 08:39:20 +00:00
path = unquote(request.DATA.get('p', '').encode('utf-8'))
2012-12-23 14:27:16 +00:00
if not path:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
if path[-1] == '/':
path = path[:-1]
l = FileShare.objects.filter(repo_id=repo_id).filter(
username=request.user.username).filter(path=path)
if len(l) > 0:
fileshare = l[0]
token = fileshare.token
else:
token = gen_token(max_length=10)
fs = FileShare()
fs.username = request.user.username
fs.repo_id = repo_id
fs.path = path
fs.token = token
try:
fs.save()
except IntegrityError, e:
return api_err(status.HTTP_500_INTERNAL_SERVER_ERROR, e.msg)
http_or_https = request.is_secure() and 'https' or 'http'
domain = RequestSite(request).domain
file_shared_link = '%s://%s%sf/%s/' % (http_or_https, domain,
settings.SITE_ROOT, token)
resp = Response(status=status.HTTP_201_CREATED)
resp['Location'] = file_shared_link
return resp
class DirView(APIView):
"""
Support uniform interface for directory operations, including
create/delete/rename/list, etc.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, IsRepoWritable, )
2012-12-26 03:03:38 +00:00
throttle_classes = (UserRateThrottle, )
2012-12-23 14:27:16 +00:00
def get(self, request, repo_id, format=None):
# list dir
repo = get_repo(repo_id)
2012-12-25 03:09:34 +00:00
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Repo not found.')
2012-12-23 14:27:16 +00:00
resp = check_repo_access_permission(request, repo)
if resp:
return resp
path = request.GET.get('p', '/')
if path[-1] != '/':
path = path + '/'
try:
2012-12-24 08:53:32 +00:00
dir_id = seafserv_threaded_rpc.get_dir_id_by_path(repo_id,
2012-12-23 14:27:16 +00:00
path.encode('utf-8'))
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to get dir id by path.")
if not dir_id:
return api_error(status.HTTP_404_NOT_FOUND, "Path does not exist")
old_oid = request.GET.get('oid', None)
if old_oid and old_oid == dir_id :
response = HttpResponse(json.dumps("uptodate"), status=200,
content_type=json_content_type)
response["oid"] = dir_id
return response
else:
return get_dir_entrys_by_id(request, dir_id)
def post(self, request, repo_id, format=None):
# new dir
2012-12-25 03:09:34 +00:00
repo = get_repo(repo_id)
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Repo not found.')
resp = check_repo_access_permission(request, repo)
2012-12-23 14:27:16 +00:00
if resp:
return resp
path = request.GET.get('p', '')
if not path or path[0] != '/':
return api_error(status.HTTP_400_BAD_REQUEST, "Path is missing.")
if path == '/': # Can not make root dir.
return api_error(status.HTTP_400_BAD_REQUEST, "Path is invalid.")
if path[-1] == '/': # Cut out last '/' if possible.
path = path[:-1]
operation = request.POST.get('operation', '')
if operation.lower() == 'mkdir':
parent_dir = os.path.dirname(path)
2013-02-25 08:39:20 +00:00
parent_dir_utf8 = parent_dir.encode('utf-8')
2012-12-23 14:27:16 +00:00
new_dir_name = os.path.basename(path)
new_dir_name = check_filename_with_rename(repo_id, parent_dir,
new_dir_name)
2013-02-25 07:04:13 +00:00
new_dir_name_utf8 = new_dir_name.encode('utf-8')
2012-12-23 14:27:16 +00:00
try:
seafserv_threaded_rpc.post_dir(repo_id, parent_dir,
new_dir_name,
request.user.username)
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
'Failed to make directory.')
if request.GET.get('reloaddir', '').lower() == 'true':
2013-01-18 15:02:05 +00:00
resp = reloaddir(request, repo_id, parent_dir)
2012-12-23 14:27:16 +00:00
else:
resp = Response('success', status=status.HTTP_201_CREATED)
uri = reverse('DirView', args=[repo_id], request=request)
2013-02-25 08:39:20 +00:00
resp['Location'] = uri + '?p=' + quote(parent_dir_utf8) + \
2012-12-23 14:27:16 +00:00
quote(new_dir_name_utf8)
2013-01-18 15:02:05 +00:00
return resp
2012-12-23 14:27:16 +00:00
# elif operation.lower() == 'rename':
# pass
# elif operation.lower() == 'move':
# pass
else:
return api_error(status.HTTP_400_BAD_REQUEST,
"Operation not supported.")
def delete(self, request, repo_id, format=None):
# delete dir or file
repo = get_repo(repo_id)
2012-12-25 03:09:34 +00:00
if not repo:
return api_error(status.HTTP_404_NOT_FOUND, 'Repo not found.')
2012-12-23 14:27:16 +00:00
resp = check_repo_access_permission(request, repo)
if resp:
return resp
path = request.GET.get('p', None)
if not path:
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.')
if path == '/': # Can not delete root path.
return api_error(status.HTTP_400_BAD_REQUEST, 'Path is invalid.')
if path[-1] == '/': # Cut out last '/' if possible.
path = path[:-1]
parent_dir = os.path.dirname(path)
2013-02-25 07:04:13 +00:00
parent_dir_utf8 = os.path.dirname(path).encode('utf-8')
file_name_utf8 = os.path.basename(path).encode('utf-8')
2012-12-23 14:27:16 +00:00
try:
seafserv_threaded_rpc.del_file(repo_id, parent_dir_utf8,
file_name_utf8,
request.user.username)
except SearpcError, e:
return api_error(HTTP_520_OPERATION_FAILED,
"Failed to delete file.")
return reloaddir_if_neccessary(request, repo_id, parent_dir)
2013-02-20 12:51:37 +00:00
class SharedRepos(APIView):
"""
List repos that a user share to others/groups/public.
2013-02-20 12:51:37 +00:00
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, )
2013-02-20 12:51:37 +00:00
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
username = request.user.username
shared_repos = []
2012-12-23 14:27:16 +00:00
2013-02-20 12:51:37 +00:00
shared_repos += list_share_repos(username, 'from_email', -1, -1)
shared_repos += get_group_repos_by_owner(username)
if not CLOUD_MODE:
shared_repos += list_inner_pub_repos_by_owner(username)
return HttpResponse(json.dumps(shared_repos, cls=SearpcObjEncoder),
status=200, content_type=json_content_type)
class SharedRepo(APIView):
"""
Support uniform interface for shared libraries.
"""
authentication_classes = (TokenAuthentication, )
permission_classes = (IsAuthenticated, IsRepoOwner)
throttle_classes = (UserRateThrottle, )
def delete(self, request, repo_id, format=None):
"""
Unshare a library. Only repo owner can perform this operation.
"""
share_type = request.GET.get('share_type', '')
user = request.GET.get('user', '')
group_id = request.GET.get('group_id', '')
if not (share_type and user and group_id):
return api_error(status.HTTP_400_BAD_REQUEST,
'share_type and user and group_id is required.')
if share_type == 'personal':
remove_share(repo_id, request.user.username, user)
elif share_type == 'group':
unshare_group_repo(repo_id, group_id, user)
elif share_type == 'public':
unset_inner_pub_repo(repo_id)
else:
return api_error(status.HTTP_400_BAD_REQUEST,
'share_type can only be personal or group or public.')
return Response('success', status=status.HTTP_200_OK)