Ejemplo n.º 1
0
    def add_provider(self, share, provider, readonly=False):
        """Add a provider to the providerMap."""
        # Make sure share starts with, or is '/'
        share = "/" + share.strip("/")
        assert share not in self.providerMap
        # We allow a simple string as 'provider'. In this case we interpret
        # it as a file system root folder that is published.
        if compat.is_basestring(provider):
            provider = FilesystemProvider(provider, readonly)
        elif type(provider) in (list, tuple):
            provider = FilesystemProvider(provider[0], provider[1])

        if not isinstance(provider, DAVProvider):
            raise ValueError("Invalid provider {}".format(provider))

        provider.set_share_path(share)
        if self.mount_path:
            provider.set_mount_path(self.mount_path)

        # TODO: someday we may want to configure different lock/prop
        # managers per provider
        provider.set_lock_manager(self.locksManager)
        provider.set_prop_manager(self.propsManager)

        self.providerMap[share] = {"provider": provider, "allow_anonymous": False}

        # Store the list of share paths, ordered by length, so route lookups
        # will return the most specific match
        self.sortedShareList = [s.lower() for s in self.providerMap.keys()]
        self.sortedShareList = sorted(self.sortedShareList, key=len, reverse=True)

        return provider
Ejemplo n.º 2
0
 def __init__(self, path, environ, display_info, member_name_list):
     super(VirtualCollection, self).__init__(path, environ)
     if compat.is_basestring(display_info):
         display_info = {"type": display_info}
     assert type(display_info) is dict
     assert type(member_name_list) is list
     self.display_info = display_info
     self.member_name_list = member_name_list
Ejemplo n.º 3
0
 def __init__(self, path, environ, displayInfo, memberNameList):
     DAVCollection.__init__(self, path, environ)
     if compat.is_basestring(displayInfo):
         displayInfo = {"type": displayInfo}
     assert type(displayInfo) is dict
     assert type(memberNameList) is list
     self.displayInfo = displayInfo
     self.memberNameList = memberNameList
Ejemplo n.º 4
0
 def __init__(self, path, environ, display_info, member_name_list):
     super(VirtualCollection, self).__init__(path, environ)
     if compat.is_basestring(display_info):
         display_info = {"type": display_info}
     assert type(display_info) is dict
     assert type(member_name_list) is list
     self.display_info = display_info
     self.member_name_list = member_name_list
Ejemplo n.º 5
0
    def add_provider(self, share, provider, readonly=False):
        """Add a provider to the provider_map routing table."""
        # Make sure share starts with, or is '/'
        share = "/" + share.strip("/")
        assert share not in self.provider_map

        if compat.is_basestring(provider):
            # Syntax:
            #   <mount_path>: <folder_path>
            # We allow a simple string as 'provider'. In this case we interpret
            # it as a file system root folder that is published.
            provider = FilesystemProvider(provider, readonly)
        elif type(provider) in (dict, ):
            if "provider" in provider:
                # Syntax:
                #   <mount_path>: {"provider": <class_path>, "args": <pos_args>, "kwargs": <named_args}
                prov_class = dynamic_import_class(provider["provider"])
                provider = prov_class(*provider.get("args", []),
                                      **provider.get("kwargs", {}))
            else:
                # Syntax:
                #   <mount_path>: {"root": <path>, "redaonly": <bool>}
                provider = FilesystemProvider(
                    provider["root"], bool(provider.get("readonly", False)))
        elif type(provider) in (list, tuple):
            raise ValueError(
                "Provider {}: tuple/list syntax is no longer supported".format(
                    provider))
            # provider = FilesystemProvider(provider[0], provider[1])

        if not isinstance(provider, DAVProvider):
            raise ValueError("Invalid provider {}".format(provider))

        provider.set_share_path(share)
        if self.mount_path:
            provider.set_mount_path(self.mount_path)

        # TODO: someday we may want to configure different lock/prop
        # managers per provider
        provider.set_lock_manager(self.lock_manager)
        provider.set_prop_manager(self.prop_manager)

        self.provider_map[share] = provider
        # self.provider_map[share] = {"provider": provider, "allow_anonymous": False}

        # Store the list of share paths, ordered by length, so route lookups
        # will return the most specific match
        self.sorted_share_list = [s.lower() for s in self.provider_map.keys()]
        self.sorted_share_list = sorted(self.sorted_share_list,
                                        key=len,
                                        reverse=True)

        return provider
Ejemplo n.º 6
0
    def add_provider(self, share, provider, readonly=False):
        """Add a provider to the provider_map routing table."""
        # Make sure share starts with, or is '/'
        share = "/" + share.strip("/")
        assert share not in self.provider_map

        if compat.is_basestring(provider):
            # Syntax:
            #   <mount_path>: <folder_path>
            # We allow a simple string as 'provider'. In this case we interpret
            # it as a file system root folder that is published.
            provider = FilesystemProvider(provider, readonly)
        elif type(provider) in (dict,):
            if "provider" in provider:
                # Syntax:
                #   <mount_path>: {"provider": <class_path>, "args": <pos_args>, "kwargs": <named_args}
                prov_class = dynamic_import_class(provider["provider"])
                provider = prov_class(
                    *provider.get("args", []), **provider.get("kwargs", {})
                )
            else:
                # Syntax:
                #   <mount_path>: {"root": <path>, "redaonly": <bool>}
                provider = FilesystemProvider(
                    provider["root"], bool(provider.get("readonly", False))
                )
        elif type(provider) in (list, tuple):
            raise ValueError(
                "Provider {}: tuple/list syntax is no longer supported".format(provider)
            )
            # provider = FilesystemProvider(provider[0], provider[1])

        if not isinstance(provider, DAVProvider):
            raise ValueError("Invalid provider {}".format(provider))

        provider.set_share_path(share)
        if self.mount_path:
            provider.set_mount_path(self.mount_path)

        # TODO: someday we may want to configure different lock/prop
        # managers per provider
        provider.set_lock_manager(self.lock_manager)
        provider.set_prop_manager(self.prop_manager)

        self.provider_map[share] = provider
        # self.provider_map[share] = {"provider": provider, "allow_anonymous": False}

        # Store the list of share paths, ordered by length, so route lookups
        # will return the most specific match
        self.sorted_share_list = [s.lower() for s in self.provider_map.keys()]
        self.sorted_share_list = sorted(self.sorted_share_list, key=len, reverse=True)

        return provider
Ejemplo n.º 7
0
    def write(self, chunk):
        """Put a chunk of bytes (or an iterable) to the queue.

        May block if max_size number of chunks is reached.
        """
        if self.is_closed:
            raise ValueError("Cannot write to closed object")
        # print("FileLikeQueue.write(), n={}".format(len(chunk)))
        # Add chunk to queue (blocks if queue is full)
        if compat.is_basestring(chunk):
            self.queue.put(chunk)
        else:  # if not a string, assume an iterable
            for o in chunk:
                self.queue.put(o)
Ejemplo n.º 8
0
    def write(self, chunk):
        """Put a chunk of bytes (or an iterable) to the queue.

        May block if max_size number of chunks is reached.
        """
        if self.is_closed:
            raise ValueError("Cannot write to closed object")
        # print("FileLikeQueue.write(), n={}".format(len(chunk)))
        # Add chunk to queue (blocks if queue is full)
        if compat.is_basestring(chunk):
            self.queue.put(chunk)
        else:  # if not a string, assume an iterable
            for o in chunk:
                self.queue.put(o)
