diff --git a/paperless.conf.example b/paperless.conf.example index 27f826462..424e046fb 100755 --- a/paperless.conf.example +++ b/paperless.conf.example @@ -204,6 +204,3 @@ PAPERLESS_EMAIL_SECRET="" # 100 will be used. #PAPERLESS_LIST_PER_PAGE=100 - -# Enable WebDAV support for Paperless. Default is false. -#PAPERLESS_ENABLE_WEBDAV="true" diff --git a/requirements.txt b/requirements.txt index d53da13d7..c6cb6fd47 100755 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,6 @@ django-extensions==2.0.7 django-filter==1.1.0 django-flat-responsive==2.0 django==2.0.7 -djangodav==0.0.1b26 djangorestframework==3.8.2 docopt==0.6.2 execnet==1.5.0 diff --git a/src/paperless/dav.py b/src/paperless/dav.py deleted file mode 100755 index 3a082a75c..000000000 --- a/src/paperless/dav.py +++ /dev/null @@ -1,275 +0,0 @@ -import base64 -import binascii -import os -from functools import wraps -from urllib.parse import unquote_plus - -from django.http import HttpResponse -from django.utils.decorators import method_decorator -from django.contrib.auth import authenticate - -from djangodav.base.resources import MetaEtagMixIn, BaseDavResource -from djangodav.utils import url_join -from djangodav.views import DavView - -from documents.models import Tag, Document, Correspondent - - -def extract_basicauth(authorization_header, encoding='utf-8'): - splitted = authorization_header.split(' ') - if len(splitted) != 2: - return None - - auth_type, auth_string = splitted - - if 'basic' != auth_type.lower(): - return None - - try: - b64_decoded = base64.b64decode(auth_string) - except (TypeError, binascii.Error): - return None - try: - auth_string_decoded = b64_decoded.decode(encoding) - except UnicodeDecodeError: - return None - - splitted = auth_string_decoded.split(':') - - if len(splitted) != 2: - return None - - username, password = map(unquote_plus, splitted) - return username, password - - -def validate_request(request): - - if 'HTTP_AUTHORIZATION' not in request.META: - return False - - authorization_header = request.META['HTTP_AUTHORIZATION'] - ret = extract_basicauth(authorization_header) - if not ret: - return False - - username, password = ret - - user = authenticate(username=username, password=password) - - if user is None: - return False - - request.META['REMOTE_USER'] = username - return True - - -class HttpResponseUnauthorized(HttpResponse): - status_code = 401 - - def __init__(self): - super(HttpResponseUnauthorized, self).__init__( - """Basic auth required -

Authorization Required

