Exemple #1
0
def file_is_locked(request):

    token = request.GET.get('access_token', None)
    info_dict = get_file_info_by_token(token)
    repo_id = info_dict['repo_id']
    file_path = info_dict['file_path']

    return if_locked_by_online_office(repo_id, file_path)
Exemple #2
0
    def put(self, request, token):
        """ This api only used for refresh OnlineOffice lock
        when user edit office file via share link.

        Permission checking:
        1, If enable SHARE_LINK_LOGIN_REQUIRED, user must have been authenticated.
        2, Share link should have can_edit permission.
        3, File must have been locked by OnlineOffice.
        """

        if SHARE_LINK_LOGIN_REQUIRED and \
                not request.user.is_authenticated():
            error_msg = 'Permission denied.'
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        try:
            share_link = FileShare.objects.get(token=token)
        except FileShare.DoesNotExist:
            error_msg = 'Share link %s not found.' % token
            return api_error(status.HTTP_404_NOT_FOUND, error_msg)

        if share_link.is_expired():
            error_msg = 'Share link %s is expired.' % token
            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

        shared_by = share_link.username
        repo_id = share_link.repo_id
        path = normalize_file_path(share_link.path)
        parent_dir = os.path.dirname(path)
        if seafile_api.check_permission_by_path(
                repo_id, parent_dir, shared_by) != PERMISSION_READ_WRITE:
            error_msg = 'Permission denied.'
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        permissions = share_link.get_permissions()
        can_edit = permissions['can_edit']
        if not can_edit:
            error_msg = 'Share link %s has no edit permission.' % token
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        locked_by_online_office = if_locked_by_online_office(repo_id, path)
        if locked_by_online_office:
            # refresh lock file
            try:
                seafile_api.refresh_file_lock(repo_id, path)
            except SearpcError as e:
                logger.error(e)
                error_msg = 'Internal Server Error'
                return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
                                 error_msg)
        else:
            error_msg = _("You can not refresh this file's lock.")
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        return Response({'success': True})
Exemple #3
0
def file_is_locked(request):

    if not is_pro_version():
        key, value = generate_file_lock_key_value(request)
        return True if cache.get(key, '') else False

    token = request.GET.get('access_token', None)
    info_dict = get_file_info_by_token(token)
    repo_id = info_dict['repo_id']
    file_path = info_dict['file_path']

    return if_locked_by_online_office(repo_id, file_path)
Exemple #4
0
    def put(self, request, token):
        """ This api only used for refresh OnlineOffice lock
        when user edit office file via share link.

        Permission checking:
        1, If enable SHARE_LINK_LOGIN_REQUIRED, user must have been authenticated.
        2, Share link should have can_edit permission.
        3, File must have been locked by OnlineOffice.
        """

        if SHARE_LINK_LOGIN_REQUIRED and \
                not request.user.is_authenticated():
            error_msg = 'Permission denied.'
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        try:
            share_link = FileShare.objects.get(token=token)
        except FileShare.DoesNotExist:
            error_msg = 'Share link %s not found.' % token
            return api_error(status.HTTP_404_NOT_FOUND, error_msg)

        if share_link.is_expired():
            error_msg = 'Share link %s is expired.' % token
            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

        shared_by = share_link.username
        repo_id = share_link.repo_id
        path = normalize_file_path(share_link.path)
        parent_dir = os.path.dirname(path)
        if seafile_api.check_permission_by_path(repo_id,
                parent_dir, shared_by) != PERMISSION_READ_WRITE:
            error_msg = 'Permission denied.'
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        permissions = share_link.get_permissions()
        can_edit = permissions['can_edit']
        if not can_edit:
            error_msg = 'Share link %s has no edit permission.' % token
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        locked_by_online_office = if_locked_by_online_office(repo_id, path)
        if locked_by_online_office:
            # refresh lock file
            try:
                seafile_api.refresh_file_lock(repo_id, path)
            except SearpcError, e:
                logger.error(e)
                error_msg = 'Internal Server Error'
                return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