Ejemplo n.º 9
0
def make_domain_controller(config):
    dc = config.get("http_authenticator", {}).get("domain_controller")
    if dc is True or not dc:
        # True or null:
        dc = SimpleDomainController

    if compat.is_basestring(dc):
        # If a plain string is passed, try to import it as class
        dc = dynamic_import_class(dc)

    if inspect.isclass(dc):
        # If a class is passed, instantiate that
        dc = dc(config)

    # print("make_domain_controller", dc)
    return dc
Ejemplo n.º 10
0
def make_domain_controller(wsgidav_app, config):
    dc = config.get("http_authenticator", {}).get("domain_controller")
    org_dc = dc
    if dc is True or not dc:
        # True or null:
        dc = SimpleDomainController
    elif compat.is_basestring(dc):
        # If a plain string is passed, try to import it as class
        dc = dynamic_import_class(dc)

    if inspect.isclass(dc):
        # If a class is passed, instantiate that
        dc = dc(wsgidav_app, config)
    else:
        raise RuntimeError(
            "Could not resolve domain controller class (got {})".format(org_dc)
        )
    # print("make_domain_controller", dc)
    return dc
Ejemplo n.º 11
0
def make_domain_controller(wsgidav_app, config):
    dc = config.get("http_authenticator", {}).get("domain_controller")
    org_dc = dc
    if dc is True or not dc:
        # True or null:
        dc = SimpleDomainController
    elif compat.is_basestring(dc):
        # If a plain string is passed, try to import it as class
        dc = dynamic_import_class(dc)

    if inspect.isclass(dc):
        # If a class is passed, instantiate that
        dc = dc(wsgidav_app, config)
    else:
        raise RuntimeError(
            "Could not resolve domain controller class (got {})".format(org_dc)
        )
    # print("make_domain_controller", dc)
    return dc
Ejemplo n.º 12
0
 def _expand(v):
     """Replace some string templates with defined values."""
     if expand and compat.is_basestring(v) and v.lower() in expand:
         return expand[v]
     return v
Ejemplo n.º 13
0
    def __init__(self, config):
        self.config = config

        util.initLogging(config["verbose"], config.get("enable_loggers", []))

        util.log("Default encoding: %s (file system: %s)" %
                 (sys.getdefaultencoding(), sys.getfilesystemencoding()))

        # Evaluate configuration and set defaults
        _checkConfig(config)
        provider_mapping = self.config["provider_mapping"]
#        response_trailer = config.get("response_trailer", "")
        self._verbose = config.get("verbose", 2)

        lockStorage = config.get("locksmanager")
        if lockStorage is True:
            lockStorage = LockStorageDict()

        if not lockStorage:
            locksManager = None
        else:
            locksManager = LockManager(lockStorage)

        propsManager = config.get("propsmanager")
        if not propsManager:
            # Normalize False, 0 to None
            propsManager = None
        elif propsManager is True:
            propsManager = PropertyManager()

        mount_path = config.get("mount_path")

        # Instantiate DAV resource provider objects for every share
        self.providerMap = {}
        for (share, provider) in provider_mapping.items():
            # Make sure share starts with, or is, '/'
            share = "/" + share.strip("/")

            # We allow a simple string as 'provider'. In this case we interpret
            # it as a file system root folder that is published.
            if compat.is_basestring(provider):
                provider = FilesystemProvider(provider)

            assert isinstance(provider, DAVProvider)

            provider.setSharePath(share)
            if mount_path:
                provider.setMountPath(mount_path)

            # TODO: someday we may want to configure different lock/prop
            # managers per provider
            provider.setLockManager(locksManager)
            provider.setPropManager(propsManager)

            self.providerMap[share] = {
                "provider": provider, "allow_anonymous": False}

        # Define WSGI application stack
        application = RequestResolver()

        domain_controller = None
        dir_browser = config.get("dir_browser", {})
        middleware_stack = config.get("middleware_stack", [])

        # Replace WsgiDavDirBrowser to custom class for backward compatibility only
        # In normal way you should insert it into middleware_stack
        if dir_browser.get("enable", True) and "app_class" in dir_browser.keys():
            config["middleware_stack"] = [m if m != WsgiDavDirBrowser else dir_browser[
                'app_class'] for m in middleware_stack]

        for mw in middleware_stack:
            if mw.isSuitable(config):
                if self._verbose >= 2:
                    print("Middleware %s is suitable" % mw)
                application = mw(application, config)

                if issubclass(mw, HTTPAuthenticator):
                    domain_controller = application.getDomainController()
                    # check anonymous access
                    for share, data in self.providerMap.items():
                        if application.allowAnonymousAccess(share):
                            data['allow_anonymous'] = True
            else:
                if self._verbose >= 2:
                    print("Middleware %s is not suitable" % mw)

        # Print info
        if self._verbose >= 2:
            print("Using lock manager: %r" % locksManager)
            print("Using property manager: %r" % propsManager)
            print("Using domain controller: %s" % domain_controller)
            print("Registered DAV providers:")
            for share, data in self.providerMap.items():
                hint = " (anonymous)" if data['allow_anonymous'] else ""
                print("  Share '%s': %s%s" % (share, provider, hint))
        if self._verbose >= 1:
            for share, data in self.providerMap.items():
                if data['allow_anonymous']:
                    # TODO: we should only warn here, if --no-auth is not given
                    print("WARNING: share '%s' will allow anonymous access." % share)

        self._application = application
