# -*- coding: utf-8 -*-
import os
import posixpath
from urllib.parse import unquote

from django.conf import settings
from django.core.files import File
from django.core.files.storage import DefaultStorage
from django.http import HttpResponseRedirect, Http404
from django.shortcuts import get_object_or_404
from django.views.generic import RedirectView

from grandchallenge.challenges.models import Challenge
from grandchallenge.serving.api import serve_file
from grandchallenge.serving.permissions import can_access


def sanitize_path(*, path: str):
    path = posixpath.normpath(unquote(path))
    path = path.lstrip("/")

    newpath = ""
    for part in path.split("/"):
        if not part:
            # Strip empty path components.
            continue

        drive, part = os.path.splitdrive(part)
        head, part = os.path.split(part)
        if part in (os.curdir, os.pardir):
            # Strip '.' and '..' in path.
            continue

        newpath = os.path.join(newpath, part).replace("\\", "/")

    return newpath


def serve_folder(request, *, challenge_short_name=None, folder=None, path):
    """
    Serve static files in a folder.

    If the file is in a challenge folder, then the subfolders of this challenge
    will be checked for permissions, see `can_access`.

    If the challenge_short_name is not set, then the folder must be set.
    ALL FILES IN THIS FOLDER WILL BE AVAILABLE TO DOWNLOAD.
    """
    newpath = sanitize_path(path=path)

    if path != newpath:
        return HttpResponseRedirect(newpath)

    if challenge_short_name:
        if folder:
            raise AttributeError(
                "Only challenge_short_name or folder should be set"
            )
        challenge = get_object_or_404(
            Challenge, short_name__iexact=challenge_short_name
        )
        fullpath = os.path.join(
            settings.MEDIA_ROOT, challenge.short_name, newpath
        )
        allowed = can_access(request.user, newpath, challenge=challenge)
    elif folder:
        fullpath = os.path.join(settings.MEDIA_ROOT, folder, newpath)
        allowed = True
    else:
        raise AttributeError("challenge_short_name or folder must be set")

    storage = DefaultStorage()

    if storage.exists(fullpath) and allowed:
        try:
            f = storage.open(fullpath, "rb")
            file = File(f)
            return serve_file(file, save_as=True)
        except IOError:
            pass

    raise Http404("File not found.")


class ChallengeServeRedirect(RedirectView):
    # Do not redirect to a view name as this could skip some other handlers

    def get_redirect_url(self, *args, **kwargs):
        return f"/media/{kwargs['challenge_short_name']}/{kwargs['path']}/"

@@ -28,60 +28,17 @@
 ort 
-os%0Aimport posixpath%0Afrom urllib.parse import unquote
+posixpath
 %0A%0Afr
@@ -183,30 +183,8 @@
 port
- HttpResponseRedirect,
  Htt
@@ -231,24 +231,63 @@
 ject_or_404%0A
+from django.utils._os import safe_join%0A
 from django.
@@ -490,533 +490,8 @@
 s%0A%0A%0A
-def sanitize_path(*, path: str):%0A    path = posixpath.normpath(unquote(path))%0A    path = path.lstrip(%22/%22)%0A%0A    newpath = %22%22%0A    for part in path.split(%22/%22):%0A        if not part:%0A            # Strip empty path components.%0A            continue%0A%0A        drive, part = os.path.splitdrive(part)%0A        head, part = os.path.split(part)%0A        if part in (os.curdir, os.pardir):%0A            # Strip '.' and '..' in path.%0A            continue%0A%0A        newpath = os.path.join(newpath, part).replace(%22%5C%5C%22, %22/%22)%0A%0A    return newpath%0A%0A%0A
 def 
@@ -892,111 +892,50 @@
     
-new
 path = 
-sanitize_path(
+posixpath.norm
 path
-=
+(
 path)
-%0A%0A    if path != newpath:%0A        return HttpResponseRedirect(newpath
+.lstrip(%22/%22
 )%0A%0A 
@@ -1221,45 +1221,34 @@
     
-fullpath = os.path.join(%0A            
+document_root = safe_join(
 sett
@@ -1288,295 +1288,453 @@
 name
-, newpath%0A        )%0A        allowed = can_access(request.user, newpath, challenge=challenge)%0A    elif folder:%0A        fullpath = os.path.join(settings.MEDIA_ROOT, folder, newpath)%0A        allowed = True%0A    else:%0A        raise AttributeError(%22challenge_short_name or folder must be set%22)
+)%0A    elif folder:%0A        challenge = None%0A        document_root = safe_join(settings.MEDIA_ROOT, folder)%0A    else:%0A        raise AttributeError(%22challenge_short_name or folder must be set%22)%0A%0A    fullpath = safe_join(document_root, path)%0A%0A    if challenge:%0A        allowed = can_access(%0A            request.user,%0A            fullpath%5Blen(document_root) :%5D.lstrip(%22/%22),%0A            challenge=challenge,%0A        )%0A    else:%0A        allowed = True
 %0A%0A  

Fix redirects