Exemple #5
0
    def get(self, request, file_id, format=None):
        """ WOPI endpoint for check file info
        """

        token = request.GET.get('access_token', None)
        request_user, repo_id, file_path = get_file_info_by_token(token)
        repo = seafile_api.get_repo(repo_id)
        obj_id = seafile_api.get_file_id_by_path(repo_id, file_path)

        try:
            file_size = seafile_api.get_file_size(repo.store_id, repo.version,
                                                  obj_id)
        except SearpcError as e:
            logger.error(e)
            return HttpResponse(json.dumps({}),
                                status=500,
                                content_type=json_content_type)

        if file_size == -1:
            logger.error('File %s not found.' % file_path)
            return HttpResponse(json.dumps({}),
                                status=401,
                                content_type=json_content_type)

        result = {}
        # necessary
        result['BaseFileName'] = os.path.basename(file_path)
        result['Size'] = file_size
        result['UserId'] = request_user
        result['Version'] = obj_id

        try:
            if is_pro_version():
                result['OwnerId'] = seafile_api.get_repo_owner(repo_id) or \
                        seafile_api.get_org_repo_owner(repo_id)
            else:
                result['OwnerId'] = seafile_api.get_repo_owner(repo_id)
        except Exception as e:
            logger.error(e)
            return HttpResponse(json.dumps({}),
                                status=500,
                                content_type=json_content_type)

        # optional
        result['UserFriendlyName'] = email2nickname(request_user)
        absolute_uri = request.build_absolute_uri('/')
        result['PostMessageOrigin'] = urlparse.urljoin(absolute_uri,
                                                       SITE_ROOT).strip('/')
        result['HidePrintOption'] = False
        result['HideSaveOption'] = False
        result['HideExportOption'] = False
        result['EnableOwnerTermination'] = True
        result['SupportsLocks'] = True
        result['SupportsGetLock'] = True
        result['SupportsUpdate'] = True

        filename = os.path.basename(file_path)
        filetype, fileext = get_file_type_and_ext(filename)

        try:
            is_locked, locked_by_me = check_file_lock(repo_id, file_path,
                                                      request_user)
        except Exception as e:
            logger.error(e)
            return HttpResponse(json.dumps({}),
                                status=500,
                                content_type=json_content_type)

        locked_by_online_office = if_locked_by_online_office(
            repo_id, file_path)

        perm = seafile_api.check_permission_by_path(repo_id, file_path,
                                                    request_user)

        if ENABLE_OFFICE_WEB_APP_EDIT and not repo.encrypted and \
                perm == 'rw' and fileext in OFFICE_WEB_APP_EDIT_FILE_EXTENSION and \
                ((not is_locked) or (is_locked and locked_by_me) or \
                (is_locked and locked_by_online_office)):
            result['UserCanWrite'] = True

        return HttpResponse(json.dumps(result),
                            status=200,
                            content_type=json_content_type)