Ejemplo n.º 14
0
    def _get_context(self, environ, dav_res):
        """
        @see: http://www.webdav.org/specs/rfc4918.html#rfc.section.9.4
        """
        assert dav_res.is_collection

        is_readonly = environ["wsgidav.provider"].is_readonly()

        context = {
            "htdocs": (self.config.get("mount_path") or "") + ASSET_SHARE,
            "rows": [],
            "version": __version__,
            "display_path": compat.unquote(dav_res.get_href()),
            "url": dav_res.get_href(),  # util.make_complete_url(environ),
            "parent_url": util.get_uri_parent(dav_res.get_href()),
            "config": self.dir_config,
            "is_readonly": is_readonly,
            "access": "read-only" if is_readonly else "read-write",
            "is_authenticated": False,
        }

        trailer = self.dir_config.get("response_trailer")
        if trailer is True:
            #trailer = "${version} - ${time}"
            trailer = "Shihira Fung - ${time}"

        if trailer:
            trailer = trailer.replace(
                "${version}",
                "<a href='https://github.com/mar10/wsgidav/'>WsgiDAV/{}</a>".format(
                    __version__
                ),
            )
            trailer = trailer.replace("${time}", util.get_rfc1123_time())

        context["trailer"] = trailer

        rows = context["rows"]

        # Ask collection for member info list
        dirInfoList = dav_res.get_directory_info()

        if dirInfoList is None:
            # No pre-build info: traverse members
            dirInfoList = []
            childList = dav_res.get_descendants(depth="1", add_self=False)
            for res in childList:
                di = res.get_display_info()
                href = res.get_href()
                ofe_prefix = None
                tr_classes = []
                a_classes = []
                if res.is_collection:
                    tr_classes.append("directory")

                if not is_readonly and not res.is_collection:
                    ext = os.path.splitext(href)[1].lstrip(".").lower()
                    officeType = msOfficeExtToTypeMap.get(ext)
                    if officeType:
                        if self.dir_config.get("ms_sharepoint_support"):
                            ofe_prefix = "ms-{}:ofe|u|".format(officeType)
                            a_classes.append("msoffice")
                        # elif self.dir_config.get("ms_sharepoint_plugin"):
                        #     a_classes.append("msoffice")
                        # elif self.dir_config.get("ms_sharepoint_urls"):
                        #     href = "ms-{}:ofe|u|{}".format(officeType, href)

                entry = {
                    "href": href,
                    "ofe_prefix": ofe_prefix,
                    "a_class": " ".join(a_classes),
                    "tr_class": " ".join(tr_classes),
                    "display_name": res.get_display_name(),
                    "last_modified": res.get_last_modified(),
                    "is_collection": res.is_collection,
                    "content_length": res.get_content_length(),
                    "display_type": di.get("type"),
                    "display_type_comment": di.get("typeComment"),
                }

                dirInfoList.append(entry)
        #
        ignore_patterns = self.dir_config.get("ignore", [])
        if compat.is_basestring(ignore_patterns):
            ignore_patterns = ignore_patterns.split(",")

        ignored_list = []
        for entry in dirInfoList:
            # Skip ignore patterns
            ignore = False
            for pat in ignore_patterns:
                if fnmatch(entry["display_name"], pat):
                    ignored_list.append(entry["display_name"])
                    # _logger.debug("Ignore {}".format(entry["display_name"]))
                    ignore = True
                    break
            if ignore:
                continue
            #
            last_modified = entry.get("last_modified")
            if last_modified is None:
                entry["str_modified"] = ""
            else:
                import time
                entry["str_modified"] = time.strftime("%b %d %Y, %H:%M:%S", time.localtime(last_modified))

            entry["str_size"] = "-"
            if not entry.get("is_collection"):
                content_length = entry.get("content_length")
                if content_length is not None:
                    for unit in ['Bytes', 'KiB', 'MiB', 'GiB']:
                        if content_length < 1024:
                            content_length = "%.2f %s" % (content_length, unit)
                            break
                        content_length /= 1024.0
                    entry["str_size"] = content_length

            rows.append(entry)
        if ignored_list:
            _logger.debug(
                "Dir browser ignored {} entries: {}".format(
                    len(ignored_list), ignored_list
                )
            )

        # sort
        sort = "name"
        if sort == "name":
            rows.sort(
                key=lambda v: "{}{}".format(
                    not v["is_collection"], v["display_name"].lower()
                )
            )

        if "wsgidav.auth.user_name" in environ:
            context.update(
                {
                    "is_authenticated": True,
                    "user_name": (environ.get("wsgidav.auth.user_name") or "anonymous"),
                    "realm": environ.get("wsgidav.auth.realm"),
                    "user_roles": ", ".join(environ.get("wsgidav.auth.roles") or []),
                    "user_permissions": ", ".join(
                        environ.get("wsgidav.auth.permissions") or []
                    ),
                }
            )

        return context
Ejemplo n.º 15
0
    def __init__(self, config):

        self.config = copy.deepcopy(DEFAULT_CONFIG)
        util.deep_update(self.config, config)
        config = self.config

        # Evaluate configuration and set defaults
        _check_config(config)

        self.verbose = config.get("verbose", 3)

        hotfixes = config.get("hotfixes", {})

        self.re_encode_path_info = hotfixes.get("re_encode_path_info", None)
        if self.re_encode_path_info is None:
            self.re_encode_path_info = compat.PY3

        self.unquote_path_info = hotfixes.get("unquote_path_info", False)

        lock_storage = config.get("lock_manager")
        if lock_storage is True:
            lock_storage = LockStorageDict()

        if not lock_storage:
            self.lock_manager = None
        else:
            self.lock_manager = LockManager(lock_storage)

        self.prop_manager = config.get("property_manager")
        if not self.prop_manager:
            # Normalize False, 0 to None
            self.prop_manager = None
        elif self.prop_manager is True:
            self.prop_manager = PropertyManager()

        self.mount_path = config.get("mount_path")
        auth_conf = config.get("http_authenticator", {})

        # Instantiate DAV resource provider objects for every share.
        # provider_mapping may contain the args that are passed to a
        # `FilesystemProvider` instance:
        #     <mount_path>: <folder_path>
        # or
        #     <mount_path>: { "root": <folder_path>, "readonly": True }
        # or contain a complete new instance:
        #     <mount_path>: <DAVProvider Instance>

        provider_mapping = self.config["provider_mapping"]

        self.provider_map = {}
        self.sorted_share_list = None
        for share, provider in provider_mapping.items():
            self.add_provider(share, provider)

        self.http_authenticator = None
        domain_controller = None

        # Define WSGI application stack
        middleware_stack = config.get("middleware_stack", [])
        mw_list = []

        # This is the 'outer' application, i.e. the WSGI application object that
        # is eventually called by the server.
        self.application = self

        # The `middleware_stack` is configured such that the first app in the
        # list should be called first. Since every app wraps its predecessor, we
        # iterate in reverse order:
        for mw in reversed(middleware_stack):
            # The middleware stack configuration may contain plain strings, dicts,
            # classes, or objects
            app = None
            if compat.is_basestring(mw):
                # If a plain string is passed, try to import it, assuming
                # `BaseMiddleware` signature
                app_class = dynamic_import_class(mw)
                app = app_class(self, self.application, config)
            elif type(mw) is dict:
                # If a dict with one entry is passed, use the key as module/class name
                # and the value as constructor arguments (positional or kwargs).
                assert len(mw) == 1
                name, args = list(mw.items())[0]
                expand = {"${application}": self.application}
                app = dynamic_instantiate_middleware(name, args, expand)
            elif inspect.isclass(mw):
                # If a class is passed, assume BaseMiddleware (or compatible)
                assert issubclass(
                    mw, BaseMiddleware
                )  # TODO: remove this assert with 3.0
                app = mw(self, self.application, config)
            else:
                # Otherwise assume an initialized middleware instance
                app = mw

            # Remember
            if isinstance(app, HTTPAuthenticator):
                self.http_authenticator = app
                domain_controller = app.get_domain_controller()

            # Add middleware to the stack
            if app:
                if callable(getattr(app, "is_disabled", None)) and app.is_disabled():
                    _logger.warning(
                        "App {}.is_disabled() returned True: skipping.".format(app)
                    )
                else:
                    mw_list.append(app)
                    self.application = app
            else:
                _logger.error("Could not add middleware {}.".format(mw))

        domain_controller
        # Print info
        _logger.info(
            "WsgiDAV/{} Python/{} {}".format(
                __version__, util.PYTHON_VERSION, platform.platform(aliased=True)
            )
        )
        if self.verbose >= 4:
            _logger.info(
                "Default encoding: {!r} (file system: {!r})".format(
                    sys.getdefaultencoding(), sys.getfilesystemencoding()
                )
            )

        if self.verbose >= 3:
            _logger.info("Lock manager:      {}".format(self.lock_manager))
            _logger.info("Property manager:  {}".format(self.prop_manager))
            _logger.info("Domain controller: {}".format(domain_controller))

        if self.verbose >= 4:
            # We traversed the stack in reverse order. Now revert again, so
            # we see the order that was configured:
            _logger.info("Middleware stack:")
            for mw in reversed(mw_list):
                _logger.info("  - {}".format(mw))

        if self.verbose >= 3:
            _logger.info("Registered DAV providers by route:")
            for share in self.sorted_share_list:
                provider = self.provider_map[share]
                hint = (
                    " (anonymous)"
                    if domain_controller.is_share_anonymous(share)
                    else ""
                )
                _logger.info("  - '{}': {}{}".format(share, provider, hint))

        if auth_conf.get("accept_basic") and not config.get("ssl_certificate"):
            _logger.warning(
                "Basic authentication is enabled: It is highly recommended to enable SSL."
            )

        if domain_controller:
            for share, provider in self.provider_map.items():
                if domain_controller.is_share_anonymous(share):
                    _logger.warning(
                        "Share '{}' will allow anonymous {} access.".format(
                            share, "read" if provider.is_readonly() else "write"
                        )
                    )
        return