""", - ) - realm = 'Paperless WebDAV' - self['WWW-Authenticate'] = 'Basic realm="{}"'.format(realm) - - -def basic_auth_required(func=None, - target_test=(lambda request: True)): - def actual_decorator(view_func): - @wraps(view_func) - def _wrapped(request, *args, **kwargs): - if target_test(request) and not validate_request(request): - return HttpResponseUnauthorized() - return view_func(request, *args, **kwargs) - return _wrapped - - if func: - return actual_decorator(func) - else: - return actual_decorator - -#@method_decorator(basic_auth_required, name='dispatch') -class SecuredDavView(DavView): - pass - -class PaperlessDavResource(MetaEtagMixIn, BaseDavResource): - - document = None - _exists = True - - def __init__(self, path, **kwargs): - super(PaperlessDavResource, self).__init__(path) - if 'document' in kwargs: - # this greatly reduces the amount of database requests. - self.document = kwargs.pop('document') - else: - self._exists, self.documents, self.document, self.children = parse_path(path) - - @property - def getcontentlength(self): - if self.document: - return os.path.getsize(self.document.source_path) - else: - return None - - def get_created(self): - """Return the create time as datetime object.""" - if self.document: - return self.document.created - else: - return None - - def get_modified(self): - if self.document: - return self.document.modified - else: - return None - - @property - def is_collection(self): - return self.exists and not self.document - - @property - def is_object(self): - return self.exists and self.document - - @property - def exists(self): - return self._exists - - def get_children(self): - if not self.document: - for child in self.children: - yield self.clone(url_join(*(self.path + [child]))) - - for doc in self.documents: - yield self.clone(url_join(*(self.path + [doc.file_name])), document=doc) - - def write(self, content, temp_file=None): - raise NotImplementedError() - - def read(self): - return self.document.source_file - - def delete(self): - raise NotImplementedError() - - def create_collection(self): - raise NotImplementedError() - - def copy_object(self, destination, depth=0): - raise NotImplementedError() - - def move_object(self, destination): - raise NotImplementedError() - -def parse_path(path): - """ - This method serves multiple purposes: - 1. validate the path and ensure that it valid (i.e., conforms to the specification provided above). - 2. provide a database filter that returns a set of documents to be displayed, applying filters if necessary. - 3. provide a set of "folders" that act as filters to narrow down the list of documents. - - This is achieved by implementing a state machine. This machine processes the path segment by segment and switches - states as the path is processed. Depending on the state, only certain path segments are allowed - :param path: - :return: - """ - used_tags = [] - correspondent_selected = False - year_selected = False - month_selected = False - day_selected = False - show_documents = False - - def get_filter_children(is_root=False): - filters = [] - if is_root: - filters.append("show_all_documents") - if not year_selected: - filters.append('year') - elif not month_selected: - filters.append('month') - elif not day_selected: - filters.append('day') - if not correspondent_selected: - filters.append('correspondent') - #TODO: this should probably not get displayed if the resulting list of tags is empty, but it would result in even more database queries. - filters.append('tag') - return filters - - path_queue = [x for x in path.split('/') if x] - - filter = Document.objects.all() - children = get_filter_children(True) - document = None - exists = True - - current_state = 'select_filter' - - while len(path_queue) > 0: - path_segment = path_queue.pop(0) - show_documents = False - children = [] - next_state = '' - - if current_state == 'select_filter' and path_segment == 'year': - next_state = 'select_year' - children = [str(d.year) for d in filter.dates('created', 'year')] - elif current_state == 'select_filter' and path_segment == 'month': - next_state = 'select_month' - children = [str(d.month) for d in filter.dates('created', 'month')] - elif current_state == 'select_filter' and path_segment == 'day': - next_state = 'select_day' - children = [str(d.day) for d in filter.dates('created', 'day')] - elif current_state == 'select_filter' and path_segment == 'correspondent': - next_state = 'select_correspondent' - children = [c.name for c in Correspondent.objects.filter(documents__in=filter).distinct()] - elif current_state == 'select_filter' and path_segment == 'tag': - next_state = 'select_tag' - children = [t.name for t in Tag.objects.filter(documents__in=filter).distinct() if t.name not in used_tags] - elif current_state == 'select_filter' and path_segment == 'show_all_documents': - show_documents = True - elif current_state == 'select_tag': - next_state = 'select_filter' - filter = filter.filter(tags__name=path_segment) - used_tags.append(path_segment) - children = get_filter_children() - show_documents = True - elif current_state == 'select_correspondent': - next_state = 'select_filter' - filter = filter.filter(correspondent__name=path_segment) - correspondent_selected = True - children = get_filter_children() - show_documents = True - elif current_state == 'select_year': - next_state = 'select_filter' - filter = filter.filter(created__year=path_segment) - year_selected = True - children = get_filter_children() - show_documents = True - elif current_state == 'select_month': - next_state = 'select_filter' - filter = filter.filter(created__month=path_segment) - month_selected = True - children = get_filter_children() - show_documents = True - elif current_state == 'select_day': - next_state = 'select_filter' - filter = filter.filter(created__day=path_segment) - day_selected = True - children = get_filter_children() - show_documents = True - else: - try: - #TODO: this is pretty slow and sketchy. - document = [d for d in Document.objects.all() if d.file_name == path_segment][0] - except IndexError: - exists = False - - current_state = next_state - - return exists, filter if show_documents else [], document, children \ No newline at end of file diff --git a/src/paperless/settings.py b/src/paperless/settings.py index d18af3566..b52cf1ca8 100755 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -72,9 +72,7 @@ "rest_framework", "crispy_forms", - "django_filters", - - "djangodav" + "django_filters" ] @@ -280,5 +278,3 @@ # Specify the default date order (for autodetected dates) DATE_ORDER = os.getenv("PAPERLESS_DATE_ORDER", "DMY") - -ENABLE_WEBDAV = bool(os.getenv("PAPERLESS_ENABLE_WEBDAV", "NO").lower() in ("yes", "y", "1", "t", "true")) diff --git a/src/paperless/urls.py b/src/paperless/urls.py index 83c9dfa37..6f50ac644 100755 --- a/src/paperless/urls.py +++ b/src/paperless/urls.py @@ -6,9 +6,6 @@ from django.views.generic import RedirectView from rest_framework.routers import DefaultRouter -from djangodav.acls import FullAcl -from djangodav.locks import DummyLock - from documents.views import ( CorrespondentViewSet, DocumentViewSet, @@ -17,7 +14,6 @@ PushView, TagViewSet ) -from paperless.dav import PaperlessDavResource, SecuredDavView from reminders.views import ReminderViewSet router = DefaultRouter() @@ -56,9 +52,6 @@ ] + static.static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) -if settings.ENABLE_WEBDAV: - urlpatterns.append(url(r'^dav(?P.*)$', SecuredDavView.as_view(resource_class=PaperlessDavResource, lock_class=DummyLock, acl_class=FullAcl))) - # Text in each page's

(and above login form). admin.site.site_header = 'Paperless' # Text at the end of each page's .