1from wsgidav.dav_error import DAVError, HTTP_BAD_REQUEST, HTTP_FORBIDDEN, \ 2 HTTP_NOT_FOUND, HTTP_INTERNAL_ERROR 3from wsgidav.dav_provider import DAVProvider, DAVCollection, DAVNonCollection 4from threading import Timer, Lock 5 6import wsgidav.util as util 7import os 8import time 9import posixpath 10 11import tempfile 12 13from seaserv import seafile_api, CALC_SHARE_USAGE 14from pysearpc import SearpcError 15from seafobj import commit_mgr, fs_mgr 16from seafobj.fs import SeafFile, SeafDir 17from seafobj.blocks import block_mgr 18from wsgidav.dc.seaf_utils import SEAFILE_CONF_DIR 19 20__docformat__ = "reStructuredText" 21 22_logger = util.get_module_logger(__name__) 23 24NEED_PROGRESS = 0 25SYNCHRONOUS = 1 26 27INFINITE_QUOTA = -2 28 29def sort_repo_list(repos): 30 return sorted(repos, key = lambda r: r.id) 31 32class BlockMap(object): 33 def __init__(self): 34 self.block_sizes = [] 35 self.timestamp = time.time() 36 37class SeafileStream(object): 38 '''Implements basic file-like interface''' 39 def __init__(self, file_obj, block_map, block_map_lock): 40 self.file_obj = file_obj 41 self.block = None 42 self.block_idx = 0 43 self.block_offset = 0 44 self.block_map = block_map 45 self.block_map_lock = block_map_lock 46 47 def read(self, size): 48 remain = size 49 blocks = self.file_obj.blocks 50 ret = b'' 51 52 while True: 53 if not self.block: 54 if self.block_idx == len(blocks): 55 break 56 self.block = block_mgr.load_block(self.file_obj.store_id, 57 self.file_obj.version, 58 blocks[self.block_idx]) 59 60 if self.block_offset + remain >= len(self.block): 61 self.block_idx += 1 62 ret += self.block[self.block_offset:] 63 remain -= (len(self.block) - self.block_offset) 64 self.block = None 65 self.block_offset = 0 66 else: 67 ret += self.block[self.block_offset:self.block_offset+remain] 68 self.block_offset += remain 69 remain = 0 70 71 if remain == 0: 72 break 73 74 return ret 75 76 def close(self): 77 pass 78 79 def seek(self, pos): 80 self.block = None 81 self.block_idx = 0 82 self.block_offset = 0 83 84 current_pos = pos 85 if current_pos == 0: 86 return 87 88 with self.block_map_lock: 89 if self.file_obj.obj_id not in self.block_map: 90 block_map = BlockMap() 91 for i in range(len(self.file_obj.blocks)): 92 block_size = block_mgr.stat_block(self.file_obj.store_id, self.file_obj.version, self.file_obj.blocks[i]) 93 block_map.block_sizes.append(block_size) 94 self.block_map[self.file_obj.obj_id] = block_map 95 block_map = self.block_map[self.file_obj.obj_id] 96 block_map.timestamp = time.time() 97 98 while current_pos > 0: 99 if self.block_idx == len(self.file_obj.blocks): 100 break 101 block_size = block_map.block_sizes[self.block_idx] 102 if current_pos >= block_size: 103 self.block_idx += 1 104 current_pos -= block_size 105 self.block_offset = 0 106 else: 107 self.block_offset = current_pos 108 current_pos = 0 109 110#=============================================================================== 111# SeafileResource 112#=============================================================================== 113class SeafileResource(DAVNonCollection): 114 def __init__(self, path, repo, rel_path, obj, environ, block_map={}, block_map_lock=None): 115 super(SeafileResource, self).__init__(path, environ) 116 self.repo = repo 117 self.rel_path = rel_path 118 self.obj = obj 119 self.username = environ.get("http_authenticator.username", "") 120 self.org_id = environ.get("seafile.org_id", "") 121 self.is_guest = environ.get("seafile.is_guest", False) 122 self.tmpfile_path = None 123 self.owner = None 124 self.block_map = block_map 125 self.block_map_lock = block_map_lock 126 127 # Getter methods for standard live properties 128 def get_content_length(self): 129 return self.obj.size 130 def get_content_type(self): 131# (mimetype, _mimeencoding) = mimetypes.guess_type(self.path) 132# print "mimetype(%s): %r, %r" % (self.path, mimetype, _mimeencoding) 133# if not mimetype: 134# mimetype = "application/octet-stream" 135# print "mimetype(%s): return %r" % (self.path, mimetype) 136# return mimetype 137 return util.guess_mime_type(self.path) 138 def get_creation_date(self): 139# return int(time.time()) 140 return None 141 def get_display_name(self): 142 return self.name 143 def get_etag(self): 144 return self.obj.obj_id 145 146 def get_last_modified(self): 147 cached_mtime = getattr(self.obj, 'last_modified', None) 148 if cached_mtime: 149 return cached_mtime 150 151 if self.obj.mtime > 0: 152 return self.obj.mtime 153 154 # XXX: What about not return last modified for files in v0 repos, 155 # since they can be too expensive sometimes? 156 parent, filename = os.path.split(self.rel_path) 157 try: 158 mtimes = seafile_api.get_files_last_modified(self.repo.id, parent, -1) 159 except SearpcError as e: 160 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 161 for mtime in mtimes: 162 if (mtime.file_name == filename): 163 return mtime.last_modified 164 165 return None 166 167 def support_etag(self): 168 return True 169 def support_ranges(self): 170 return True 171 172 def get_content(self): 173 """Open content as a stream for reading. 174 175 See DAVResource.getContent() 176 """ 177 assert not self.is_collection 178 return SeafileStream(self.obj, self.block_map, self.block_map_lock) 179 180 def check_repo_owner_quota(self, isnewfile=True, contentlength=-1): 181 """Check if the upload would cause the user quota be exceeded 182 183 `contentlength` is only positive when the client does not use "transfer-encode: chunking" 184 185 Return True if the quota would not be exceeded, otherwise return False. 186 """ 187 try: 188 if contentlength <= 0: 189 # When client use "transfer-encode: chunking", the content length 190 # is not included in the request headers 191 if isnewfile: 192 return seafile_api.check_quota(self.repo.id) >= 0 193 else: 194 return True 195 else: 196 delta = contentlength - self.obj.size 197 return seafile_api.check_quota(self.repo.id, delta) >= 0 198 except SearpcError as e: 199 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 200 201 def begin_write(self, content_type=None, isnewfile=True, contentlength=-1): 202 """Open content as a stream for writing. 203 204 See DAVResource.beginWrite() 205 """ 206 assert not self.is_collection 207 if self.provider.readonly: 208 raise DAVError(HTTP_FORBIDDEN) 209 210 try: 211 if seafile_api.check_permission_by_path(self.repo.id, self.rel_path, self.username) != "rw": 212 raise DAVError(HTTP_FORBIDDEN) 213 except SearpcError as e: 214 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 215 216 if not self.check_repo_owner_quota(isnewfile, contentlength): 217 raise DAVError(HTTP_FORBIDDEN, "The quota of the repo owner is exceeded") 218 219 fd, path = tempfile.mkstemp(dir=self.provider.tmpdir) 220 self.tmpfile_path = path 221 return os.fdopen(fd, "wb") 222 223 def end_write(self, with_errors, isnewfile=True): 224 try: 225 if not with_errors: 226 parent, filename = os.path.split(self.rel_path) 227 contentlength = os.stat(self.tmpfile_path).st_size 228 if not self.check_repo_owner_quota(isnewfile=isnewfile, contentlength=contentlength): 229 if self.tmpfile_path: 230 try: 231 os.unlink(self.tmpfile_path) 232 finally: 233 self.tmpfile_path = None 234 raise DAVError(HTTP_FORBIDDEN, "The quota of the repo owner is exceeded") 235 seafile_api.put_file(self.repo.id, self.tmpfile_path, parent, filename, 236 self.username, None) 237 except SearpcError as e: 238 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 239 finally: 240 if self.tmpfile_path: 241 try: 242 os.unlink(self.tmpfile_path) 243 finally: 244 self.tmpfile_path = None 245 246 def handle_delete(self): 247 if self.provider.readonly: 248 raise DAVError(HTTP_FORBIDDEN) 249 250 try: 251 if seafile_api.check_permission_by_path(self.repo.id, self.rel_path, self.username) != "rw": 252 raise DAVError(HTTP_FORBIDDEN) 253 254 file_id = seafile_api.get_file_id_by_path(self.repo.id, self.rel_path) 255 if file_id is None: 256 return True 257 258 parent, filename = os.path.split(self.rel_path) 259 seafile_api.del_file(self.repo.id, parent, filename, self.username) 260 except SearpcError as e: 261 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 262 263 return True 264 265 def handle_move(self, dest_path): 266 if self.provider.readonly: 267 raise DAVError(HTTP_FORBIDDEN) 268 269 parts = dest_path.strip("/").split("/", 1) 270 if len(parts) <= 1: 271 raise DAVError(HTTP_BAD_REQUEST) 272 repo_name = parts[0] 273 rel_path = parts[1] 274 275 dest_dir, dest_file = os.path.split(rel_path) 276 dest_repo = getRepoByName(repo_name, self.username, self.org_id, self.is_guest) 277 if dest_repo.id is None: 278 raise DAVError(HTTP_BAD_REQUEST) 279 280 try: 281 if seafile_api.check_permission_by_path(dest_repo.id, self.rel_path, self.username) != "rw": 282 raise DAVError(HTTP_FORBIDDEN) 283 284 src_dir, src_file = os.path.split(self.rel_path) 285 286 if not seafile_api.is_valid_filename(dest_repo.id, dest_file): 287 raise DAVError(HTTP_BAD_REQUEST) 288 289 # some clients such as GoodReader requires "overwrite" semantics 290 file_id_dest = seafile_api.get_file_id_by_path(dest_repo.id, rel_path) 291 if file_id_dest != None: 292 seafile_api.del_file(dest_repo.id, dest_dir, dest_file, self.username) 293 294 seafile_api.move_file(self.repo.id, src_dir, src_file, 295 dest_repo.id, dest_dir, dest_file, 1, self.username, NEED_PROGRESS, SYNCHRONOUS) 296 except SearpcError as e: 297 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 298 299 return True 300 301 def handle_copy(self, dest_path, depth_infinity): 302 if self.provider.readonly: 303 raise DAVError(HTTP_FORBIDDEN) 304 305 parts = dest_path.strip("/").split("/", 1) 306 if len(parts) <= 1: 307 raise DAVError(HTTP_BAD_REQUEST) 308 repo_name = parts[0] 309 rel_path = parts[1] 310 311 dest_dir, dest_file = os.path.split(rel_path) 312 dest_repo = getRepoByName(repo_name, self.username, self.org_id, self.is_guest) 313 if dest_repo.id is None: 314 raise DAVError(HTTP_BAD_REQUEST) 315 316 try: 317 if seafile_api.check_permission_by_path(dest_repo.id, self.rel_path, self.username) != "rw": 318 raise DAVError(HTTP_FORBIDDEN) 319 320 src_dir, src_file = os.path.split(self.rel_path) 321 if not src_file: 322 raise DAVError(HTTP_BAD_REQUEST) 323 324 if not seafile_api.is_valid_filename(dest_repo.id, dest_file): 325 raise DAVError(HTTP_BAD_REQUEST) 326 327 seafile_api.copy_file(self.repo.id, src_dir, src_file, 328 dest_repo.id, dest_dir, dest_file, self.username, NEED_PROGRESS, SYNCHRONOUS) 329 except SearpcError as e: 330 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 331 332 return True 333 334#=============================================================================== 335# SeafDirResource 336#=============================================================================== 337class SeafDirResource(DAVCollection): 338 def __init__(self, path, repo, rel_path, obj, environ): 339 super(SeafDirResource, self).__init__(path, environ) 340 self.repo = repo 341 self.rel_path = rel_path 342 self.obj = obj 343 self.username = environ.get("http_authenticator.username", "") 344 self.org_id = environ.get("seafile.org_id", "") 345 self.is_guest = environ.get("seafile.is_guest", False) 346 347 # Getter methods for standard live properties 348 def get_creation_date(self): 349# return int(time.time()) 350 return None 351 def get_display_name(self): 352 return self.name 353 def get_directory_info(self): 354 return None 355 def get_etag(self): 356 return self.obj.obj_id 357 def get_last_modified(self): 358# return int(time.time()) 359 return None 360 361 def get_member_names(self): 362 namelist = [] 363 for e in self.obj.dirs: 364 namelist.append(e[0]) 365 for e in self.obj.files: 366 namelist.append(e[0]) 367 return namelist 368 369 def get_member(self, name): 370 member_rel_path = "/".join([self.rel_path, name]) 371 member_path = "/".join([self.path, name]) 372 member = self.obj.lookup(name) 373 374 if not member: 375 raise DAVError(HTTP_NOT_FOUND) 376 377 if isinstance(member, SeafFile): 378 return SeafileResource(member_path, self.repo, member_rel_path, member, self.environ) 379 else: 380 return SeafDirResource(member_path, self.repo, member_rel_path, member, self.environ) 381 382 def get_member_list(self): 383 member_list = [] 384 d = self.obj 385 386 if d.version == 0: 387 file_mtimes = [] 388 try: 389 file_mtimes = seafile_api.get_files_last_modified(self.repo.id, self.rel_path, -1) 390 except SearpcError as e: 391 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 392 393 mtimes = {} 394 for entry in file_mtimes: 395 mtimes[entry.file_name] = entry.last_modified 396 for name, dent in d.dirents.items(): 397 member_path = posixpath.join(self.path, name) 398 member_rel_path = posixpath.join(self.rel_path, name) 399 400 if dent.is_dir(): 401 obj = fs_mgr.load_seafdir(d.store_id, d.version, dent.id) 402 res = SeafDirResource(member_path, self.repo, member_rel_path, obj, self.environ) 403 elif dent.is_file(): 404 obj = fs_mgr.load_seafile(d.store_id, d.version, dent.id) 405 res = SeafileResource(member_path, self.repo, member_rel_path, obj, self.environ) 406 else: 407 continue 408 409 if d.version == 1: 410 obj.last_modified = dent.mtime 411 else: 412 obj.last_modified = mtimes[name] 413 414 member_list.append(res) 415 416 return member_list 417 418 # --- Read / write --------------------------------------------------------- 419 def create_empty_resource(self, name): 420 """Create an empty (length-0) resource. 421 422 See DAVResource.createEmptyResource() 423 """ 424 assert not "/" in name 425 if self.provider.readonly: 426 raise DAVError(HTTP_FORBIDDEN) 427 428 try: 429 if seafile_api.check_permission_by_path(self.repo.id, self.rel_path, self.username) != "rw": 430 raise DAVError(HTTP_FORBIDDEN) 431 432 if seafile_api.check_quota(self.repo.id) < 0: 433 raise DAVError(HTTP_FORBIDDEN, "The quota of the repo owner is exceeded") 434 except SearpcError as e: 435 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 436 437 try: 438 seafile_api.post_empty_file(self.repo.id, self.rel_path, name, self.username) 439 except SearpcError as e: 440 if e.msg == 'Invalid file name': 441 raise DAVError(HTTP_BAD_REQUEST, e.msg) 442 if e.msg != 'file already exists': 443 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 444 445 try: 446 # Repo was updated, can't use self.repo 447 repo = seafile_api.get_repo(self.repo.id) 448 except SearpcError as e: 449 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 450 if not repo: 451 raise DAVError(HTTP_INTERNAL_ERROR) 452 453 member_rel_path = "/".join([self.rel_path, name]) 454 member_path = "/".join([self.path, name]) 455 obj = resolveRepoPath(repo, member_rel_path) 456 if not obj or not isinstance(obj, SeafFile): 457 raise DAVError(HTTP_INTERNAL_ERROR) 458 459 return SeafileResource(member_path, repo, member_rel_path, obj, self.environ) 460 461 def create_collection(self, name): 462 """Create a new collection as member of self. 463 464 See DAVResource.createCollection() 465 """ 466 assert not "/" in name 467 if self.provider.readonly: 468 raise DAVError(HTTP_FORBIDDEN) 469 470 try: 471 if seafile_api.check_permission_by_path(self.repo.id, self.rel_path, self.username) != "rw": 472 raise DAVError(HTTP_FORBIDDEN) 473 474 if not seafile_api.is_valid_filename(self.repo.id, name): 475 raise DAVError(HTTP_BAD_REQUEST) 476 477 seafile_api.post_dir(self.repo.id, self.rel_path, name, self.username) 478 except SearpcError as e: 479 if e.msg != 'file already exists': 480 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 481 482 def handle_delete(self): 483 if self.provider.readonly: 484 raise DAVError(HTTP_FORBIDDEN) 485 486 try: 487 if seafile_api.check_permission_by_path(self.repo.id, self.rel_path, self.username) != "rw": 488 raise DAVError(HTTP_FORBIDDEN) 489 490 parent, filename = os.path.split(self.rel_path) 491 # Can't delete repo root 492 if not filename: 493 raise DAVError(HTTP_BAD_REQUEST) 494 495 seafile_api.del_file(self.repo.id, parent, filename, self.username) 496 except SearpcError as e: 497 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 498 499 return True 500 501 def handle_move(self, dest_path): 502 if self.provider.readonly: 503 raise DAVError(HTTP_FORBIDDEN) 504 505 parts = dest_path.strip("/").split("/", 1) 506 if len(parts) <= 1: 507 raise DAVError(HTTP_BAD_REQUEST) 508 repo_name = parts[0] 509 rel_path = parts[1] 510 511 dest_dir, dest_file = os.path.split(rel_path) 512 dest_repo = getRepoByName(repo_name, self.username, self.org_id, self.is_guest) 513 514 if dest_repo.id is None or self.rel_path is None or self.username is None: 515 raise DAVError(HTTP_BAD_REQUEST) 516 517 try: 518 if seafile_api.check_permission_by_path(dest_repo.id, self.rel_path, self.username) != "rw": 519 raise DAVError(HTTP_FORBIDDEN) 520 521 src_dir, src_file = os.path.split(self.rel_path) 522 if not src_file: 523 raise DAVError(HTTP_BAD_REQUEST) 524 525 if not seafile_api.is_valid_filename(dest_repo.id, dest_file): 526 raise DAVError(HTTP_BAD_REQUEST) 527 528 seafile_api.move_file(self.repo.id, src_dir, src_file, 529 dest_repo.id, dest_dir, dest_file, 0, self.username, NEED_PROGRESS, SYNCHRONOUS) 530 except SearpcError as e: 531 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 532 533 return True 534 535 def handle_copy(self, dest_path, depth_infinity): 536 if self.provider.readonly: 537 raise DAVError(HTTP_FORBIDDEN) 538 539 parts = dest_path.strip("/").split("/", 1) 540 if len(parts) <= 1: 541 raise DAVError(HTTP_BAD_REQUEST) 542 repo_name = parts[0] 543 rel_path = parts[1] 544 545 dest_dir, dest_file = os.path.split(rel_path) 546 dest_repo = getRepoByName(repo_name, self.username, self.org_id, self.is_guest) 547 548 if dest_repo.id is None or self.rel_path is None or self.username is None: 549 raise DAVError(HTTP_BAD_REQUEST) 550 551 try: 552 if seafile_api.check_permission_by_path(dest_repo.id, self.rel_path, self.username) != "rw": 553 raise DAVError(HTTP_FORBIDDEN) 554 555 src_dir, src_file = os.path.split(self.rel_path) 556 if not src_file: 557 raise DAVError(HTTP_BAD_REQUEST) 558 559 if not seafile_api.is_valid_filename(dest_repo.id, dest_file): 560 raise DAVError(HTTP_BAD_REQUEST) 561 562 seafile_api.copy_file(self.repo.id, src_dir, src_file, 563 dest_repo.id, dest_dir, dest_file, self.username, NEED_PROGRESS, SYNCHRONOUS) 564 except SearpcError as e: 565 raise DAVError(HTTP_INTERNAL_ERROR, e.msg) 566 567 return True 568 569class RootResource(DAVCollection): 570 def __init__(self, username, environ, show_repo_id): 571 super(RootResource, self).__init__("/", environ) 572 self.username = username 573 self.show_repo_id = show_repo_id 574 self.org_id = environ.get('seafile.org_id', '') 575 self.is_guest = environ.get('seafile.is_guest', False) 576 577 # Getter methods for standard live properties 578 def get_creation_date(self): 579# return int(time.time()) 580 return None 581 def get_display_name(self): 582 return "" 583 def get_directory_info(self): 584 return None 585 def get_etag(self): 586 return None 587 def getLastModified(self): 588# return int(time.time()) 589 return None 590 591 def get_member_names(self): 592 all_repos = getAccessibleRepos(self.username, self.org_id, self.is_guest) 593 594 name_hash = {} 595 for r in all_repos: 596 r_list = name_hash[r.name] 597 if not r_list: 598 name_hash[r.name] = [r] 599 else: 600 r_list.append(r) 601 602 namelist = [] 603 for r_list in name_hash.values(): 604 if len(r_list) == 1: 605 repo = r_list[0] 606 namelist.append(repo.name) 607 else: 608 for repo in sort_repo_list(r_list): 609 unique_name = repo.name + "-" + repo.id[:6] 610 namelist.append(unique_name) 611 612 return namelist 613 614 def get_member(self, name): 615 repo = getRepoByName(name, self.username, self.org_id, self.is_guest) 616 return self._createRootRes(repo, name) 617 618 def get_member_list(self): 619 """ 620 Overwrite this method for better performance. 621 The default implementation call getMemberNames() then call getMember() 622 for each name. This calls getAccessibleRepos() for too many times. 623 """ 624 all_repos = getAccessibleRepos(self.username, self.org_id, self.is_guest) 625 626 name_hash = {} 627 for r in all_repos: 628 r_list = name_hash.get(r.name, []) 629 if not r_list: 630 name_hash[r.name] = [r] 631 else: 632 r_list.append(r) 633 634 member_list = [] 635 for r_list in name_hash.values(): 636 if len(r_list) == 1: 637 repo = r_list[0] 638 unique_name = repo.name 639 if self.show_repo_id: 640 unique_name = repo.name + "-" + repo.id[:6] 641 res = self._createRootRes(repo, unique_name) 642 member_list.append(res) 643 else: 644 for repo in sort_repo_list(r_list): 645 unique_name = repo.name + "-" + repo.id[:6] 646 res = self._createRootRes(repo, unique_name) 647 member_list.append(res) 648 649 return member_list 650 651 def _createRootRes(self, repo, name): 652 obj = get_repo_root_seafdir(repo) 653 return SeafDirResource("/"+name, repo, "", obj, self.environ) 654 655 # --- Read / write --------------------------------------------------------- 656 657 def create_empty_resource(self, name): 658 raise DAVError(HTTP_FORBIDDEN) 659 660 def create_collection(self, name): 661 raise DAVError(HTTP_FORBIDDEN) 662 663 def handle_delete(self): 664 raise DAVError(HTTP_FORBIDDEN) 665 666 def handle_move(self, dest_path): 667 raise DAVError(HTTP_FORBIDDEN) 668 669 def handle_copy(self, dest_path, depth_infinity): 670 raise DAVError(HTTP_FORBIDDEN) 671 672 673#=============================================================================== 674# SeafileProvider 675#=============================================================================== 676class SeafileProvider(DAVProvider): 677 678 def __init__(self, show_repo_id, readonly=False): 679 super(SeafileProvider, self).__init__() 680 self.readonly = readonly 681 self.show_repo_id = show_repo_id 682 self.tmpdir = os.path.join(SEAFILE_CONF_DIR, "webdavtmp") 683 self.block_map = {} 684 self.block_map_lock = Lock() 685 self.clean_block_map_task_started = False 686 if not os.access(self.tmpdir, os.F_OK): 687 os.mkdir(self.tmpdir) 688 689 def clean_block_map_per_hour(self): 690 delete_items = [] 691 with self.block_map_lock: 692 for obj_id, block in self.block_map.items(): 693 if time.time() - block.timestamp >= 3600*24: 694 delete_items.append(obj_id) 695 for i in range(len(delete_items)): 696 self.block_map.pop(delete_items[i]) 697 t = Timer(3600, self.clean_block_map_per_hour) 698 t.start() 699 700 def __repr__(self): 701 rw = "Read-Write" 702 if self.readonly: 703 rw = "Read-Only" 704 return "%s for Seafile (%s)" % (self.__class__.__name__, rw) 705 706 707 def get_resource_inst(self, path, environ): 708 """Return info dictionary for path. 709 710 See DAVProvider.getResourceInst() 711 """ 712 713 # start the scheduled task of cleaning up the block map here, 714 # because __init__ runs in a separate process. 715 if not self.clean_block_map_task_started: 716 self.clean_block_map_task_started = True 717 self.clean_block_map_per_hour() 718 719 self._count_get_resource_inst += 1 720 721 username = environ.get("http_authenticator.username", "") 722 org_id = environ.get("seafile.org_id", "") 723 is_guest = environ.get("seafile.is_guest", False) 724 725 if path == "/" or path == "": 726 return RootResource(username, environ, self.show_repo_id) 727 728 path = path.rstrip("/") 729 try: 730 repo, rel_path, obj = resolvePath(path, username, org_id, is_guest) 731 except DAVError as e: 732 if e.value == HTTP_NOT_FOUND: 733 return None 734 raise 735 736 if isinstance(obj, SeafDir): 737 return SeafDirResource(path, repo, rel_path, obj, environ) 738 return SeafileResource(path, repo, rel_path, obj, environ, self.block_map, self.block_map_lock) 739 740def resolvePath(path, username, org_id, is_guest): 741 segments = path.strip("/").split("/") 742 if len(segments) == 0: 743 raise DAVError(HTTP_BAD_REQUEST) 744 repo_name = segments.pop(0) 745 746 repo = getRepoByName(repo_name, username, org_id, is_guest) 747 748 rel_path = "" 749 obj = get_repo_root_seafdir(repo) 750 751 n_segs = len(segments) 752 i = 0 753 parent = None 754 for segment in segments: 755 parent = obj 756 obj = parent.lookup(segment) 757 758 if not obj or (isinstance(obj, SeafFile) and i != n_segs-1): 759 raise DAVError(HTTP_NOT_FOUND) 760 761 rel_path += "/" + segment 762 i += 1 763 764 if parent: 765 obj.mtime = parent.lookup_dent(segment).mtime 766 767 return (repo, rel_path, obj) 768 769def resolveRepoPath(repo, path): 770 segments = path.strip("/").split("/") 771 772 obj = get_repo_root_seafdir(repo) 773 774 n_segs = len(segments) 775 i = 0 776 for segment in segments: 777 obj = obj.lookup(segment) 778 779 if not obj or (isinstance(obj, SeafFile) and i != n_segs-1): 780 return None 781 782 i += 1 783 784 return obj 785 786def get_repo_root_seafdir(repo): 787 root_id = commit_mgr.get_commit_root_id(repo.id, repo.version, repo.head_cmmt_id) 788 return fs_mgr.load_seafdir(repo.store_id, repo.version, root_id) 789 790def getRepoByName(repo_name, username, org_id, is_guest): 791 repos = getAccessibleRepos(username, org_id, is_guest) 792 793 ret_repo = None 794 for repo in repos: 795 if repo.name == repo_name: 796 ret_repo = repo 797 break 798 799 if not ret_repo: 800 for repo in repos: 801 if repo.name + "-" + repo.id[:6] == repo_name: 802 ret_repo = repo 803 break 804 if not ret_repo: 805 raise DAVError(HTTP_NOT_FOUND) 806 807 return ret_repo 808 809def getAccessibleRepos(username, org_id, is_guest): 810 all_repos = {} 811 812 def addRepo(repo): 813 if all_repos.get(repo.repo_id): 814 return 815 if not repo.encrypted: 816 all_repos[repo.repo_id] = repo 817 818 try: 819 owned_repos = get_owned_repos(username, org_id) 820 except SearpcError as e: 821 util.warn("Failed to list owned repos: %s" % e.msg) 822 823 for orepo in owned_repos: 824 if orepo: 825 # store_id is used by seafobj to access fs object. 826 # repo's store_id is equal to repo_id except virtual_repo. 827 orepo.store_id = orepo.repo_id 828 addRepo(orepo) 829 830 try: 831 shared_repos = get_share_in_repo_list(username, org_id) 832 except SearpcError as e: 833 util.warn("Failed to list shared repos: %s" % e.msg) 834 835 for srepo in shared_repos: 836 if srepo: 837 addRepo(srepo) 838 pass 839 840 try: 841 repos = get_group_repos(username, org_id) 842 except SearpcError as e: 843 util.warn("Failed to get groups for %s" % username) 844 for grepo in repos: 845 if grepo: 846 addRepo(grepo) 847 848 for prepo in list_inner_pub_repos(username, org_id, is_guest): 849 if prepo: 850 addRepo(prepo) 851 852 return all_repos.values() 853 854def get_group_repos(username, org_id): 855 if org_id: 856 return seafile_api.get_org_group_repos_by_user(username, org_id) 857 else: 858 return seafile_api.get_group_repos_by_user(username) 859 860def get_owned_repos(username, org_id): 861 if org_id: 862 return seafile_api.get_org_owned_repo_list(org_id, username) 863 else: 864 return seafile_api.get_owned_repo_list(username) 865 866def get_share_in_repo_list(username, org_id): 867 """List share in repos. 868 """ 869 if org_id: 870 repo_list = seafile_api.get_org_share_in_repo_list(org_id, username, 871 -1, -1) 872 else: 873 repo_list = seafile_api.get_share_in_repo_list(username, -1, -1) 874 875 return repo_list 876 877def list_inner_pub_repos(username, org_id, is_guest): 878 if is_guest: 879 return [] 880 881 if org_id: 882 return seafile_api.list_org_inner_pub_repos(org_id) 883 884 return seafile_api.get_inner_pub_repo_list() 885