Ejemplo n.º 16
0
    def __init__(self, config):

        self.config = copy.deepcopy(DEFAULT_CONFIG)
        util.deep_update(self.config, config)
        config = self.config

        # Evaluate configuration and set defaults
        _check_config(config)

        self.verbose = config.get("verbose", 3)

        hotfixes = config.get("hotfixes", {})

        self.re_encode_path_info = hotfixes.get("re_encode_path_info", None)
        if self.re_encode_path_info is None:
            self.re_encode_path_info = compat.PY3

        self.unquote_path_info = hotfixes.get("unquote_path_info", False)

        lock_storage = config.get("lock_manager")
        if lock_storage is True:
            lock_storage = LockStorageDict()

        if not lock_storage:
            self.lock_manager = None
        else:
            self.lock_manager = LockManager(lock_storage)

        self.prop_manager = config.get("property_manager")
        if not self.prop_manager:
            # Normalize False, 0 to None
            self.prop_manager = None
        elif self.prop_manager is True:
            self.prop_manager = PropertyManager()

        self.mount_path = config.get("mount_path")
        auth_conf = config.get("http_authenticator", {})

        # Instantiate DAV resource provider objects for every share.
        # provider_mapping may contain the args that are passed to a
        # `FilesystemProvider` instance:
        #     <mount_path>: <folder_path>
        # or
        #     <mount_path>: { "root": <folder_path>, "readonly": True }
        # or contain a complete new instance:
        #     <mount_path>: <DAVProvider Instance>

        provider_mapping = self.config["provider_mapping"]

        self.provider_map = {}
        self.sorted_share_list = None
        for share, provider in provider_mapping.items():
            self.add_provider(share, provider)

        self.http_authenticator = None
        domain_controller = None

        # Define WSGI application stack
        middleware_stack = config.get("middleware_stack", [])
        mw_list = []

        # This is the 'outer' application, i.e. the WSGI application object that
        # is eventually called by the server.
        self.application = self

        # The `middleware_stack` is configured such that the first app in the
        # list should be called first. Since every app wraps its predecessor, we
        # iterate in reverse order:
        for mw in reversed(middleware_stack):
            # The middleware stack configuration may contain plain strings, dicts,
            # classes, or objects
            app = None
            if compat.is_basestring(mw):
                # If a plain string is passed, try to import it, assuming
                # `BaseMiddleware` signature
                app_class = dynamic_import_class(mw)
                app = app_class(self, self.application, config)
            elif type(mw) is dict:
                # If a dict with one entry is passed, use the key as module/class name
                # and the value as constructor arguments (positional or kwargs).
                assert len(mw) == 1
                name, args = list(mw.items())[0]
                expand = {"${application}": self.application}
                app = dynamic_instantiate_middleware(name, args, expand)
            elif inspect.isclass(mw):
                # If a class is passed, assume BaseMiddleware (or compatible)
                assert issubclass(
                    mw, BaseMiddleware)  # TODO: remove this assert with 3.0
                app = mw(self, self.application, config)
            else:
                # Otherwise assume an initialized middleware instance
                app = mw

            # Remember
            if isinstance(app, HTTPAuthenticator):
                self.http_authenticator = app
                domain_controller = app.get_domain_controller()

            # Add middleware to the stack
            if app:
                if callable(getattr(app, "is_disabled",
                                    None)) and app.is_disabled():
                    _logger.warning(
                        "App {}.is_disabled() returned True: skipping.".format(
                            app))
                else:
                    mw_list.append(app)
                    self.application = app
            else:
                _logger.error("Could not add middleware {}.".format(mw))

        domain_controller
        # Print info
        _logger.info("WsgiDAV/{} Python/{} {}".format(
            __version__, util.PYTHON_VERSION, platform.platform(aliased=True)))
        if self.verbose >= 4:
            _logger.info("Default encoding: {!r} (file system: {!r})".format(
                sys.getdefaultencoding(), sys.getfilesystemencoding()))

        if self.verbose >= 3:
            _logger.info("Lock manager:      {}".format(self.lock_manager))
            _logger.info("Property manager:  {}".format(self.prop_manager))
            _logger.info("Domain controller: {}".format(domain_controller))

        if self.verbose >= 4:
            # We traversed the stack in reverse order. Now revert again, so
            # we see the order that was configured:
            _logger.info("Middleware stack:")
            for mw in reversed(mw_list):
                _logger.info("  - {}".format(mw))

        if self.verbose >= 3:
            _logger.info("Registered DAV providers by route:")
            for share in self.sorted_share_list:
                provider = self.provider_map[share]
                hint = (" (anonymous)"
                        if domain_controller.is_share_anonymous(share) else "")
                _logger.info("  - '{}': {}{}".format(share, provider, hint))

        if auth_conf.get("accept_basic") and not config.get("ssl_certificate"):
            _logger.warning(
                "Basic authentication is enabled: It is highly recommended to enable SSL."
            )

        if domain_controller:
            for share, provider in self.provider_map.items():
                if domain_controller.is_share_anonymous(share):
                    _logger.warning(
                        "Share '{}' will allow anonymous {} access.".format(
                            share,
                            "read" if provider.is_readonly() else "write"))
        return
