1# Copyright (c) 2012-2016 Seafile Ltd.
2# -*- coding: utf-8 -*-
3import stat
4import logging
5from collections import namedtuple
6
7import seaserv
8from seaserv import seafile_api, ccnet_api
9
10from seahub.constants import (
11    PERMISSION_PREVIEW, PERMISSION_PREVIEW_EDIT,
12    PERMISSION_READ, PERMISSION_READ_WRITE, PERMISSION_ADMIN,
13    REPO_STATUS_NORMAL, REPO_STATUS_READ_ONLY
14)
15from seahub.utils import EMPTY_SHA1, is_org_context, is_pro_version
16from seahub.base.models import RepoSecretKey
17from seahub.base.templatetags.seahub_tags import email2nickname
18
19from seahub.settings import ENABLE_STORAGE_CLASSES, \
20        STORAGE_CLASS_MAPPING_POLICY, ENABLE_FOLDER_PERM
21
22logger = logging.getLogger(__name__)
23
24
25def normalize_repo_status_code(status):
26    if status == 0:
27        return REPO_STATUS_NORMAL
28    elif status == 1:
29        return REPO_STATUS_READ_ONLY
30    else:
31        return ''
32
33
34def normalize_repo_status_str(status):
35    if status == 'normal':
36        return 0
37    elif status == 'read-only':
38        return 1
39    else:
40        return ''
41
42
43def get_available_repo_perms():
44    perms = [PERMISSION_READ, PERMISSION_READ_WRITE, PERMISSION_ADMIN]
45    if is_pro_version():
46        perms += [PERMISSION_PREVIEW, PERMISSION_PREVIEW_EDIT]
47
48    return perms
49
50
51def parse_repo_perm(perm):
52    RP = namedtuple('RepoPerm', [
53        'can_download', 'can_upload',  # download/uplaod files/folders
54        'can_edit_on_web',             # edit files on web
55        'can_copy',                    # copy files/folders on web
56        'can_preview',                 # preview files on web
57        'can_generate_share_link',     # generate share link
58    ])
59
60    RP.can_download = True if perm in [
61        PERMISSION_READ, PERMISSION_READ_WRITE, PERMISSION_ADMIN] else False
62    RP.can_upload = True if perm in [
63        PERMISSION_READ_WRITE, PERMISSION_ADMIN] else False
64    RP.can_edit_on_web = True if perm in [
65        PERMISSION_READ_WRITE, PERMISSION_ADMIN, PERMISSION_PREVIEW_EDIT
66    ] else False
67    RP.can_copy = True if perm in [
68        PERMISSION_READ, PERMISSION_READ_WRITE, PERMISSION_ADMIN,
69    ] else False
70    RP.can_preview = True if perm in [
71        PERMISSION_READ, PERMISSION_READ_WRITE, PERMISSION_ADMIN,
72        PERMISSION_PREVIEW, PERMISSION_PREVIEW_EDIT
73    ] else False
74    RP.can_generate_share_link = True if perm in [
75        PERMISSION_READ_WRITE, PERMISSION_READ, PERMISSION_ADMIN,
76        PERMISSION_PREVIEW, PERMISSION_PREVIEW_EDIT
77    ] else False
78    return RP
79
80def list_dir_by_path(cmmt, path):
81    if cmmt.root_id == EMPTY_SHA1:
82        return []
83    else:
84        dirs = seafile_api.list_dir_by_commit_and_path(cmmt.repo_id, cmmt.id, path)
85        return dirs if dirs else []
86
87def get_sub_repo_abbrev_origin_path(repo_name, origin_path):
88    """Return abbrev path for sub repo based on `repo_name` and `origin_path`.
89
90    Arguments:
91    - `repo_id`:
92    - `origin_path`:
93    """
94    if len(origin_path) > 20:
95        abbrev_path = origin_path[-20:]
96        return repo_name + '/...' + abbrev_path
97    else:
98        return repo_name + origin_path
99
100def get_repo_owner(request, repo_id):
101    if is_org_context(request):
102        repo_owner = seafile_api.get_org_repo_owner(repo_id)
103    else:
104        # for admin panel
105        # administrator may get org repo's owner
106        repo_owner = seafile_api.get_repo_owner(repo_id)
107        if not repo_owner:
108            repo_owner = seafile_api.get_org_repo_owner(repo_id)
109
110    return repo_owner
111
112def is_repo_owner(request, repo_id, username):
113    return username == get_repo_owner(request, repo_id)
114
115def get_repo_shared_users(repo_id, repo_owner, include_groups=True):
116    """Return a list contains users and group users. Repo owner is ommited.
117    """
118    ret = []
119    users = seafile_api.list_repo_shared_to(repo_owner, repo_id)
120    ret += [x.user for x in users]
121    if include_groups:
122        for e in seafile_api.list_repo_shared_group_by_user(repo_owner, repo_id):
123            g_members = seaserv.get_group_members(e.group_id)
124            ret += [x.user_name for x in g_members if x.user_name != repo_owner]
125
126    return list(set(ret))
127
128def get_library_storages(request):
129    """ Return info of storages can be used.
130
131    1. If not enable user role feature OR
132       haven't configured `storage_ids` option in user role setting:
133
134       Return storage info getted from seafile_api.
135       And always put the default storage as the first item in the returned list.
136
137    2. If have configured `storage_ids` option in user role setting:
138
139       Only return storage info in `storage_ids`.
140       Filter out the wrong stotage id(s).
141       Not change the order of the `storage_ids` list.
142    """
143
144    if not is_pro_version():
145        return []
146
147    if not ENABLE_STORAGE_CLASSES:
148        return []
149
150    # get all storages info
151    try:
152        storage_classes = seafile_api.get_all_storage_classes()
153    except Exception as e:
154        logger.error(e)
155        return []
156
157    all_storages = []
158    for storage in storage_classes:
159        storage_info = {
160            'storage_id': storage.storage_id,
161            'storage_name': storage.storage_name,
162            'is_default': storage.is_default,
163        }
164        if storage.is_default:
165            all_storages.insert(0, storage_info)
166        else:
167            all_storages.append(storage_info)
168
169    if STORAGE_CLASS_MAPPING_POLICY == 'USER_SELECT':
170
171        return all_storages
172
173    elif STORAGE_CLASS_MAPPING_POLICY == 'ROLE_BASED':
174        user_role_storage_ids = request.user.permissions.storage_ids()
175        if not user_role_storage_ids:
176            return []
177
178        user_role_storages = []
179        for user_role_storage_id in user_role_storage_ids:
180            for storage in all_storages:
181                if storage['storage_id'] == user_role_storage_id:
182                    user_role_storages.append(storage)
183                    continue
184
185        return user_role_storages
186
187    else:
188        # STORAGE_CLASS_MAPPING_POLICY == 'REPO_ID_MAPPING'
189        return []
190
191def get_locked_files_by_dir(request, repo_id, folder_path):
192    """ Get locked files in a folder
193
194    Returns:
195        A dict contains locked file name and locker owner.
196
197        locked_files = {
198            'file_name': 'lock_owner';
199            ...
200        }
201    """
202
203    username = request.user.username
204
205    # get lock files
206    dir_id = seafile_api.get_dir_id_by_path(repo_id, folder_path)
207    dirents = seafile_api.list_dir_with_perm(repo_id,
208            folder_path, dir_id, username, -1, -1)
209
210    locked_files = {}
211    for dirent in dirents:
212        if dirent.is_locked:
213            locked_files[dirent.obj_name] = dirent.lock_owner
214
215    return locked_files
216
217def get_sub_folder_permission_by_dir(request, repo_id, parent_dir):
218    """ Get sub folder permission in a folder
219
220    Returns:
221        A dict contains folder name and permission.
222
223        folder_permission_dict = {
224            'folder_name_1': 'r';
225            'folder_name_2': 'rw';
226            ...
227        }
228    """
229    username = request.user.username
230    dir_id = seafile_api.get_dir_id_by_path(repo_id, parent_dir)
231    dirents = seafile_api.list_dir_with_perm(repo_id,
232            parent_dir, dir_id, username, -1, -1)
233
234    folder_permission_dict = {}
235    for dirent in dirents:
236        if stat.S_ISDIR(dirent.mode):
237            folder_permission_dict[dirent.obj_name] = dirent.permission
238
239    return folder_permission_dict
240
241def get_shared_groups_by_repo(repo_id, org_id=None):
242    if not org_id:
243        group_ids = seafile_api.get_shared_group_ids_by_repo(
244                repo_id)
245    else:
246        group_ids = seafile_api.org_get_shared_group_ids_by_repo(org_id,
247                repo_id)
248
249    if not group_ids:
250        return []
251
252    groups = []
253    for group_id in group_ids:
254        group = ccnet_api.get_group(int(group_id))
255        if group:
256            groups.append(group)
257
258    return groups
259
260def get_related_users_by_repo(repo_id, org_id=None):
261    """ Return all users who can view this library.
262
263    1. repo owner
264    2. users repo has been shared to
265    3. members of groups repo has been shared to
266    """
267
268    users = []
269
270    # 1. users repo has been shared to
271    if org_id and org_id > 0:
272        users.extend(seafile_api.org_get_shared_users_by_repo(org_id, repo_id))
273        owner = seafile_api.get_org_repo_owner(repo_id)
274    else:
275        users.extend(seafile_api.get_shared_users_by_repo(repo_id))
276        owner = seafile_api.get_repo_owner(repo_id)
277
278    # 2. repo owner
279    if owner not in users:
280        users.append(owner)
281
282    # 3. members of groups repo has been shared to
283    groups = get_shared_groups_by_repo(repo_id, org_id)
284    for group in groups:
285        members = ccnet_api.get_group_members(group.id)
286        for member in members:
287            if member.user_name not in users:
288                users.append(member.user_name)
289
290    return users
291
292# TODO
293def is_valid_repo_id_format(repo_id):
294    return len(repo_id) == 36
295
296def can_set_folder_perm_by_user(username, repo, repo_owner):
297    """ user can get/update/add/delete folder perm feature must comply with the following
298            setting: ENABLE_FOLDER_PERM
299            repo:repo is not virtual
300            permission: is admin or repo owner.
301    """
302    if not ENABLE_FOLDER_PERM:
303        return False
304    if repo.is_virtual:
305        return False
306    is_admin = is_repo_admin(username, repo.id)
307    if username != repo_owner and not is_admin:
308        return False
309    return True
310
311def add_encrypted_repo_secret_key_to_database(repo_id, password):
312    try:
313        if not RepoSecretKey.objects.get_secret_key(repo_id):
314            # get secret_key, then save it to database
315            secret_key = seafile_api.get_secret_key(repo_id, password)
316            RepoSecretKey.objects.add_secret_key(repo_id, secret_key)
317    except Exception as e:
318        logger.error(e)
319
320
321def is_group_repo_staff(request, repo_id, username):
322    is_staff = False
323
324    repo_owner = get_repo_owner(request, repo_id)
325
326    if '@seafile_group' in repo_owner:
327        group_id = email2nickname(repo_owner)
328        is_staff = seaserv.check_group_staff(group_id, username)
329
330    return is_staff
331
332def repo_has_been_shared_out(request, repo_id):
333
334    has_been_shared_out = False
335    username = request.user.username
336
337    if is_org_context(request):
338        org_id = request.user.org.org_id
339
340        is_inner_org_pub_repo = False
341        # check if current repo is pub-repo
342        org_pub_repos = seafile_api.list_org_inner_pub_repos_by_owner(
343                org_id, username)
344        for org_pub_repo in org_pub_repos:
345            if repo_id == org_pub_repo.id:
346                is_inner_org_pub_repo = True
347                break
348
349        if seafile_api.org_repo_has_been_shared(repo_id, including_groups=True) or is_inner_org_pub_repo:
350            has_been_shared_out = True
351    else:
352        if seafile_api.repo_has_been_shared(repo_id, including_groups=True) or \
353                (not request.cloud_mode and seafile_api.is_inner_pub_repo(repo_id)):
354            has_been_shared_out = True
355
356    return has_been_shared_out
357
358# TODO
359from seahub.share.utils import is_repo_admin
360