Exemple #6
0
def onlyoffice_editor_callback(request):
    """ Callback func of OnlyOffice.

    The document editing service informs the document storage service about status of the document editing using the callbackUrl from JavaScript API. The document editing service use the POST request with the information in body.

    https://api.onlyoffice.com/editors/callback
    """

    if request.method != 'POST':
        logger.error('Request method if not POST.')
        # The document storage service must return the following response.
        # otherwise the document editor will display an error message.
        return HttpResponse('{"error": 0}')

    # body info of POST rquest when open file on browser
    # {u'actions': [{u'type': 1, u'userid': u'uid-1527736776860'}],
    #  u'key': u'8062bdccf9b4cf809ae3',
    #  u'status': 1,
    #  u'users': [u'uid-1527736776860']}

    # body info of POST rquest when close file's web page (save file)
    # {u'actions': [{u'type': 0, u'userid': u'uid-1527736951523'}],
    # u'changesurl': u'...',
    # u'history': {u'changes': [{u'created': u'2018-05-31 03:17:17',
    #                            u'user': {u'id': u'uid-1527736577058',
    #                                      u'name': u'lian'}},
    #                           {u'created': u'2018-05-31 03:23:55',
    #                            u'user': {u'id': u'uid-1527736951523',
    #                                      u'name': u'lian'}}],
    #              u'serverVersion': u'5.1.4'},
    # u'key': u'61484dec693009f3d506',
    # u'lastsave': u'2018-05-31T03:23:55.767Z',
    # u'notmodified': False,
    # u'status': 2,
    # u'url': u'...',
    # u'users': [u'uid-1527736951523']}

    # Defines the status of the document. Can have the following values:
    # 0 - no document with the key identifier could be found,
    # 1 - document is being edited,
    # 2 - document is ready for saving,
    # 3 - document saving error has occurred,
    # 4 - document is closed with no changes,
    # 6 - document is being edited, but the current document state is saved,
    # 7 - error has occurred while force saving the document.

    # Status 1 is received every user connection to or disconnection from document co-editing.
    #
    # Status 2 (3) is received 10 seconds after the document is closed for editing with the identifier of the user who was the last to send the changes to the document editing service.
    #
    # Status 4 is received after the document is closed for editing with no changes by the last user.
    #
    # Status 6 (7) is received when the force saving request is performed.

    post_data = json.loads(request.body)
    status = int(post_data.get('status', -1))

    if status not in (1, 2, 4, 6):
        logger.error('onlyoffice status invalid: {}'.format(status))
        return HttpResponse('{"error": 0}')

    # get file basic info
    doc_key = post_data.get('key')
    doc_info = json.loads(cache.get("ONLYOFFICE_%s" % doc_key))

    repo_id = doc_info['repo_id']
    file_path = doc_info['file_path']
    username = doc_info['username']

    cache_key = generate_onlyoffice_cache_key(repo_id, file_path)

    # save file
    if status in (2, 6):

        # Defines the link to the edited document to be saved with the document storage service.
        # The link is present when the status value is equal to 2 or 3 only.
        url = post_data.get('url')
        onlyoffice_resp = requests.get(url,
                                       verify=VERIFY_ONLYOFFICE_CERTIFICATE)
        if not onlyoffice_resp:
            logger.error('[OnlyOffice] No response from file content url.')
            return HttpResponse('{"error": 0}')

        fake_obj_id = {'online_office_update': True}
        update_token = seafile_api.get_fileserver_access_token(
            repo_id, json.dumps(fake_obj_id), 'update', username)

        if not update_token:
            logger.error('[OnlyOffice] No fileserver access token.')
            return HttpResponse('{"error": 0}')

        # get file content
        files = {
            'file': onlyoffice_resp.content,
            'file_name': os.path.basename(file_path),
            'target_file': file_path,
        }

        # update file
        update_url = gen_inner_file_upload_url('update-api', update_token)
        requests.post(update_url, files=files)

        # 2 - document is ready for saving,
        if status == 2:

            cache.delete(cache_key)
            cache.delete("ONLYOFFICE_%s" % doc_key)

            if is_pro_version() and if_locked_by_online_office(
                    repo_id, file_path):
                seafile_api.unlock_file(repo_id, file_path)

        # 6 - document is being edited, but the current document state is saved,
        if status == 6:
            # cache document key when forcesave
            cache.set(cache_key, doc_key)

    # 4 - document is closed with no changes,
    if status == 4:

        cache.delete(cache_key)
        cache.delete("ONLYOFFICE_%s" % doc_key)

        if is_pro_version() and if_locked_by_online_office(repo_id, file_path):
            seafile_api.unlock_file(repo_id, file_path)

    return HttpResponse('{"error": 0}')