Ejemplo n.º 17
0
    def __call__(self, environ, start_response):
        """"""
        verbose = self._config.get("verbose", 2)
        self.last_request_time = "{0}_{1}".format(
            datetime.utcnow().strftime("%Y-%m-%d_%H-%M-%S"),
            int(round(time.time() * 1000)))

        method = environ["REQUEST_METHOD"]

        debugBreak = False
        dumpRequest = False
        dumpResponse = False

        if verbose >= 3 or self._config.get("dump_requests"):
            dumpRequest = dumpResponse = True

        # Process URL commands
        if "dump_storage" in environ.get("QUERY_STRING"):
            dav = environ.get("wsgidav.provider")
            if dav.lockManager:
                dav.lockManager._dump()
            if dav.propManager:
                dav.propManager._dump()

        # Turn on max. debugging for selected litmus tests
        litmusTag = environ.get("HTTP_X_LITMUS",
                                environ.get("HTTP_X_LITMUS_SECOND"))
        if litmusTag and verbose >= 2:
            print("----\nRunning litmus test '%s'..." % litmusTag,
                  file=self.out)
            for litmusSubstring in self.debug_litmus:
                if litmusSubstring in litmusTag:
                    verbose = 3
                    debugBreak = True
                    dumpRequest = True
                    dumpResponse = True
                    break
            for litmusSubstring in self.break_after_litmus:
                if litmusSubstring in self.passedLitmus and litmusSubstring not in litmusTag:
                    print(" *** break after litmus %s" % litmusTag,
                          file=self.out)
                    sys.exit(-1)
                if litmusSubstring in litmusTag:
                    self.passedLitmus[litmusSubstring] = True

        # Turn on max. debugging for selected request methods
        if verbose >= 2 and method in self.debug_methods:
            verbose = 3
            debugBreak = True
            dumpRequest = True
            dumpResponse = True

        # Set debug options to environment
        environ["wsgidav.verbose"] = verbose
        #        environ["wsgidav.debug_methods"] = self.debug_methods
        environ["wsgidav.debug_break"] = debugBreak
        environ["wsgidav.dump_request_body"] = dumpRequest
        environ["wsgidav.dump_response_body"] = dumpResponse

        # Dump request headers
        if dumpRequest:
            print("<%s> --- %s Request ---" %
                  (threading.currentThread().ident, method),
                  file=self.out)
            for k, v in environ.items():
                if k == k.upper():
                    print("%20s: '%s'" % (k, v), file=self.out)
            print("\n", file=self.out)
            self._dump_request(environ, xml=None)

        # Intercept start_response
        #
        sub_app_start_response = util.SubAppStartResponse()

        nbytes = 0
        first_yield = True
        app_iter = self._application(environ, sub_app_start_response)

        for v in app_iter:
            # Start response (the first time)
            if first_yield:
                # Success!
                start_response(
                    sub_app_start_response.status,
                    sub_app_start_response.response_headers,
                    sub_app_start_response.exc_info,
                )

            # Dump response headers
            if first_yield and dumpResponse:
                print(
                    "<%s> --- %s Response(%s): ---" %
                    (threading.currentThread().ident, method,
                     sub_app_start_response.status),
                    file=self.out,
                )
                headersdict = dict(sub_app_start_response.response_headers)
                for envitem in headersdict.keys():
                    print("%s: %s" % (envitem, repr(headersdict[envitem])),
                          file=self.out)
                print("", file=self.out)

            # Check, if response is a binary string, otherwise we probably have
            # calculated a wrong content-length
            assert compat.is_bytes(v), v

            # Dump response body
            drb = environ.get("wsgidav.dump_response_body")
            if compat.is_basestring(drb):
                # Middleware provided a formatted body representation
                print(drb, file=self.out)
            elif drb is True:
                # Else dump what we get, (except for long GET responses)
                if method == "GET":
                    if first_yield:
                        print(v[:50], "...", file=self.out)
                elif len(v) > 0:
                    print(v, file=self.out)

            if dumpResponse:
                self._dump_response(sub_app_start_response, drb)

            drb = environ["wsgidav.dump_response_body"] = None

            nbytes += len(v)
            first_yield = False
            yield v
        if hasattr(app_iter, "close"):
            app_iter.close()

        # Start response (if it hasn't been done yet)
        if first_yield:
            start_response(
                # Success!
                sub_app_start_response.status,
                sub_app_start_response.response_headers,
                sub_app_start_response.exc_info,
            )

        if dumpResponse:
            print(
                "\n<%s> --- End of %s Response (%i bytes) ---" %
                (threading.currentThread().ident, method, nbytes),
                file=self.out,
            )
        return
Ejemplo n.º 18
0
    def __call__(self, environ, start_response):
        """"""
        # srvcfg = environ["wsgidav.config"]
        verbose = self._config.get("verbose", 3)

        method = environ["REQUEST_METHOD"]

        debugBreak = False
        dumpRequest = False
        dumpResponse = False

        if verbose >= 5:
            dumpRequest = dumpResponse = True

        # Process URL commands
        if "dump_storage" in environ.get("QUERY_STRING", ""):
            dav = environ.get("wsgidav.provider")
            if dav.lockManager:
                dav.lockManager._dump()
            if dav.propManager:
                dav.propManager._dump()

        # Turn on max. debugging for selected litmus tests
        litmusTag = environ.get("HTTP_X_LITMUS",
                                environ.get("HTTP_X_LITMUS_SECOND"))
        if litmusTag and verbose >= 3:
            _logger.info("----\nRunning litmus test '{}'...".format(litmusTag))
            for litmusSubstring in self.debug_litmus:
                if litmusSubstring in litmusTag:
                    verbose = 5
                    debugBreak = True
                    dumpRequest = True
                    dumpResponse = True
                    break
            for litmusSubstring in self.break_after_litmus:
                if litmusSubstring in self.passedLitmus and litmusSubstring not in litmusTag:
                    _logger.info(
                        " *** break after litmus {}".format(litmusTag))
                    sys.exit(-1)
                if litmusSubstring in litmusTag:
                    self.passedLitmus[litmusSubstring] = True

        # Turn on max. debugging for selected request methods
        if verbose >= 3 and method in self.debug_methods:
            verbose = 5
            debugBreak = True
            dumpRequest = True
            dumpResponse = True

        # Set debug options to environment
        environ["wsgidav.verbose"] = verbose
        # environ["wsgidav.debug_methods"] = self.debug_methods
        environ["wsgidav.debug_break"] = debugBreak
        environ["wsgidav.dump_request_body"] = dumpRequest
        environ["wsgidav.dump_response_body"] = dumpResponse

        # Dump request headers
        if dumpRequest:
            _logger.info("{} Request ---".format(method))
            # _logger.info("<{}> --- {} Request ---".format(
            #         threading.currentThread().ident, method))
            for k, v in environ.items():
                if k == k.upper():
                    _logger.info("{:<20}: '{}'".format(
                        k, safe_re_encode(v, "utf8")))
            _logger.info("\n")

        # Intercept start_response
        #
        sub_app_start_response = util.SubAppStartResponse()

        nbytes = 0
        first_yield = True
        app_iter = self.next_app(environ, sub_app_start_response)

        for v in app_iter:
            # Start response (the first time)
            if first_yield:
                # Success!
                start_response(sub_app_start_response.status,
                               sub_app_start_response.response_headers,
                               sub_app_start_response.exc_info)

            # Dump response headers
            if first_yield and dumpResponse:
                _logger.info("<{}> ---{}  Response({}): ---".format(
                    threading.currentThread().ident, method,
                    sub_app_start_response.status))
                headersdict = dict(sub_app_start_response.response_headers)
                for envitem in headersdict.keys():
                    _logger.info("{}: {}".format(envitem,
                                                 repr(headersdict[envitem])))
                _logger.info("")

            # Check, if response is a binary string, otherwise we probably have
            # calculated a wrong content-length
            assert compat.is_bytes(v), v

            # Dump response body
            drb = environ.get("wsgidav.dump_response_body")
            if compat.is_basestring(drb):
                # Middleware provided a formatted body representation
                _logger.info(drb)
                drb = environ["wsgidav.dump_response_body"] = None
            elif drb is True:
                # Else dump what we get, (except for long GET responses)
                if method == "GET":
                    if first_yield:
                        _logger.info("{}...".format(v[:50]))
                elif len(v) > 0:
                    _logger.info(v)

            nbytes += len(v)
            first_yield = False
            yield v
        if hasattr(app_iter, "close"):
            app_iter.close()

        # Start response (if it hasn't been done yet)
        if first_yield:
            # Success!
            start_response(sub_app_start_response.status,
                           sub_app_start_response.response_headers,
                           sub_app_start_response.exc_info)

        if dumpResponse:
            _logger.info("<{}> --- End of {} Response ({:d} bytes) ---".format(
                threading.currentThread().ident, method, nbytes))
        return