Exemple #7
0
    def put(self, request, repo_id, format=None):
        """ Currently only support lock, unlock, refresh-lock file.

        Permission checking:
        1. user with 'rw' permission for current file;
        """

        if not is_pro_version():
            error_msg = 'file lock feature only supported in professional edition.'
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        # argument check
        path = request.GET.get('p', None)
        if not path:
            error_msg = 'p invalid.'
            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
        path = normalize_file_path(path)

        operation = request.data.get('operation', None)
        if not operation:
            error_msg = 'operation invalid.'
            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

        operation = operation.lower()
        if operation not in ('lock', 'unlock', 'refresh-lock'):
            error_msg = "operation can only be 'lock', 'unlock' or 'refresh-lock'."
            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

        # resource check
        repo = seafile_api.get_repo(repo_id)
        if not repo:
            error_msg = 'Library %s not found.' % repo_id
            return api_error(status.HTTP_404_NOT_FOUND, error_msg)

        file_id = seafile_api.get_file_id_by_path(repo_id, path)
        if not file_id:
            error_msg = 'File %s not found.' % path
            return api_error(status.HTTP_404_NOT_FOUND, error_msg)

        # permission check
        parent_dir = os.path.dirname(path)
        if check_folder_permission(request, repo_id, parent_dir) != PERMISSION_READ_WRITE:
            error_msg = 'Permission denied.'
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        username = request.user.username
        try:
            is_locked, locked_by_me = check_file_lock(repo_id, path, username)
        except Exception as e:
            logger.error(e)
            error_msg = 'Internal Server Error'
            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)

        # check if is locked by online office
        locked_by_online_office = if_locked_by_online_office(repo_id, path)

        if operation == 'lock':

            if is_locked:
                error_msg = _("File is locked")
                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

            # lock file
            expire = request.data.get('expire', FILE_LOCK_EXPIRATION_DAYS)
            try:
                seafile_api.lock_file(repo_id, path, username, expire)
            except SearpcError, e:
                logger.error(e)
                error_msg = 'Internal Server Error'
                return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
Exemple #8
0
    def put(self, request, repo_id, format=None):
        """ Currently only support lock, unlock, refresh-lock file.

        Permission checking:
        1. user with 'rw' permission for current file;
        """

        if not is_pro_version():
            error_msg = 'file lock feature only supported in professional edition.'
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        # argument check
        path = request.GET.get('p', None)
        if not path:
            error_msg = 'p invalid.'
            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
        path = normalize_file_path(path)

        operation = request.data.get('operation', None)
        if not operation:
            error_msg = 'operation invalid.'
            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

        operation = operation.lower()
        if operation not in ('lock', 'unlock', 'refresh-lock'):
            error_msg = "operation can only be 'lock', 'unlock' or 'refresh-lock'."
            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

        # resource check
        repo = seafile_api.get_repo(repo_id)
        if not repo:
            error_msg = 'Library %s not found.' % repo_id
            return api_error(status.HTTP_404_NOT_FOUND, error_msg)

        file_id = seafile_api.get_file_id_by_path(repo_id, path)
        if not file_id:
            error_msg = 'File %s not found.' % path
            return api_error(status.HTTP_404_NOT_FOUND, error_msg)

        # permission check
        parent_dir = os.path.dirname(path)
        if check_folder_permission(request, repo_id,
                                   parent_dir) != PERMISSION_READ_WRITE:
            error_msg = 'Permission denied.'
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        username = request.user.username
        try:
            is_locked, locked_by_me = check_file_lock(repo_id, path, username)
        except Exception as e:
            logger.error(e)
            error_msg = 'Internal Server Error'
            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)

        # check if is locked by online office
        locked_by_online_office = if_locked_by_online_office(repo_id, path)

        if operation == 'lock':

            if is_locked:
                error_msg = _("File is locked")
                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

            expire = request.data.get('expire', 0)
            try:
                expire = int(expire)
            except ValueError:
                error_msg = 'expire invalid.'
                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

            if expire < 0:
                error_msg = 'expire invalid.'
                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

            # lock file
            try:
                if expire > 0:
                    seafile_api.lock_file(repo_id, path, username,
                                          int(time.time()) + expire)
                else:
                    seafile_api.lock_file(repo_id, path, username, 0)
            except SearpcError as e:
                logger.error(e)
                error_msg = 'Internal Server Error'
                return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
                                 error_msg)

        if operation == 'unlock':

            if not is_locked:
                error_msg = _("File is not locked.")
                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

            if locked_by_me or locked_by_online_office or \
                    is_repo_owner(request, repo_id, username) or \
                    is_repo_admin(username, repo_id):
                # unlock file
                try:
                    seafile_api.unlock_file(repo_id, path)
                except SearpcError as e:
                    logger.error(e)
                    error_msg = 'Internal Server Error'
                    return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
                                     error_msg)
            else:
                error_msg = 'You can not unlock this file.'
                return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        if operation == 'refresh-lock':

            if not is_locked:
                error_msg = _("File is not locked.")
                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

            expire = request.data.get('expire', 0)
            try:
                expire = int(expire)
            except ValueError:
                error_msg = 'expire invalid.'
                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

            if expire < 0:
                error_msg = 'expire invalid.'
                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

            if locked_by_me or locked_by_online_office:
                # refresh lock file
                try:
                    if expire > 0:
                        seafile_api.refresh_file_lock(
                            repo_id, path,
                            int(time.time()) + expire)
                    else:
                        seafile_api.refresh_file_lock(repo_id, path)
                except SearpcError as e:
                    logger.error(e)
                    error_msg = 'Internal Server Error'
                    return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR,
                                     error_msg)
            else:
                error_msg = _("You can not refresh this file's lock.")
                return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        file_info = self.get_file_info(username, repo_id, path)
        return Response(file_info)
Exemple #9
0
def get_onlyoffice_dict(request,
                        username,
                        repo_id,
                        file_path,
                        file_id='',
                        can_edit=False,
                        can_download=True):

    logger.info('{} open file {} in repo {} with can_edit {}'.format(
        username, file_path, repo_id, can_edit))

    repo = seafile_api.get_repo(repo_id)
    if repo.is_virtual:
        origin_repo_id = repo.origin_repo_id
        origin_file_path = posixpath.join(repo.origin_path,
                                          file_path.strip('/'))
        # for view history/trash/snapshot file
        if not file_id:
            file_id = seafile_api.get_file_id_by_path(origin_repo_id,
                                                      origin_file_path)
    else:
        origin_repo_id = repo_id
        origin_file_path = file_path
        if not file_id:
            file_id = seafile_api.get_file_id_by_path(repo_id, file_path)

    dl_token = seafile_api.get_fileserver_access_token(repo_id,
                                                       file_id,
                                                       'download',
                                                       username,
                                                       use_onetime=False)
    if not dl_token:
        return None

    filetype, fileext = get_file_type_and_ext(file_path)
    if fileext in ('xls', 'xlsx', 'ods', 'fods', 'csv'):
        document_type = 'spreadsheet'
    elif fileext in ('pptx', 'ppt', 'odp', 'fodp', 'ppsx', 'pps'):
        document_type = 'presentation'
    else:
        document_type = 'text'

    if not can_edit:
        info_bytes = force_bytes(origin_repo_id + origin_file_path + file_id)
        doc_key = hashlib.md5(info_bytes).hexdigest()[:20]
    else:
        cache_key = generate_onlyoffice_cache_key(origin_repo_id,
                                                  origin_file_path)
        doc_key = cache.get(cache_key)

        # temporary solution when failed to get data from cache(django_pylibmc)
        # when init process for the first time
        if not doc_key:
            doc_key = cache.get(cache_key)

        if doc_key:
            logger.info('get doc_key {} from cache by cache_key {}'.format(
                doc_key, cache_key))
        else:
            # In theory, file is unlocked when editing finished.
            # This can happend if memcache is restarted or memcache is full and doc key is deleted.
            if if_locked_by_online_office(repo_id, file_path):
                logger.warning(
                    'no doc_key in cache, but file {} in {} is locked by online office'
                    .format(file_path, repo_id))

            # generate doc_key
            info_bytes = force_bytes(origin_repo_id + origin_file_path +
                                     file_id)
            doc_key = hashlib.md5(info_bytes).hexdigest()[:20]
            logger.info(
                'generate new doc_key {} by repo_id {} file_path {} file_id {}'
                .format(doc_key, origin_repo_id, origin_file_path, file_id))
            logger.info('set cache_key {} and doc_key {} to cache'.format(
                cache_key, doc_key))
            cache.set(cache_key, doc_key, None)

        if not cache.get("ONLYOFFICE_%s" % doc_key):

            doc_info = json.dumps({
                'repo_id': origin_repo_id,
                'file_path': origin_file_path,
                'username': username
            })

            cache.set("ONLYOFFICE_%s" % doc_key, doc_info, None)
            logger.info('set doc_key {} and doc_info {} to cache'.format(
                doc_key, doc_info))

    # for render onlyoffice html
    file_name = os.path.basename(file_path.rstrip('/'))
    doc_url = gen_file_get_url(dl_token, file_name)

    base_url = get_site_scheme_and_netloc()
    onlyoffice_editor_callback_url = reverse('onlyoffice_editor_callback')
    callback_url = urllib.parse.urljoin(base_url,
                                        onlyoffice_editor_callback_url)

    return_dict = {
        'repo_id': repo_id,
        'path': file_path,
        'ONLYOFFICE_APIJS_URL': ONLYOFFICE_APIJS_URL,
        'file_type': fileext,
        'doc_key': doc_key,
        'doc_title': file_name,
        'doc_url': doc_url,
        'document_type': document_type,
        'callback_url': callback_url,
        'can_edit': can_edit,
        'can_download': can_download,
        'username': username,
        'onlyoffice_force_save': ONLYOFFICE_FORCE_SAVE,
        'enable_watermark': ENABLE_WATERMARK,
    }

    if ONLYOFFICE_JWT_SECRET:
        import jwt
        config = {
            "document": {
                "fileType": fileext,
                "key": doc_key,
                "title": file_name,
                "url": doc_url,
                "permissions": {
                    "download": can_download,
                    "edit": can_edit,
                    "print": can_download,
                    "review": True
                }
            },
            "documentType": document_type,
            "editorConfig": {
                "callbackUrl": callback_url,
                "lang": request.LANGUAGE_CODE,
                "mode": can_edit,
                "customization": {
                    "forcesave": ONLYOFFICE_FORCE_SAVE,
                },
                "user": {
                    "name": email2nickname(username)
                }
            }
        }

        return_dict['onlyoffice_jwt_token'] = jwt.encode(
            config, ONLYOFFICE_JWT_SECRET)

    return return_dict