Ejemplo n.º 19
0
    def _get_context(self, environ, davres):
        """
        @see: http://www.webdav.org/specs/rfc4918.html#rfc.section.9.4
        """
        assert davres.is_collection

        dirConfig = environ["wsgidav.config"].get("dir_browser", {})
        isReadOnly = environ["wsgidav.provider"].is_readonly()

        context = {
            "htdocs": (self.config.get("mount_path") or "") + ASSET_SHARE,
            "rows": [],
            "version": __version__,
            "displaypath": compat.unquote(davres.get_href()),
            "url": davres.get_href(),  # util.make_complete_url(environ),
            "parentUrl": util.get_uri_parent(davres.get_href()),
            "config": dirConfig,
            "is_readonly": isReadOnly,
        }

        trailer = dirConfig.get("response_trailer")
        if trailer is True:
            trailer = "${version} - ${time}"

        if trailer:
            trailer = trailer.replace(
                "${version}",
                "<a href='https://github.com/mar10/wsgidav/'>WsgiDAV/{}</a>".format(
                    __version__
                ),
            )
            trailer = trailer.replace("${time}", util.get_rfc1123_time())

        context["trailer"] = trailer

        rows = context["rows"]

        # Ask collection for member info list
        dirInfoList = davres.get_directory_info()

        if dirInfoList is None:
            # No pre-build info: traverse members
            dirInfoList = []
            childList = davres.get_descendants(depth="1", addSelf=False)
            for res in childList:
                di = res.get_display_info()
                href = res.get_href()
                classes = []
                if res.is_collection:
                    classes.append("directory")

                if not isReadOnly and not res.is_collection:
                    ext = os.path.splitext(href)[1].lstrip(".").lower()
                    officeType = msOfficeExtToTypeMap.get(ext)
                    if officeType:
                        if dirConfig.get("ms_sharepoint_plugin"):
                            classes.append("msoffice")
                        elif dirConfig.get("ms_sharepoint_urls"):
                            href = "ms-{}:ofe|u|{}".format(officeType, href)

                entry = {
                    "href": href,
                    "class": " ".join(classes),
                    "displayName": res.get_display_name(),
                    "lastModified": res.get_last_modified(),
                    "is_collection": res.is_collection,
                    "contentLength": res.get_content_length(),
                    "displayType": di.get("type"),
                    "displayTypeComment": di.get("typeComment"),
                }

                dirInfoList.append(entry)
        #
        ignore_patterns = dirConfig.get("ignore", [])
        if compat.is_basestring(ignore_patterns):
            ignore_patterns = ignore_patterns.split(",")

        for entry in dirInfoList:
            # Skip ignore patterns
            ignore = False
            for pat in ignore_patterns:
                if fnmatch(entry["displayName"], pat):
                    _logger.debug("Ignore {}".format(entry["displayName"]))
                    ignore = True
                    break
            if ignore:
                continue
            #
            lastModified = entry.get("lastModified")
            if lastModified is None:
                entry["strModified"] = ""
            else:
                entry["strModified"] = util.get_rfc1123_time(lastModified)

            entry["strSize"] = "-"
            if not entry.get("is_collection"):
                contentLength = entry.get("contentLength")
                if contentLength is not None:
                    entry["strSize"] = util.byte_number_string(contentLength)

            rows.append(entry)

        # sort
        sort = "name"
        if sort == "name":
            rows.sort(
                key=lambda v: "{}{}".format(
                    not v["is_collection"], v["displayName"].lower()
                )
            )

        if "http_authenticator.username" in environ:
            context["username"] = (
                environ.get("http_authenticator.username") or "anonymous"
            )
            context["realm"] = environ.get("http_authenticator.realm")

        return context
Ejemplo n.º 20
0
Archivo: utils.py Proyecto: buxx/tracim
    def __call__(self, environ, start_response):
        """"""
        #        srvcfg = environ["wsgidav.config"]
        verbose = self._config.get("verbose", 2)
        self.last_request_time = '{0}_{1}'.format(
            datetime.utcnow().strftime('%Y-%m-%d_%H-%M-%S'),
            int(round(time.time() * 1000)),
        )

        method = environ["REQUEST_METHOD"]

        debugBreak = False
        dumpRequest = False
        dumpResponse = False

        if verbose >= 3 or self._config.get("dump_requests"):
            dumpRequest = dumpResponse = True

        # Process URL commands
        if "dump_storage" in environ.get("QUERY_STRING"):
            dav = environ.get("wsgidav.provider")
            if dav.lockManager:
                dav.lockManager._dump()
            if dav.propManager:
                dav.propManager._dump()

        # Turn on max. debugging for selected litmus tests
        litmusTag = environ.get("HTTP_X_LITMUS",
                                environ.get("HTTP_X_LITMUS_SECOND"))
        if litmusTag and verbose >= 2:
            print("----\nRunning litmus test '%s'..." % litmusTag,
                  file=self.out)
            for litmusSubstring in self.debug_litmus:
                if litmusSubstring in litmusTag:
                    verbose = 3
                    debugBreak = True
                    dumpRequest = True
                    dumpResponse = True
                    break
            for litmusSubstring in self.break_after_litmus:
                if litmusSubstring in self.passedLitmus and litmusSubstring not in litmusTag:
                    print(" *** break after litmus %s" % litmusTag,
                          file=self.out)
                    sys.exit(-1)
                if litmusSubstring in litmusTag:
                    self.passedLitmus[litmusSubstring] = True

        # Turn on max. debugging for selected request methods
        if verbose >= 2 and method in self.debug_methods:
            verbose = 3
            debugBreak = True
            dumpRequest = True
            dumpResponse = True

        # Set debug options to environment
        environ["wsgidav.verbose"] = verbose
        #        environ["wsgidav.debug_methods"] = self.debug_methods
        environ["wsgidav.debug_break"] = debugBreak
        environ["wsgidav.dump_request_body"] = dumpRequest
        environ["wsgidav.dump_response_body"] = dumpResponse

        # Dump request headers
        if dumpRequest:
            print("<%s> --- %s Request ---" % (
            threading.currentThread().ident, method), file=self.out)
            for k, v in environ.items():
                if k == k.upper():
                    print("%20s: '%s'" % (k, v), file=self.out)
            print("\n", file=self.out)
            self._dump_request(environ, xml=None)

        # Intercept start_response
        #
        sub_app_start_response = util.SubAppStartResponse()

        nbytes = 0
        first_yield = True
        app_iter = self._application(environ, sub_app_start_response)

        for v in app_iter:
            # Start response (the first time)
            if first_yield:
                # Success!
                start_response(sub_app_start_response.status,
                               sub_app_start_response.response_headers,
                               sub_app_start_response.exc_info)

            # Dump response headers
            if first_yield and dumpResponse:
                print("<%s> --- %s Response(%s): ---" % (
                threading.currentThread().ident,
                method,
                sub_app_start_response.status),
                      file=self.out)
                headersdict = dict(sub_app_start_response.response_headers)
                for envitem in headersdict.keys():
                    print("%s: %s" % (envitem, repr(headersdict[envitem])),
                          file=self.out)
                print("", file=self.out)

            # Check, if response is a binary string, otherwise we probably have
            # calculated a wrong content-length
            assert compat.is_bytes(v), v

            # Dump response body
            drb = environ.get("wsgidav.dump_response_body")
            if compat.is_basestring(drb):
                # Middleware provided a formatted body representation
                print(drb, file=self.out)
            elif drb is True:
                # Else dump what we get, (except for long GET responses)
                if method == "GET":
                    if first_yield:
                        print(v[:50], "...", file=self.out)
                elif len(v) > 0:
                    print(v, file=self.out)

            if dumpResponse:
                self._dump_response(sub_app_start_response, drb)

            drb = environ["wsgidav.dump_response_body"] = None

            nbytes += len(v)
            first_yield = False
            yield v
        if hasattr(app_iter, "close"):
            app_iter.close()

        # Start response (if it hasn't been done yet)
        if first_yield:
            # Success!
            start_response(sub_app_start_response.status,
                           sub_app_start_response.response_headers,
                           sub_app_start_response.exc_info)

        if dumpResponse:
            print("\n<%s> --- End of %s Response (%i bytes) ---" % (
            threading.currentThread().ident, method, nbytes), file=self.out)
        return
Ejemplo n.º 21
0
    def __init__(self, config):

        self.config = copy.deepcopy(DEFAULT_CONFIG)
        util.deep_update(self.config, config)
        # self.config.update(config)
        config = self.config

        # Evaluate configuration and set defaults
        _check_config(config)

        #        response_trailer = config.get("response_trailer", "")
        self.verbose = config.get("verbose", 3)

        lockStorage = config.get("lock_manager")
        if lockStorage is True:
            lockStorage = LockStorageDict()

        if not lockStorage:
            self.locksManager = None
        else:
            self.locksManager = LockManager(lockStorage)

        self.propsManager = config.get("property_manager")
        if not self.propsManager:
            # Normalize False, 0 to None
            self.propsManager = None
        elif self.propsManager is True:
            self.propsManager = PropertyManager()

        self.mount_path = config.get("mount_path")

        # Instantiate DAV resource provider objects for every share
        # provider_mapping may contain the args that are passed to a `FilesystemProvider`
        # instance:
        #     <mount_path>: <folder_path>
        # or
        #     <mount_path>: [folder_path, is_readonly]
        # or contain a complete new instance:
        #     <mount_path>: <DAVProvider Instance>

        provider_mapping = self.config["provider_mapping"]

        self.providerMap = {}
        self.sortedShareList = None
        for share, provider in provider_mapping.items():
            self.add_provider(share, provider)

        domain_controller = None

        # Define WSGI application stack
        middleware_stack = config.get("middleware_stack", [])
        mw_list = []

        # This is the 'outer' application, i.e. the WSGI application object that
        # is eventually called by the server.
        self.application = self

        # When building up the middleware stack, this app will be wrapped and replaced by the
        # next middleware in the list.
        # The `middleware_stack` is configured such that the first app in the list should be
        # called first. But since every app wraps its predecessor, we iterate in reverse
        # order:
        for mw in reversed(middleware_stack):
            # The middleware stack configuration may contain plain strings, dicts,
            # classes, or objects
            app = None
            if compat.is_basestring(mw):
                # If a plain string is passed, try to import it, assuming BaseMiddleware
                # signature
                app_class = dynamic_import_class(mw)
                app = app_class(self, self.application, config)
            elif type(mw) is dict:
                # If a dict with one entry is passed, use the key as module/class name
                # and the value as constructor arguments (positional or kwargs).
                assert len(mw) == 1
                name, args = list(mw.items())[0]
                expand = {"${application}": self.application}
                app = dynamic_instantiate_middleware(name, args, expand)
            elif inspect.isclass(mw):
                # If a class is passed, assume BaseMiddleware (or compatible)
                assert issubclass(
                    mw, BaseMiddleware
                )  # TODO: remove this assert with 3.0
                app = mw(self, self.application, config)
            else:
                # Otherwise assume an initialized middleware instance
                app = mw

            # TODO: We should try to generalize this specific code:
            if isinstance(app, HTTPAuthenticator):
                domain_controller = app.get_domain_controller()
                # Check anonymous access
                for share, data in self.providerMap.items():
                    if app.allow_anonymous_access(share):
                        data["allow_anonymous"] = True

            # Add middleware to the stack
            if app:
                mw_list.append(app)
                self.application = app
            else:
                _logger.error("Could not add middleware {}.".format(mw))

        # Print info
        _logger.info(
            "WsgiDAV/{} Python/{} {}".format(
                __version__, util.PYTHON_VERSION, platform.platform(aliased=True)
            )
        )
        if self.verbose >= 4:
            _logger.info(
                "Default encoding: {!r} (file system: {!r})".format(
                    sys.getdefaultencoding(), sys.getfilesystemencoding()
                )
            )
            _logger.info("Lock manager:      {}".format(self.locksManager))
            _logger.info("Property manager:  {}".format(self.propsManager))
            _logger.info("Domain controller: {}".format(domain_controller))

            # We traversed the stack in reverse order. Now revert again, so
            # we see the order that was configured:
            _logger.info("Middleware stack:")
            for mw in reversed(mw_list):
                _logger.info("  - {}".format(mw))

            _logger.info("Registered DAV providers by route:")
            for share in self.sortedShareList:
                data = self.providerMap[share]
                hint = " (anonymous)" if data["allow_anonymous"] else ""
                _logger.info("  - '{}': {}{}".format(share, data["provider"], hint))

        for share, data in self.providerMap.items():
            if data["allow_anonymous"]:
                # TODO: we should only warn here, if --no-auth is not given
                _logger.warn("Share '{}' will allow anonymous access.".format(share))
        return