Exemple #10
0
    def put(self, request, repo_id, format=None):
        """ Currently only support lock, unlock, refresh-lock file.

        Permission checking:
        1. user with 'rw' permission for current file;
        """

        if not is_pro_version():
            error_msg = 'file lock feature only supported in professional edition.'
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        # argument check
        path = request.GET.get('p', None)
        if not path:
            error_msg = 'p invalid.'
            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
        path = normalize_file_path(path)

        operation = request.data.get('operation', None)
        if not operation:
            error_msg = 'operation invalid.'
            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

        operation = operation.lower()
        if operation not in ('lock', 'unlock', 'refresh-lock'):
            error_msg = "operation can only be 'lock', 'unlock' or 'refresh-lock'."
            return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

        # resource check
        repo = seafile_api.get_repo(repo_id)
        if not repo:
            error_msg = 'Library %s not found.' % repo_id
            return api_error(status.HTTP_404_NOT_FOUND, error_msg)

        file_id = seafile_api.get_file_id_by_path(repo_id, path)
        if not file_id:
            error_msg = 'File %s not found.' % path
            return api_error(status.HTTP_404_NOT_FOUND, error_msg)

        # permission check
        parent_dir = os.path.dirname(path)
        if check_folder_permission(request, repo_id, parent_dir) != PERMISSION_READ_WRITE:
            error_msg = 'Permission denied.'
            return api_error(status.HTTP_403_FORBIDDEN, error_msg)

        username = request.user.username
        try:
            is_locked, locked_by_me = check_file_lock(repo_id, path, username)
        except Exception as e:
            logger.error(e)
            error_msg = 'Internal Server Error'
            return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)

        # check if is locked by online office
        locked_by_online_office = if_locked_by_online_office(repo_id, path)

        if operation == 'lock':

            if is_locked:
                error_msg = _("File is locked")
                return api_error(status.HTTP_400_BAD_REQUEST, error_msg)

            # lock file
            expire = request.data.get('expire', FILE_LOCK_EXPIRATION_DAYS)
            try:
                seafile_api.lock_file(repo_id, path, username, expire)
            except SearpcError, e:
                logger.error(e)
                error_msg = 'Internal Server Error'
                return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)