Ejemplo n.º 22
0
    def __call__(self, environ, start_response):
        """"""
        # srvcfg = environ["wsgidav.config"]
        verbose = self._config.get("verbose", 3)

        method = environ["REQUEST_METHOD"]

        debugBreak = False
        dumpRequest = False
        dumpResponse = False

        if verbose >= 5:
            dumpRequest = dumpResponse = True

        # Process URL commands
        if "dump_storage" in environ.get("QUERY_STRING", ""):
            dav = environ.get("wsgidav.provider")
            if dav.lock_manager:
                dav.lock_manager._dump()
            if dav.prop_manager:
                dav.prop_manager._dump()

        # Turn on max. debugging for selected litmus tests
        litmusTag = environ.get("HTTP_X_LITMUS", environ.get("HTTP_X_LITMUS_SECOND"))
        if litmusTag and verbose >= 3:
            _logger.info("----\nRunning litmus test '{}'...".format(litmusTag))
            for litmusSubstring in self.debug_litmus:
                if litmusSubstring in litmusTag:
                    verbose = 5
                    debugBreak = True
                    dumpRequest = True
                    dumpResponse = True
                    break
            for litmusSubstring in self.break_after_litmus:
                if (
                    litmusSubstring in self.passedLitmus
                    and litmusSubstring not in litmusTag
                ):
                    _logger.info(" *** break after litmus {}".format(litmusTag))
                    sys.exit(-1)
                if litmusSubstring in litmusTag:
                    self.passedLitmus[litmusSubstring] = True

        # Turn on max. debugging for selected request methods
        if verbose >= 3 and method in self.debug_methods:
            verbose = 5
            debugBreak = True
            dumpRequest = True
            dumpResponse = True

        # Set debug options to environment
        environ["wsgidav.verbose"] = verbose
        # environ["wsgidav.debug_methods"] = self.debug_methods
        environ["wsgidav.debug_break"] = debugBreak
        environ["wsgidav.dump_request_body"] = dumpRequest
        environ["wsgidav.dump_response_body"] = dumpResponse

        # Dump request headers
        if dumpRequest:
            _logger.info("{} Request ---".format(method))
            # _logger.info("<{}> --- {} Request ---".format(
            #         threading.currentThread().ident, method))
            for k, v in environ.items():
                if k == k.upper():
                    _logger.info("{:<20}: '{}'".format(k, safe_re_encode(v, "utf8")))
            _logger.info("\n")

        # Intercept start_response
        #
        sub_app_start_response = util.SubAppStartResponse()

        nbytes = 0
        first_yield = True
        app_iter = self.next_app(environ, sub_app_start_response)

        for v in app_iter:
            # Start response (the first time)
            if first_yield:
                # Success!
                start_response(
                    sub_app_start_response.status,
                    sub_app_start_response.response_headers,
                    sub_app_start_response.exc_info,
                )

            # Dump response headers
            if first_yield and dumpResponse:
                _logger.info(
                    "<{}> ---{}  Response({}): ---".format(
                        threading.currentThread().ident,
                        method,
                        sub_app_start_response.status,
                    )
                )
                headersdict = dict(sub_app_start_response.response_headers)
                for envitem in headersdict.keys():
                    _logger.info("{}: {}".format(envitem, repr(headersdict[envitem])))
                _logger.info("")

            # Check, if response is a binary string, otherwise we probably have
            # calculated a wrong content-length
            assert compat.is_bytes(v), v

            # Dump response body
            drb = environ.get("wsgidav.dump_response_body")
            if compat.is_basestring(drb):
                # Middleware provided a formatted body representation
                _logger.info(drb)
                drb = environ["wsgidav.dump_response_body"] = None
            elif drb is True:
                # Else dump what we get, (except for long GET responses)
                if method == "GET":
                    if first_yield:
                        _logger.info("{}...".format(v[:50]))
                elif len(v) > 0:
                    _logger.info(v)

            nbytes += len(v)
            first_yield = False
            yield v
        if hasattr(app_iter, "close"):
            app_iter.close()

        # Start response (if it hasn't been done yet)
        if first_yield:
            # Success!
            start_response(
                sub_app_start_response.status,
                sub_app_start_response.response_headers,
                sub_app_start_response.exc_info,
            )

        if dumpResponse:
            _logger.info(
                "<{}> --- End of {} Response ({:d} bytes) ---".format(
                    threading.currentThread().ident, method, nbytes
                )
            )
        return
Ejemplo n.º 23
0
 def _expand(v):
     """Replace some string templates with defined values."""
     if expand and compat.is_basestring(v) and v.lower() in expand:
         return expand[v]
     return v
Ejemplo n.º 24
0
    def _get_context(self, environ, dav_res):
        """
        @see: http://www.webdav.org/specs/rfc4918.html#rfc.section.9.4
        """
        assert dav_res.is_collection

        is_readonly = environ["wsgidav.provider"].is_readonly()

        context = {
            "htdocs": (self.config.get("mount_path") or "") + ASSET_SHARE,
            "rows": [],
            "version": __version__,
            "display_path": compat.unquote(dav_res.get_href()),
            "url": dav_res.get_href(),  # util.make_complete_url(environ),
            "parent_url": util.get_uri_parent(dav_res.get_href()),
            "config": self.dir_config,
            "is_readonly": is_readonly,
            "access": "read-only" if is_readonly else "read-write",
            "is_authenticated": False,
        }

        trailer = self.dir_config.get("response_trailer")
        if trailer is True:
            trailer = "${version} - ${time}"

        if trailer:
            trailer = trailer.replace(
                "${version}",
                "<a href='https://github.com/mar10/wsgidav/'>WsgiDAV/{}</a>".format(
                    __version__
                ),
            )
            trailer = trailer.replace("${time}", util.get_rfc1123_time())

        context["trailer"] = trailer

        rows = context["rows"]

        # Ask collection for member info list
        dirInfoList = dav_res.get_directory_info()

        if dirInfoList is None:
            # No pre-build info: traverse members
            dirInfoList = []
            childList = dav_res.get_descendants(depth="1", add_self=False)
            for res in childList:
                di = res.get_display_info()
                href = res.get_href()
                ofe_prefix = None
                tr_classes = []
                a_classes = []
                if res.is_collection:
                    tr_classes.append("directory")

                if not is_readonly and not res.is_collection:
                    ext = os.path.splitext(href)[1].lstrip(".").lower()
                    officeType = msOfficeExtToTypeMap.get(ext)
                    if officeType:
                        if self.dir_config.get("ms_sharepoint_support"):
                            ofe_prefix = "ms-{}:ofe|u|".format(officeType)
                            a_classes.append("msoffice")
                        # elif self.dir_config.get("ms_sharepoint_plugin"):
                        #     a_classes.append("msoffice")
                        # elif self.dir_config.get("ms_sharepoint_urls"):
                        #     href = "ms-{}:ofe|u|{}".format(officeType, href)

                entry = {
                    "href": href,
                    "ofe_prefix": ofe_prefix,
                    "a_class": " ".join(a_classes),
                    "tr_class": " ".join(tr_classes),
                    "display_name": res.get_display_name(),
                    "last_modified": res.get_last_modified(),
                    "is_collection": res.is_collection,
                    "content_length": res.get_content_length(),
                    "display_type": di.get("type"),
                    "display_type_comment": di.get("typeComment"),
                }

                dirInfoList.append(entry)
        #
        ignore_patterns = self.dir_config.get("ignore", [])
        if compat.is_basestring(ignore_patterns):
            ignore_patterns = ignore_patterns.split(",")

        ignored_list = []
        for entry in dirInfoList:
            # Skip ignore patterns
            ignore = False
            for pat in ignore_patterns:
                if fnmatch(entry["display_name"], pat):
                    ignored_list.append(entry["display_name"])
                    # _logger.debug("Ignore {}".format(entry["display_name"]))
                    ignore = True
                    break
            if ignore:
                continue
            #
            last_modified = entry.get("last_modified")
            if last_modified is None:
                entry["str_modified"] = ""
            else:
                entry["str_modified"] = util.get_rfc1123_time(last_modified)

            entry["str_size"] = "-"
            if not entry.get("is_collection"):
                content_length = entry.get("content_length")
                if content_length is not None:
                    entry["str_size"] = util.byte_number_string(content_length)

            rows.append(entry)
        if ignored_list:
            _logger.debug(
                "Dir browser ignored {} entries: {}".format(
                    len(ignored_list), ignored_list
                )
            )

        # sort
        sort = "name"
        if sort == "name":
            rows.sort(
                key=lambda v: "{}{}".format(
                    not v["is_collection"], v["display_name"].lower()
                )
            )

        if "wsgidav.auth.user_name" in environ:
            context.update(
                {
                    "is_authenticated": True,
                    "user_name": (environ.get("wsgidav.auth.user_name") or "anonymous"),
                    "realm": environ.get("wsgidav.auth.realm"),
                    "user_roles": ", ".join(environ.get("wsgidav.auth.roles") or []),
                    "user_permissions": ", ".join(
                        environ.get("wsgidav.auth.permissions") or []
                    ),
                }
            )

        return context