signature:
def add_provider ( self , share , provider , readonly = False ) :
    """Add a provider to the provider _ map routing table ."""

implementation:
# 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


signature:
def resolve_provider ( self , path ) :
    """Get the registered DAVProvider for a given path .
 Returns :
 tuple : ( share , provider )"""

implementation:
# Find DAV provider that matches the share
share = None
lower_path = path . lower ( )
for r in self . sorted_share_list : # @ @ : Case sensitivity should be an option of some sort here ;
# os . path . normpath might give the preferred case for a filename .
    if r == "/" :
        share = r
        break
    elif lower_path == r or lower_path . startswith ( r + "/" ) :
        share = r
        break
if share is None :
    return None , None
return share , self . provider_map . get ( share )


signature:
def compute_digest_response ( self , realm , user_name , method , uri , nonce , cnonce , qop , nc , environ ) :
    """Computes digest hash .
 Calculation of the A1 ( HA1 ) part is delegated to the dc interface method
 ` digest _ auth _ user ( ) ` .
 Args :
 realm ( str ) :
 user _ name ( str ) :
 method ( str ) : WebDAV Request Method
 uri ( str ) :
 nonce ( str ) : server generated nonce value
 cnonce ( str ) : client generated cnonce value
 qop ( str ) : quality of protection
 nc ( str ) ( number ) , nonce counter incremented by client
 Returns :
 MD5 hash string
 or False if user rejected by domain controller"""

implementation:
def md5h ( data ) :
    return md5 ( compat . to_bytes ( data ) ) . hexdigest ( )
def md5kd ( secret , data ) :
    return md5h ( secret + ":" + data )
A1 = self . domain_controller . digest_auth_user ( realm , user_name , environ )
if not A1 :
    return False
A2 = method + ":" + uri
if qop :
    res = md5kd ( A1 , nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + md5h ( A2 ) )
else :
    res = md5kd ( A1 , nonce + ":" + md5h ( A2 ) )
return res


signature:
def read ( self , size = 0 ) :
    """Read a chunk of bytes from queue .
 size = 0 : Read next chunk ( arbitrary length )
 > 0 : Read one chunk of ` size ` bytes ( or less if stream was closed )
 < 0 : Read all bytes as single chunk ( i . e . blocks until stream is closed )
 This method blocks until the requested size become available .
 However , if close ( ) was called , ' ' is returned immediately ."""

implementation:
res = self . unread
self . unread = ""
# Get next chunk , cumulating requested size as needed
while res == "" or size < 0 or ( size > 0 and len ( res ) < size ) :
    try : # Read pending data , blocking if neccessary
    # ( but handle the case that close ( ) is called while waiting )
        res += compat . to_native ( self . queue . get ( True , 0.1 ) )
    except compat . queue . Empty : # There was no pending data : wait for more , unless close ( ) was called
        if self . is_closed :
            break
# Deliver ` size ` bytes from buffer
if size > 0 and len ( res ) > size :
    self . unread = res [ size : ]
    res = res [ : size ]
# print ( " FileLikeQueue . read ( { } ) = > { } bytes " . format ( size , len ( res ) ) )
return res


signature:
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 ."""

implementation:
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 )


signature:
def read ( self , size = None ) :
    """Read bytes from an iterator ."""

implementation:
while size is None or len ( self . buffer ) < size :
    try :
        self . buffer += next ( self . data_stream )
    except StopIteration :
        break
sized_chunk = self . buffer [ : size ]
if size is None :
    self . buffer = ""
else :
    self . buffer = self . buffer [ size : ]
return sized_chunk


signature:
def handle_error ( self , request , client_address ) :
    """Handle an error gracefully . May be overridden .
 The default is to _ logger . info a traceback and continue ."""

implementation:
ei = sys . exc_info ( )
e = ei [ 1 ]
# Suppress stack trace when client aborts connection disgracefully :
# 10053 : Software caused connection abort
# 10054 : Connection reset by peer
if e . args [ 0 ] in ( 10053 , 10054 ) :
    _logger . error ( "*** Caught socket.error: {}" . format ( e ) )
    return
# This is what BaseHTTPServer . HTTPServer . handle _ error does , but with
# added thread ID and using stderr
_logger . error ( "-" * 40 , file = sys . stderr )
_logger . error ( "<{}> Exception happened during processing of request from {}" . format ( threading . currentThread ( ) . ident , client_address ) )
_logger . error ( client_address , file = sys . stderr )
traceback . print_exc ( )
_logger . error ( "-" * 40 , file = sys . stderr )
_logger . error ( request , file = sys . stderr )


signature:
def serve_forever_stoppable ( self ) :
    """Handle one request at a time until stop _ serve _ forever ( ) .
 http : / / code . activestate . com / recipes / 336012/"""

implementation:
self . stop_request = False
self . stopped = False
while not self . stop_request :
    self . handle_request ( )
# _ logger . info " serve _ forever _ stoppable ( ) stopped . "
self . stopped = True


signature:
def get_property_names ( self , is_allprop ) :
    """Return list of supported property names in Clark Notation .
 See DAVResource . get _ property _ names ( )"""

implementation:
# Let base class implementation add supported live and dead properties
propNameList = super ( HgResource , self ) . get_property_names ( is_allprop )
# Add custom live properties ( report on ' allprop ' and ' propnames ' )
if self . fctx :
    propNameList . extend ( [ "{hg:}branch" , "{hg:}date" , "{hg:}description" , "{hg:}filerev" , "{hg:}rev" , "{hg:}user" , ] )
return propNameList


signature:
def get_property_value ( self , name ) :
    """Return the value of a property .
 See get _ property _ value ( )"""

implementation:
# Supported custom live properties
if name == "{hg:}branch" :
    return self . fctx . branch ( )
elif name == "{hg:}date" : # ( secs , tz - ofs )
    return compat . to_native ( self . fctx . date ( ) [ 0 ] )
elif name == "{hg:}description" :
    return self . fctx . description ( )
elif name == "{hg:}filerev" :
    return compat . to_native ( self . fctx . filerev ( ) )
elif name == "{hg:}rev" :
    return compat . to_native ( self . fctx . rev ( ) )
elif name == "{hg:}user" :
    return compat . to_native ( self . fctx . user ( ) )
# Let base class implementation report live and dead properties
return super ( HgResource , self ) . get_property_value ( name )


signature:
def create_empty_resource ( self , name ) :
    """Create and return an empty ( length - 0 ) resource as member of self .
 See DAVResource . create _ empty _ resource ( )"""

implementation:
assert self . is_collection
self . _check_write_access ( )
filepath = self . _getFilePath ( name )
f = open ( filepath , "w" )
f . close ( )
commands . add ( self . provider . ui , self . provider . repo , filepath )
# get _ resource _ inst ( ) won ' t work , because the cached manifest is outdated
# return self . provider . get _ resource _ inst ( self . path . rstrip ( " / " ) + " / " + name , self . environ )
return HgResource ( self . path . rstrip ( "/" ) + "/" + name , False , self . environ , self . rev , self . localHgPath + "/" + name , )


signature:
def create_collection ( self , name ) :
    """Create a new collection as member of self .
 A dummy member is created , because Mercurial doesn ' t handle folders ."""

implementation:
assert self . is_collection
self . _check_write_access ( )
collpath = self . _getFilePath ( name )
os . mkdir ( collpath )
filepath = self . _getFilePath ( name , ".directory" )
f = open ( filepath , "w" )
f . write ( "Created by WsgiDAV." )
f . close ( )
commands . add ( self . provider . ui , self . provider . repo , filepath )


signature:
def get_content ( self ) :
    """Open content as a stream for reading .
 See DAVResource . get _ content ( )"""

implementation:
assert not self . is_collection
d = self . fctx . data ( )
return compat . StringIO ( d )


signature:
def begin_write ( self , content_type = None ) :
    """Open content as a stream for writing .
 See DAVResource . begin _ write ( )"""

implementation:
assert not self . is_collection
self . _check_write_access ( )
mode = "wb"
# GC issue 57 : always store as binary
# if contentType and contentType . startswith ( " text " ) :
# mode = " w "
return open ( self . absFilePath , mode , BUFFER_SIZE )


signature:
def end_write ( self , with_errors ) :
    """Called when PUT has finished writing .
 See DAVResource . end _ write ( )"""

implementation:
if not with_errors :
    commands . add ( self . provider . ui , self . provider . repo , self . localHgPath )


signature:
def delete ( self ) :
    """Remove this resource ( recursive ) ."""

implementation:
self . _check_write_access ( )
filepath = self . _getFilePath ( )
commands . remove ( self . provider . ui , self . provider . repo , filepath , force = True )


signature:
def handle_copy ( self , dest_path , depth_infinity ) :
    """Handle a COPY request natively ."""

implementation:
destType , destHgPath = util . pop_path ( dest_path )
destHgPath = destHgPath . strip ( "/" )
ui = self . provider . ui
repo = self . provider . repo
_logger . info ( "handle_copy %s -> %s" % ( self . localHgPath , destHgPath ) )
if self . rev is None and destType == "edit" : # COPY / edit / a / b to / edit / c / d : turn into ' hg copy - f a / b c / d '
    commands . copy ( ui , repo , self . localHgPath , destHgPath , force = True )
elif self . rev is None and destType == "released" : # COPY / edit / a / b to / released / c / d
# This is interpreted as ' hg commit a / b ' ( ignoring the dest . path )
    self . _commit ( "WsgiDAV commit (COPY %s -> %s)" % ( self . path , dest_path ) )
else :
    raise DAVError ( HTTP_FORBIDDEN )
# Return True : request was handled
return True


signature:
def _get_log ( self , limit = None ) :
    """Read log entries into a list of dictionaries ."""

implementation:
self . ui . pushbuffer ( )
commands . log ( self . ui , self . repo , limit = limit , date = None , rev = None , user = None )
res = self . ui . popbuffer ( ) . strip ( )
logList = [ ]
for logentry in res . split ( "\n\n" ) :
    log = { }
    logList . append ( log )
    for line in logentry . split ( "\n" ) :
        k , v = line . split ( ":" , 1 )
        assert k in ( "changeset" , "tag" , "user" , "date" , "summary" )
        log [ k . strip ( ) ] = v . strip ( )
    log [ "parsed_date" ] = util . parse_time_string ( log [ "date" ] )
    local_id , unid = log [ "changeset" ] . split ( ":" )
    log [ "local_id" ] = int ( local_id )
    log [ "unid" ] = unid
# pprint ( logList )
return logList


signature:
def _get_repo_info ( self , environ , rev , reload = False ) :
    """Return a dictionary containing all files under source control .
 dirinfos :
 Dictionary containing direct members for every collection .
 { folderpath : ( collectionlist , filelist ) , . . . }
 files :
 Sorted list of all file paths in the manifest .
 filedict :
 Dictionary containing all files under source control .
 { ' dirinfos ' : { ' ' : ( [ ' wsgidav ' ,
 ' tools ' ,
 ' WsgiDAV . egg - info ' ,
 ' tests ' ] ,
 [ ' index . rst ' ,
 ' wsgidav MAKE _ DAILY _ BUILD . launch ' ,
 ' wsgidav run _ server . py DEBUG . launch ' ,
 ' wsgidav - paste . conf ' ,
 ' setup . py ' ] ) ,
 ' wsgidav ' : ( [ ' addons ' , ' samples ' , ' server ' , ' interfaces ' ] ,
 [ ' _ _ init _ _ . pyc ' ,
 ' dav _ error . pyc ' ,
 ' dav _ provider . pyc ' ,
 ' wsgidav _ app . py ' ] ) ,
 ' files ' : [ ' . hgignore ' ,
 ' ADDONS . txt ' ,
 ' wsgidav / samples / mysql _ dav _ provider . py ' ,
 ' filedict ' : { ' . hgignore ' : True ,
 ' README . txt ' : True ,
 ' WsgiDAV . egg - info / PKG - INFO ' : True ,"""

implementation:
caches = environ . setdefault ( "wsgidav.hg.cache" , { } )
if caches . get ( compat . to_native ( rev ) ) is not None :
    _logger . debug ( "_get_repo_info(%s): cache hit." % rev )
    return caches [ compat . to_native ( rev ) ]
start_time = time . time ( )
self . ui . pushbuffer ( )
commands . manifest ( self . ui , self . repo , rev )
res = self . ui . popbuffer ( )
files = [ ]
dirinfos = { }
filedict = { }
for file in res . split ( "\n" ) :
    if file . strip ( ) == "" :
        continue
    file = file . replace ( "\\" , "/" )
    # add all parent directories to ' dirinfos '
    parents = file . split ( "/" )
    if len ( parents ) >= 1 :
        p1 = ""
        for i in range ( 0 , len ( parents ) - 1 ) :
            p2 = parents [ i ]
            dir = dirinfos . setdefault ( p1 , ( [ ] , [ ] ) )
            if p2 not in dir [ 0 ] :
                dir [ 0 ] . append ( p2 )
            if p1 == "" :
                p1 = p2
            else :
                p1 = "%s/%s" % ( p1 , p2 )
        dirinfos . setdefault ( p1 , ( [ ] , [ ] ) ) [ 1 ] . append ( parents [ - 1 ] )
    filedict [ file ] = True
files . sort ( )
cache = { "files" : files , "dirinfos" : dirinfos , "filedict" : filedict }
caches [ compat . to_native ( rev ) ] = cache
_logger . info ( "_getRepoInfo(%s) took %.3f" % ( rev , time . time ( ) - start_time ) )
return cache


signature:
def get_resource_inst ( self , path , environ ) :
    """Return HgResource object for path .
 See DAVProvider . get _ resource _ inst ( )"""

implementation:
self . _count_get_resource_inst += 1
# HG expects the resource paths without leading ' / '
localHgPath = path . strip ( "/" )
rev = None
cmd , rest = util . pop_path ( path )
if cmd == "" :
    return VirtualCollection ( path , environ , "root" , [ "edit" , "released" , "archive" ] )
elif cmd == "edit" :
    localHgPath = rest . strip ( "/" )
    rev = None
elif cmd == "released" :
    localHgPath = rest . strip ( "/" )
    rev = "tip"
elif cmd == "archive" :
    if rest == "/" : # Browse / archive : return a list of revision folders :
        loglist = self . _get_log ( limit = 10 )
        members = [ compat . to_native ( l [ "local_id" ] ) for l in loglist ]
        return VirtualCollection ( path , environ , "Revisions" , members )
    revid , rest = util . pop_path ( rest )
    try :
        int ( revid )
    except Exception : # Tried to access / archive / anyname
        return None
    # Access / archive / 19
    rev = revid
    localHgPath = rest . strip ( "/" )
else :
    return None
# read mercurial repo into request cache
cache = self . _get_repo_info ( environ , rev )
if localHgPath in cache [ "filedict" ] : # It is a version controlled file
    return HgResource ( path , False , environ , rev , localHgPath )
if localHgPath in cache [ "dirinfos" ] or localHgPath == "" : # It is an existing folder
    return HgResource ( path , True , environ , rev , localHgPath )
return None


signature:
def get_display_info ( self ) :
    """Return additional info dictionary for displaying ( optional ) .
 This information is not part of the DAV specification , but meant for use
 by the dir browser middleware .
 This default implementation returns ` ` { ' type ' : ' . . . ' } ` `"""

implementation:
if self . is_collection :
    return { "type" : "Directory" }
elif os . extsep in self . name :
    ext = self . name . split ( os . extsep ) [ - 1 ] . upper ( )
    if len ( ext ) < 5 :
        return { "type" : "{}-File" . format ( ext ) }
return { "type" : "File" }


signature:
def get_preferred_path ( self ) :
    """Return preferred mapping for a resource mapping .
 Different URLs may map to the same resource , e . g . :
 ' / a / b ' = = ' / A / b ' = = ' / a / b / '
 get _ preferred _ path ( ) returns the same value for all these variants , e . g . :
 ' / a / b / ' ( assuming resource names considered case insensitive )
 @ param path : a UTF - 8 encoded , unquoted byte string .
 @ return : a UTF - 8 encoded , unquoted byte string ."""

implementation:
if self . path in ( "" , "/" ) :
    return "/"
# Append ' / ' for collections
if self . is_collection and not self . path . endswith ( "/" ) :
    return self . path + "/"
# TODO : handle case - sensitivity , depending on OS
# ( FileSystemProvider could do this with os . path :
# ( ? ) on unix we can assume that the path already matches exactly the case of filepath
# on windows we could use path . lower ( ) or get the real case from the
# file system
return self . path


signature:
def get_href ( self ) :
    """Convert path to a URL that can be passed to XML responses .
 Byte string , UTF - 8 encoded , quoted .
 See http : / / www . webdav . org / specs / rfc4918 . html # rfc . section . 8.3
 We are using the path - absolute option . i . e . starting with ' / ' .
 URI ; See section 3.2.1 of [ RFC2068]"""

implementation:
# Nautilus chokes , if href encodes ' ( ' as ' % 28'
# So we don ' t encode ' extra ' and ' safe ' characters ( see rfc2068 3.2.1)
safe = "/" + "!*'()," + "$-_|."
return compat . quote ( self . provider . mount_path + self . provider . share_path + self . get_preferred_path ( ) , safe = safe , )


signature:
def get_member_list ( self ) :
    """Return a list of direct members ( _ DAVResource or derived objects ) .
 This default implementation calls self . get _ member _ names ( ) and
 self . get _ member ( ) for each of them .
 A provider COULD overwrite this for performance reasons ."""

implementation:
if not self . is_collection :
    raise NotImplementedError
memberList = [ ]
for name in self . get_member_names ( ) :
    member = self . get_member ( name )
    assert member is not None
    memberList . append ( member )
return memberList


signature:
def get_descendants ( self , collections = True , resources = True , depth_first = False , depth = "infinity" , add_self = False , ) :
    """Return a list _ DAVResource objects of a collection ( children ,
 grand - children , . . . ) .
 This default implementation calls self . get _ member _ list ( ) recursively .
 This function may also be called for non - collections ( with add _ self = True ) .
 : Parameters :
 depth _ first : bool
 use < False > , to list containers before content .
 ( e . g . when moving / copying branches . )
 Use < True > , to list content before containers .
 ( e . g . when deleting branches . )
 depth : string
 '0 ' | ' 1 ' | ' infinity '"""

implementation:
assert depth in ( "0" , "1" , "infinity" )
res = [ ]
if add_self and not depth_first :
    res . append ( self )
if depth != "0" and self . is_collection :
    for child in self . get_member_list ( ) :
        if not child :
            self . get_member_list ( )
        want = ( collections and child . is_collection ) or ( resources and not child . is_collection )
        if want and not depth_first :
            res . append ( child )
        if child . is_collection and depth == "infinity" :
            res . extend ( child . get_descendants ( collections , resources , depth_first , depth , add_self = False ) )
        if want and depth_first :
            res . append ( child )
if add_self and depth_first :
    res . append ( self )
return res


signature:
def get_property_names ( self , is_allprop ) :
    """Return list of supported property names in Clark Notation .
 Note that ' allprop ' , despite its name , which remains for
 backward - compatibility , does not return every property , but only dead
 properties and the live properties defined in RFC4918.
 This default implementation returns a combination of :
 - Supported standard live properties in the { DAV : } namespace , if the
 related getter method returns not None .
 - { DAV : } lockdiscovery and { DAV : } supportedlock , if a lock manager is
 present
 - If a property manager is present , then a list of dead properties is
 appended
 A resource provider may override this method , to add a list of
 supported custom live property names ."""

implementation:
# Live properties
propNameList = [ ]
propNameList . append ( "{DAV:}resourcetype" )
if self . get_creation_date ( ) is not None :
    propNameList . append ( "{DAV:}creationdate" )
if self . get_content_length ( ) is not None :
    assert not self . is_collection
    propNameList . append ( "{DAV:}getcontentlength" )
if self . get_content_type ( ) is not None :
    propNameList . append ( "{DAV:}getcontenttype" )
if self . get_last_modified ( ) is not None :
    propNameList . append ( "{DAV:}getlastmodified" )
if self . get_display_name ( ) is not None :
    propNameList . append ( "{DAV:}displayname" )
if self . get_etag ( ) is not None :
    propNameList . append ( "{DAV:}getetag" )
# Locking properties
if self . provider . lock_manager and not self . prevent_locking ( ) :
    propNameList . extend ( _lockPropertyNames )
# Dead properties
if self . provider . prop_manager :
    refUrl = self . get_ref_url ( )
    propNameList . extend ( self . provider . prop_manager . get_properties ( refUrl , self . environ ) )
return propNameList


signature:
def get_properties ( self , mode , name_list = None ) :
    """Return properties as list of 2 - tuples ( name , value ) .
 If mode is ' name ' , then None is returned for the value .
 name
 the property name in Clark notation .
 value
 may have different types , depending on the status :
 - string or unicode : for standard property values .
 - etree . Element : for complex values .
 - DAVError in case of errors .
 - None : if mode = = ' name ' .
 @ param mode : " allprop " , " name " , or " named "
 @ param name _ list : list of property names in Clark Notation ( required for mode ' named ' )
 This default implementation basically calls self . get _ property _ names ( ) to
 get the list of names , then call self . get _ property _ value on each of them ."""

implementation:
assert mode in ( "allprop" , "name" , "named" )
if mode in ( "allprop" , "name" ) : # TODO : ' allprop ' could have nameList , when < include > option is
# implemented
    assert name_list is None
    name_list = self . get_property_names ( mode == "allprop" )
else :
    assert name_list is not None
propList = [ ]
namesOnly = mode == "name"
for name in name_list :
    try :
        if namesOnly :
            propList . append ( ( name , None ) )
        else :
            value = self . get_property_value ( name )
            propList . append ( ( name , value ) )
    except DAVError as e :
        propList . append ( ( name , e ) )
    except Exception as e :
        propList . append ( ( name , as_DAVError ( e ) ) )
        if self . provider . verbose >= 2 :
            traceback . print_exc ( 10 , sys . stdout )
return propList


signature:
def get_property_value ( self , name ) :
    """Return the value of a property .
 name :
 the property name in Clark notation .
 return value :
 may have different types , depending on the status :
 - string or unicode : for standard property values .
 - lxml . etree . Element : for complex values .
 If the property is not available , a DAVError is raised .
 This default implementation handles ` ` { DAV : } lockdiscovery ` ` and
 ` ` { DAV : } supportedlock ` ` using the associated lock manager .
 All other * live * properties ( i . e . name starts with ` ` { DAV : } ` ` ) are
 delegated to the self . xxx ( ) getters .
 Finally , other properties are considered * dead * , and are handled by
 the associated property manager ."""

implementation:
refUrl = self . get_ref_url ( )
# lock properties
lm = self . provider . lock_manager
if lm and name == "{DAV:}lockdiscovery" : # TODO : we return HTTP _ NOT _ FOUND if no lockmanager is present .
# Correct ?
    activelocklist = lm . get_url_lock_list ( refUrl )
    lockdiscoveryEL = etree . Element ( name )
    for lock in activelocklist :
        activelockEL = etree . SubElement ( lockdiscoveryEL , "{DAV:}activelock" )
        locktypeEL = etree . SubElement ( activelockEL , "{DAV:}locktype" )
        # Note : make sure ` { DAV : } ` is not handled as format tag :
        etree . SubElement ( locktypeEL , "{}{}" . format ( "{DAV:}" , lock [ "type" ] ) )
        lockscopeEL = etree . SubElement ( activelockEL , "{DAV:}lockscope" )
        # Note : make sure ` { DAV : } ` is not handled as format tag :
        etree . SubElement ( lockscopeEL , "{}{}" . format ( "{DAV:}" , lock [ "scope" ] ) )
        etree . SubElement ( activelockEL , "{DAV:}depth" ) . text = lock [ "depth" ]
        if lock [ "owner" ] : # lock [ " owner " ] is an XML string
        # owner may be empty ( # 64)
            ownerEL = xml_tools . string_to_xml ( lock [ "owner" ] )
            activelockEL . append ( ownerEL )
        timeout = lock [ "timeout" ]
        if timeout < 0 :
            timeout = "Infinite"
        else : # The time remaining on the lock
            expire = lock [ "expire" ]
            timeout = "Second-" + str ( int ( expire - time . time ( ) ) )
        etree . SubElement ( activelockEL , "{DAV:}timeout" ) . text = timeout
        locktokenEL = etree . SubElement ( activelockEL , "{DAV:}locktoken" )
        etree . SubElement ( locktokenEL , "{DAV:}href" ) . text = lock [ "token" ]
        # TODO : this is ugly :
        # res . get _ property _ value ( " { DAV : } lockdiscovery " )
        # lockRoot = self . get _ href ( self . provider . ref _ url _ to _ path ( lock [ " root " ] ) )
        lockPath = self . provider . ref_url_to_path ( lock [ "root" ] )
        lockRes = self . provider . get_resource_inst ( lockPath , self . environ )
        # FIXME : test for None
        lockHref = lockRes . get_href ( )
        lockrootEL = etree . SubElement ( activelockEL , "{DAV:}lockroot" )
        etree . SubElement ( lockrootEL , "{DAV:}href" ) . text = lockHref
    return lockdiscoveryEL
elif lm and name == "{DAV:}supportedlock" : # TODO : we return HTTP _ NOT _ FOUND if no lockmanager is present . Correct ?
# TODO : the lockmanager should decide about it ' s features
    supportedlockEL = etree . Element ( name )
    lockentryEL = etree . SubElement ( supportedlockEL , "{DAV:}lockentry" )
    lockscopeEL = etree . SubElement ( lockentryEL , "{DAV:}lockscope" )
    etree . SubElement ( lockscopeEL , "{DAV:}exclusive" )
    locktypeEL = etree . SubElement ( lockentryEL , "{DAV:}locktype" )
    etree . SubElement ( locktypeEL , "{DAV:}write" )
    lockentryEL = etree . SubElement ( supportedlockEL , "{DAV:}lockentry" )
    lockscopeEL = etree . SubElement ( lockentryEL , "{DAV:}lockscope" )
    etree . SubElement ( lockscopeEL , "{DAV:}shared" )
    locktypeEL = etree . SubElement ( lockentryEL , "{DAV:}locktype" )
    etree . SubElement ( locktypeEL , "{DAV:}write" )
    return supportedlockEL
elif name . startswith ( "{DAV:}" ) : # Standard live property ( raises HTTP _ NOT _ FOUND if not supported )
    if name == "{DAV:}creationdate" and self . get_creation_date ( ) is not None : # Note : uses RFC3339 format ( ISO 8601)
        return util . get_rfc3339_time ( self . get_creation_date ( ) )
    elif name == "{DAV:}getcontenttype" and self . get_content_type ( ) is not None :
        return self . get_content_type ( )
    elif name == "{DAV:}resourcetype" :
        if self . is_collection :
            resourcetypeEL = etree . Element ( name )
            etree . SubElement ( resourcetypeEL , "{DAV:}collection" )
            return resourcetypeEL
        return ""
    elif ( name == "{DAV:}getlastmodified" and self . get_last_modified ( ) is not None ) : # Note : uses RFC1123 format
        return util . get_rfc1123_time ( self . get_last_modified ( ) )
    elif ( name == "{DAV:}getcontentlength" and self . get_content_length ( ) is not None ) : # Note : must be a numeric string
        return str ( self . get_content_length ( ) )
    elif name == "{DAV:}getetag" and self . get_etag ( ) is not None :
        return self . get_etag ( )
    elif name == "{DAV:}displayname" and self . get_display_name ( ) is not None :
        return self . get_display_name ( )
    # Unsupported , no persistence available , or property not found
    raise DAVError ( HTTP_NOT_FOUND )
# Dead property
pm = self . provider . prop_manager
if pm :
    value = pm . get_property ( refUrl , name , self . environ )
    if value is not None :
        return xml_tools . string_to_xml ( value )
# No persistence available , or property not found
raise DAVError ( HTTP_NOT_FOUND )


signature:
def set_property_value ( self , name , value , dry_run = False ) :
    """Set a property value or remove a property .
 value = = None means ' remove property ' .
 Raise HTTP _ FORBIDDEN if property is read - only , or not supported .
 When dry _ run is True , this function should raise errors , as in a real
 run , but MUST NOT change any data .
 This default implementation
 - raises HTTP _ FORBIDDEN , if trying to modify a locking property
 - raises HTTP _ FORBIDDEN , if trying to modify an immutable { DAV : }
 property
 - handles Windows ' Win32LastModifiedTime to set the getlastmodified
 property , if enabled
 - stores everything else as dead property , if a property manager is
 present .
 - raises HTTP _ FORBIDDEN , else
 Removing a non - existing prop is NOT an error .
 Note : RFC 4918 states that { DAV : } displayname ' SHOULD NOT be protected '
 A resource provider may override this method , to update supported custom
 live properties ."""

implementation:
assert value is None or xml_tools . is_etree_element ( value )
if name in _lockPropertyNames : # Locking properties are always read - only
    raise DAVError ( HTTP_FORBIDDEN , err_condition = PRECONDITION_CODE_ProtectedProperty )
# Live property
config = self . environ [ "wsgidav.config" ]
# hotfixes = config . get ( " hotfixes " , { } )
mutableLiveProps = config . get ( "mutable_live_props" , [ ] )
# Accept custom live property updates on resources if configured .
if ( name . startswith ( "{DAV:}" ) and name in _standardLivePropNames and name in mutableLiveProps ) : # Please note that some properties should not be mutable according
# to RFC4918 . This includes the ' getlastmodified ' property , which
# it may still make sense to make mutable in order to support time
# stamp changes from e . g . utime calls or the touch or rsync - a
# commands .
    if name in ( "{DAV:}getlastmodified" , "{DAV:}last_modified" ) :
        try :
            return self . set_last_modified ( self . path , value . text , dry_run )
        except Exception :
            _logger . warning ( "Provider does not support set_last_modified on {}." . format ( self . path ) )
    # Unsupported or not allowed
    raise DAVError ( HTTP_FORBIDDEN )
# Handle MS Windows Win32LastModifiedTime , if enabled .
# Note that the WebDAV client in Win7 and earler has issues and can ' t be used
# with this so we ignore older clients . Others pre - Win10 should be tested .
if name . startswith ( "{urn:schemas-microsoft-com:}" ) :
    agent = self . environ . get ( "HTTP_USER_AGENT" , "None" )
    win32_emu = config . get ( "hotfixes" , { } ) . get ( "emulate_win32_lastmod" , False )
    if win32_emu and "MiniRedir/6.1" not in agent :
        if "Win32LastModifiedTime" in name :
            return self . set_last_modified ( self . path , value . text , dry_run )
        elif "Win32FileAttributes" in name :
            return True
        elif "Win32CreationTime" in name :
            return True
        elif "Win32LastAccessTime" in name :
            return True
# Dead property
pm = self . provider . prop_manager
if pm and not name . startswith ( "{DAV:}" ) :
    refUrl = self . get_ref_url ( )
    if value is None :
        return pm . remove_property ( refUrl , name , dry_run , self . environ )
    else :
        value = etree . tostring ( value )
        return pm . write_property ( refUrl , name , value , dry_run , self . environ )
raise DAVError ( HTTP_FORBIDDEN )


signature:
def remove_all_properties ( self , recursive ) :
    """Remove all associated dead properties ."""

implementation:
if self . provider . prop_manager :
    self . provider . prop_manager . remove_properties ( self . get_ref_url ( ) , self . environ )


signature:
def is_locked ( self ) :
    """Return True , if URI is locked ."""

implementation:
if self . provider . lock_manager is None :
    return False
return self . provider . lock_manager . is_url_locked ( self . get_ref_url ( ) )


signature:
def resolve ( self , script_name , path_info ) :
    """Return a _ DAVResource object for the path ( None , if not found ) .
 ` path _ info ` : is a URL relative to this object ."""

implementation:
if path_info in ( "" , "/" ) :
    return self
assert path_info . startswith ( "/" )
name , rest = util . pop_path ( path_info )
res = self . get_member ( name )
if res is None or rest in ( "" , "/" ) :
    return res
return res . resolve ( util . join_uri ( script_name , name ) , rest )


signature:
def set_share_path ( self , share_path ) :
    """Set application location for this resource provider .
 @ param share _ path : a UTF - 8 encoded , unquoted byte string ."""

implementation:
# if isinstance ( share _ path , unicode ) :
# share _ path = share _ path . encode ( " utf8 " )
assert share_path == "" or share_path . startswith ( "/" )
if share_path == "/" :
    share_path = ""
    # This allows to code ' absPath = share _ path + path '
assert share_path in ( "" , "/" ) or not share_path . endswith ( "/" )
self . share_path = share_path


signature:
def ref_url_to_path ( self , ref_url ) :
    """Convert a refUrl to a path , by stripping the share prefix .
 Used to calculate the < path > from a storage key by inverting get _ ref _ url ( ) ."""

implementation: return "/" + compat . unquote ( util . lstripstr ( ref_url , self . share_path ) ) . lstrip ( "/" )


signature:
def is_collection ( self , path , environ ) :
    """Return True , if path maps to an existing collection resource .
 This method should only be used , if no other information is queried
 for < path > . Otherwise a _ DAVResource should be created first ."""

implementation:
res = self . get_resource_inst ( path , environ )
return res and res . is_collection


signature:
def string_to_xml ( text ) :
    """Convert XML string into etree . Element ."""

implementation:
try :
    return etree . XML ( text )
except Exception : # TODO :
# ExpatError : reference to invalid character number : line 1 , column 62
# litmus fails , when xml is used instead of lxml
# 18 . propget . . . . . FAIL ( PROPFIND on ` / temp / litmus / prop2 ' :
# Could not read status line : connection was closed by server )
# text = < ns0 : high - unicode xmlns : ns0 = " http : / / example . com / neon / litmus / " > & # 55296 ; & # 56320;
# < / ns0 : high - unicode >
# t2 = text . encode ( " utf8 " )
# return etree . XML ( t2)
    _logger . error ( "Error parsing XML string. " "If lxml is not available, and unicode is involved, then " "installing lxml _may_ solve this issue." )
    _logger . error ( "XML source: {}" . format ( text ) )
    raise


signature:
def xml_to_bytes ( element , pretty_print = False ) :
    """Wrapper for etree . tostring , that takes care of unsupported pretty _ print
 option and prepends an encoding header ."""

implementation:
if use_lxml :
    xml = etree . tostring ( element , encoding = "UTF-8" , xml_declaration = True , pretty_print = pretty_print )
else :
    xml = etree . tostring ( element , encoding = "UTF-8" )
    if not xml . startswith ( b"<?xml " ) :
        xml = b'<?xml version="1.0" encoding="utf-8" ?>\n' + xml
assert xml . startswith ( b"<?xml " )
# ET should prepend an encoding header
return xml


signature:
def make_sub_element ( parent , tag , nsmap = None ) :
    """Wrapper for etree . SubElement , that takes care of unsupported nsmap option ."""

implementation:
if use_lxml :
    return etree . SubElement ( parent , tag , nsmap = nsmap )
return etree . SubElement ( parent , tag )


signature:
def element_content_as_string ( element ) :
    """Serialize etree . Element .
 Note : element may contain more than one child or only text ( i . e . no child
 at all ) . Therefore the resulting string may raise an exception , when
 passed back to etree . XML ( ) ."""

implementation:
if len ( element ) == 0 :
    return element . text or ""
    # Make sure , None is returned as ' '
stream = compat . StringIO ( )
for childnode in element :
    stream . write ( xml_to_bytes ( childnode , pretty_print = False ) + "\n" )
    # print ( xml _ to _ bytes ( childnode , pretty _ print = False ) , file = stream )
s = stream . getvalue ( )
stream . close ( )
return s


signature:
def _get_checked_path ( path , config , must_exist = True , allow_none = True ) :
    """Convert path to absolute if not None ."""

implementation:
if path in ( None , "" ) :
    if allow_none :
        return None
    raise ValueError ( "Invalid path {!r}" . format ( path ) )
# Evaluate path relative to the folder of the config file ( if any )
config_file = config . get ( "_config_file" )
if config_file and not os . path . isabs ( path ) :
    path = os . path . normpath ( os . path . join ( os . path . dirname ( config_file ) , path ) )
else :
    path = os . path . abspath ( path )
if must_exist and not os . path . exists ( path ) :
    raise ValueError ( "Invalid path {!r}" . format ( path ) )
return path


signature:
def _init_command_line_options ( ) :
    """Parse command line options into a dictionary ."""

implementation:
description = """\

Run a WEBDAV server to share file system folders.

Examples:

  Share filesystem folder '/temp' for anonymous access (no config file used):
    wsgidav --port=80 --host=0.0.0.0 --root=/temp --auth=anonymous

  Run using a specific configuration file:
    wsgidav --port=80 --host=0.0.0.0 --config=~/my_wsgidav.yaml

  If no config file is specified, the application will look for a file named
  'wsgidav.yaml' in the current directory.
  See
    http://wsgidav.readthedocs.io/en/latest/run-configure.html
  for some explanation of the configuration file format.
  """
epilog = """\
Licensed under the MIT license.
See https://github.com/mar10/wsgidav for additional information.

"""
parser = argparse . ArgumentParser ( prog = "wsgidav" , description = description , epilog = epilog , # allow _ abbrev = False , # Py3.5 +
formatter_class = argparse . RawTextHelpFormatter , )
parser . add_argument ( "-p" , "--port" , dest = "port" , type = int , # default = 8080,
help = "port to serve on (default: 8080)" , )
parser . add_argument ( "-H" , # ' - h ' conflicts with - - help
"--host" , dest = "host" , help = ( "host to serve from (default: localhost). 'localhost' is only " "accessible from the local computer. Use 0.0.0.0 to make your " "application public" ) , ) ,
parser . add_argument ( "-r" , "--root" , dest = "root_path" , action = FullExpandedPath , help = "path to a file system folder to publish as share '/'." , )
parser . add_argument ( "--auth" , choices = ( "anonymous" , "nt" , "pam-login" ) , help = "quick configuration of a domain controller when no config file " "is used" , )
parser . add_argument ( "--server" , choices = SUPPORTED_SERVERS . keys ( ) , # default = " cheroot " ,
help = "type of pre-installed WSGI server to use (default: cheroot)." , )
parser . add_argument ( "--ssl-adapter" , choices = ( "builtin" , "pyopenssl" ) , # default = " builtin " ,
help = "used by 'cheroot' server if SSL certificates are configured " "(default: builtin)." , )
qv_group = parser . add_mutually_exclusive_group ( )
qv_group . add_argument ( "-v" , "--verbose" , action = "count" , default = 3 , help = "increment verbosity by one (default: %(default)s, range: 0..5)" , )
qv_group . add_argument ( "-q" , "--quiet" , default = 0 , action = "count" , help = "decrement verbosity by one" )
qv_group = parser . add_mutually_exclusive_group ( )
qv_group . add_argument ( "-c" , "--config" , dest = "config_file" , action = FullExpandedPath , help = ( "configuration file (default: {} in current directory)" . format ( DEFAULT_CONFIG_FILES ) ) , )
qv_group . add_argument ( "--no-config" , action = "store_true" , dest = "no_config" , help = "do not try to load default {}" . format ( DEFAULT_CONFIG_FILES ) , )
parser . add_argument ( "-V" , "--version" , action = "store_true" , help = "print version info and exit (may be combined with --verbose)" , )
args = parser . parse_args ( )
args . verbose -= args . quiet
del args . quiet
if args . root_path and not os . path . isdir ( args . root_path ) :
    msg = "{} is not a directory" . format ( args . root_path )
    raise parser . error ( msg )
if args . version :
    if args . verbose >= 4 :
        msg = "WsgiDAV/{} Python/{} {}" . format ( __version__ , util . PYTHON_VERSION , platform . platform ( aliased = True ) )
    else :
        msg = "{}" . format ( __version__ )
    print ( msg )
    sys . exit ( )
if args . no_config :
    pass
    # . . . else ignore default config files
elif args . config_file is None : # If - - config was omitted , use default ( if it exists )
    for filename in DEFAULT_CONFIG_FILES :
        defPath = os . path . abspath ( filename )
        if os . path . exists ( defPath ) :
            if args . verbose >= 3 :
                print ( "Using default configuration file: {}" . format ( defPath ) )
            args . config_file = defPath
            break
else : # If - - config was specified convert to absolute path and assert it exists
    args . config_file = os . path . abspath ( args . config_file )
    if not os . path . isfile ( args . config_file ) :
        parser . error ( "Could not find specified configuration file: {}" . format ( args . config_file ) )
# Convert args object to dictionary
cmdLineOpts = args . __dict__ . copy ( )
if args . verbose >= 5 :
    print ( "Command line args:" )
    for k , v in cmdLineOpts . items ( ) :
        print ( "    {:>12}: {}" . format ( k , v ) )
return cmdLineOpts , parser


signature:
def _read_config_file ( config_file , verbose ) :
    """Read configuration file options into a dictionary ."""

implementation:
config_file = os . path . abspath ( config_file )
if not os . path . exists ( config_file ) :
    raise RuntimeError ( "Couldn't open configuration file '{}'." . format ( config_file ) )
if config_file . endswith ( ".json" ) :
    with io . open ( config_file , mode = "r" , encoding = "utf-8" ) as json_file : # Minify the JSON file to strip embedded comments
        minified = jsmin ( json_file . read ( ) )
    conf = json . loads ( minified )
elif config_file . endswith ( ".yaml" ) :
    with io . open ( config_file , mode = "r" , encoding = "utf-8" ) as yaml_file :
        conf = yaml . safe_load ( yaml_file )
else :
    try :
        import imp
        conf = { }
        configmodule = imp . load_source ( "configuration_module" , config_file )
        for k , v in vars ( configmodule ) . items ( ) :
            if k . startswith ( "__" ) :
                continue
            elif isfunction ( v ) :
                continue
            conf [ k ] = v
    except Exception :
        exc_type , exc_value = sys . exc_info ( ) [ : 2 ]
        exc_info_list = traceback . format_exception_only ( exc_type , exc_value )
        exc_text = "\n" . join ( exc_info_list )
        print ( "Failed to read configuration file: " + config_file + "\nDue to " + exc_text , file = sys . stderr , )
        raise
conf [ "_config_file" ] = config_file
return conf


signature:
def _init_config ( ) :
    """Setup configuration dictionary from default , command line and configuration file ."""

implementation:
cli_opts , parser = _init_command_line_options ( )
cli_verbose = cli_opts [ "verbose" ]
# Set config defaults
config = copy . deepcopy ( DEFAULT_CONFIG )
# Configuration file overrides defaults
config_file = cli_opts . get ( "config_file" )
if config_file :
    file_opts = _read_config_file ( config_file , cli_verbose )
    util . deep_update ( config , file_opts )
    if cli_verbose != DEFAULT_VERBOSE and "verbose" in file_opts :
        if cli_verbose >= 2 :
            print ( "Config file defines 'verbose: {}' but is overridden by command line: {}." . format ( file_opts [ "verbose" ] , cli_verbose ) )
        config [ "verbose" ] = cli_verbose
else :
    if cli_verbose >= 2 :
        print ( "Running without configuration file." )
# Command line overrides file
if cli_opts . get ( "port" ) :
    config [ "port" ] = cli_opts . get ( "port" )
if cli_opts . get ( "host" ) :
    config [ "host" ] = cli_opts . get ( "host" )
if cli_opts . get ( "profile" ) is not None :
    config [ "profile" ] = True
if cli_opts . get ( "server" ) is not None :
    config [ "server" ] = cli_opts . get ( "server" )
if cli_opts . get ( "ssl_adapter" ) is not None :
    config [ "ssl_adapter" ] = cli_opts . get ( "ssl_adapter" )
# Command line overrides file only if - v or - q where passed :
if cli_opts . get ( "verbose" ) != DEFAULT_VERBOSE :
    config [ "verbose" ] = cli_opts . get ( "verbose" )
if cli_opts . get ( "root_path" ) :
    root_path = os . path . abspath ( cli_opts . get ( "root_path" ) )
    config [ "provider_mapping" ] [ "/" ] = FilesystemProvider ( root_path )
if config [ "verbose" ] >= 5 : # TODO : remove passwords from user _ mapping
# config _ cleaned = copy . deepcopy ( config )
    print ( "Configuration({}):\n{}" . format ( cli_opts [ "config_file" ] , pformat ( config ) ) )
if not config [ "provider_mapping" ] :
    parser . error ( "No DAV provider defined." )
# Quick - configuration of DomainController
auth = cli_opts . get ( "auth" )
auth_conf = config . get ( "http_authenticator" , { } )
if auth and auth_conf . get ( "domain_controller" ) :
    parser . error ( "--auth option can only be used when no domain_controller is configured" )
if auth == "anonymous" :
    if config [ "simple_dc" ] [ "user_mapping" ] :
        parser . error ( "--auth=anonymous can only be used when no user_mapping is configured" )
    auth_conf . update ( { "domain_controller" : "wsgidav.dc.simple_dc.SimpleDomainController" , "accept_basic" : True , "accept_digest" : True , "default_to_digest" : True , } )
    config [ "simple_dc" ] [ "user_mapping" ] = { "*" : True }
elif auth == "nt" :
    if config . get ( "nt_dc" ) :
        parser . error ( "--auth=nt can only be used when no nt_dc settings are configured" )
    auth_conf . update ( { "domain_controller" : "wsgidav.dc.nt_dc.NTDomainController" , "accept_basic" : True , "accept_digest" : False , "default_to_digest" : False , } )
    config [ "nt_dc" ] = { }
elif auth == "pam-login" :
    if config . get ( "pam_dc" ) :
        parser . error ( "--auth=pam-login can only be used when no pam_dc settings are configured" )
    auth_conf . update ( { "domain_controller" : "wsgidav.dc.pam_dc.PAMDomainController" , "accept_basic" : True , "accept_digest" : False , "default_to_digest" : False , } )
    config [ "pam_dc" ] = { "service" : "login" }
# print ( config )
if cli_opts . get ( "reload" ) :
    print ( "Installing paste.reloader." , file = sys . stderr )
    from paste import reloader
    # @ UnresolvedImport
    reloader . install ( )
    if config_file : # Add config file changes
        reloader . watch_file ( config_file )
    # import pydevd
    # pydevd . settrace ( )
return config


signature:
def _run_paste ( app , config , mode ) :
    """Run WsgiDAV using paste . httpserver , if Paste is installed .
 See http : / / pythonpaste . org / modules / httpserver . html for more options"""

implementation:
from paste import httpserver
version = "WsgiDAV/{} {} Python {}" . format ( __version__ , httpserver . WSGIHandler . server_version , util . PYTHON_VERSION )
_logger . info ( "Running {}..." . format ( version ) )
# See http : / / pythonpaste . org / modules / httpserver . html for more options
server = httpserver . serve ( app , host = config [ "host" ] , port = config [ "port" ] , server_version = version , # This option enables handling of keep - alive
# and expect - 100:
protocol_version = "HTTP/1.1" , start_loop = False , )
if config [ "verbose" ] >= 5 :
    __handle_one_request = server . RequestHandlerClass . handle_one_request
    def handle_one_request ( self ) :
        __handle_one_request ( self )
        if self . close_connection == 1 :
            _logger . debug ( "HTTP Connection : close" )
        else :
            _logger . debug ( "HTTP Connection : continue" )
    server . RequestHandlerClass . handle_one_request = handle_one_request
    # _ _ handle = server . RequestHandlerClass . handle
    # def handle ( self ) :
    # _ logger . debug ( " open HTTP connection " )
    # _ _ handle ( self )
    server . RequestHandlerClass . handle_one_request = handle_one_request
host , port = server . server_address
if host == "0.0.0.0" :
    _logger . info ( "Serving on 0.0.0.0:{} view at {}://127.0.0.1:{}" . format ( port , "http" , port ) )
else :
    _logger . info ( "Serving on {}://{}:{}" . format ( "http" , host , port ) )
try :
    server . serve_forever ( )
except KeyboardInterrupt :
    _logger . warning ( "Caught Ctrl-C, shutting down..." )
return


signature:
def _run_gevent ( app , config , mode ) :
    """Run WsgiDAV using gevent if gevent is installed .
 See
 https : / / github . com / gevent / gevent / blob / master / src / gevent / pywsgi . py # L1356
 https : / / github . com / gevent / gevent / blob / master / src / gevent / server . py # L38
 for more options"""

implementation:
import gevent
import gevent . monkey
gevent . monkey . patch_all ( )
from gevent . pywsgi import WSGIServer
server_args = { "bind_addr" : ( config [ "host" ] , config [ "port" ] ) , "wsgi_app" : app , # TODO : SSL support
"keyfile" : None , "certfile" : None , }
protocol = "http"
# Override or add custom args
server_args . update ( config . get ( "server_args" , { } ) )
dav_server = WSGIServer ( server_args [ "bind_addr" ] , app )
_logger . info ( "Running {}" . format ( dav_server ) )
_logger . info ( "Serving on {}://{}:{} ..." . format ( protocol , config [ "host" ] , config [ "port" ] ) )
try :
    gevent . spawn ( dav_server . serve_forever ( ) )
except KeyboardInterrupt :
    _logger . warning ( "Caught Ctrl-C, shutting down..." )
return


signature:
def _run__cherrypy ( app , config , mode ) :
    """Run WsgiDAV using cherrypy . wsgiserver if CherryPy is installed ."""

implementation:
assert mode == "cherrypy-wsgiserver"
try :
    from cherrypy import wsgiserver
    from cherrypy . wsgiserver . ssl_builtin import BuiltinSSLAdapter
    _logger . warning ( "WARNING: cherrypy.wsgiserver is deprecated." )
    _logger . warning ( "         Starting with CherryPy 9.0 the functionality from cherrypy.wsgiserver" )
    _logger . warning ( "         was moved to the cheroot project." )
    _logger . warning ( "         Consider using --server=cheroot." )
except ImportError :
    _logger . error ( "*" * 78 )
    _logger . error ( "ERROR: Could not import cherrypy.wsgiserver." )
    _logger . error ( "Try `pip install cherrypy` or specify another server using the --server option." )
    _logger . error ( "Note that starting with CherryPy 9.0, the server was moved to" )
    _logger . error ( "the cheroot project, so it is recommended to use `-server=cheroot`" )
    _logger . error ( "and run `pip install cheroot` instead." )
    _logger . error ( "*" * 78 )
    raise
server_name = "WsgiDAV/{} {} Python/{}" . format ( __version__ , wsgiserver . CherryPyWSGIServer . version , util . PYTHON_VERSION )
wsgiserver . CherryPyWSGIServer . version = server_name
# Support SSL
ssl_certificate = _get_checked_path ( config . get ( "ssl_certificate" ) , config )
ssl_private_key = _get_checked_path ( config . get ( "ssl_private_key" ) , config )
ssl_certificate_chain = _get_checked_path ( config . get ( "ssl_certificate_chain" ) , config )
protocol = "http"
if ssl_certificate :
    assert ssl_private_key
    wsgiserver . CherryPyWSGIServer . ssl_adapter = BuiltinSSLAdapter ( ssl_certificate , ssl_private_key , ssl_certificate_chain )
    protocol = "https"
    _logger . info ( "SSL / HTTPS enabled." )
_logger . info ( "Running {}" . format ( server_name ) )
_logger . info ( "Serving on {}://{}:{} ..." . format ( protocol , config [ "host" ] , config [ "port" ] ) )
server_args = { "bind_addr" : ( config [ "host" ] , config [ "port" ] ) , "wsgi_app" : app , "server_name" : server_name , }
# Override or add custom args
server_args . update ( config . get ( "server_args" , { } ) )
server = wsgiserver . CherryPyWSGIServer ( ** server_args )
# If the caller passed a startup event , monkey patch the server to set it
# when the request handler loop is entered
startup_event = config . get ( "startup_event" )
if startup_event :
    def _patched_tick ( ) :
        server . tick = org_tick
        # undo the monkey patch
        org_tick ( )
        _logger . info ( "CherryPyWSGIServer is ready" )
        startup_event . set ( )
    org_tick = server . tick
    server . tick = _patched_tick
try :
    server . start ( )
except KeyboardInterrupt :
    _logger . warning ( "Caught Ctrl-C, shutting down..." )
finally :
    server . stop ( )
return


signature:
def _run_cheroot ( app , config , mode ) :
    """Run WsgiDAV using cheroot . server if Cheroot is installed ."""

implementation:
assert mode == "cheroot"
try :
    from cheroot import server , wsgi
# from cheroot . ssl . builtin import BuiltinSSLAdapter
# import cheroot . ssl . pyopenssl
except ImportError :
    _logger . error ( "*" * 78 )
    _logger . error ( "ERROR: Could not import Cheroot." )
    _logger . error ( "Try `pip install cheroot` or specify another server using the --server option." )
    _logger . error ( "*" * 78 )
    raise
server_name = "WsgiDAV/{} {} Python/{}" . format ( __version__ , wsgi . Server . version , util . PYTHON_VERSION )
wsgi . Server . version = server_name
# Support SSL
ssl_certificate = _get_checked_path ( config . get ( "ssl_certificate" ) , config )
ssl_private_key = _get_checked_path ( config . get ( "ssl_private_key" ) , config )
ssl_certificate_chain = _get_checked_path ( config . get ( "ssl_certificate_chain" ) , config )
ssl_adapter = config . get ( "ssl_adapter" , "builtin" )
protocol = "http"
if ssl_certificate and ssl_private_key :
    ssl_adapter = server . get_ssl_adapter_class ( ssl_adapter )
    wsgi . Server . ssl_adapter = ssl_adapter ( ssl_certificate , ssl_private_key , ssl_certificate_chain )
    protocol = "https"
    _logger . info ( "SSL / HTTPS enabled. Adapter: {}" . format ( ssl_adapter ) )
elif ssl_certificate or ssl_private_key :
    raise RuntimeError ( "Option 'ssl_certificate' and 'ssl_private_key' must be used together." )
# elif ssl _ adapter :
# print ( " WARNING : Ignored option ' ssl _ adapter ' ( requires ' ssl _ certificate ' ) . " )
_logger . info ( "Running {}" . format ( server_name ) )
_logger . info ( "Serving on {}://{}:{} ..." . format ( protocol , config [ "host" ] , config [ "port" ] ) )
server_args = { "bind_addr" : ( config [ "host" ] , config [ "port" ] ) , "wsgi_app" : app , "server_name" : server_name , }
# Override or add custom args
server_args . update ( config . get ( "server_args" , { } ) )
server = wsgi . Server ( ** server_args )
# If the caller passed a startup event , monkey patch the server to set it
# when the request handler loop is entered
startup_event = config . get ( "startup_event" )
if startup_event :
    def _patched_tick ( ) :
        server . tick = org_tick
        # undo the monkey patch
        _logger . info ( "wsgi.Server is ready" )
        startup_event . set ( )
        org_tick ( )
    org_tick = server . tick
    server . tick = _patched_tick
try :
    server . start ( )
except KeyboardInterrupt :
    _logger . warning ( "Caught Ctrl-C, shutting down..." )
finally :
    server . stop ( )
return


signature:
def _run_flup ( app , config , mode ) :
    """Run WsgiDAV using flup . server . fcgi if Flup is installed ."""

implementation:
# http : / / trac . saddi . com / flup / wiki / FlupServers
if mode == "flup-fcgi" :
    from flup . server . fcgi import WSGIServer , __version__ as flupver
elif mode == "flup-fcgi-fork" :
    from flup . server . fcgi_fork import WSGIServer , __version__ as flupver
else :
    raise ValueError
_logger . info ( "Running WsgiDAV/{} {}/{}..." . format ( __version__ , WSGIServer . __module__ , flupver ) )
server = WSGIServer ( app , bindAddress = ( config [ "host" ] , config [ "port" ] ) , # debug = True ,
)
try :
    server . run ( )
except KeyboardInterrupt :
    _logger . warning ( "Caught Ctrl-C, shutting down..." )
return


signature:
def _run_wsgiref ( app , config , mode ) :
    """Run WsgiDAV using wsgiref . simple _ server , on Python 2.5 + ."""

implementation:
# http : / / www . python . org / doc / 2.5.2 / lib / module - wsgiref . html
from wsgiref . simple_server import make_server , software_version
version = "WsgiDAV/{} {}" . format ( __version__ , software_version )
_logger . info ( "Running {}..." . format ( version ) )
_logger . warning ( "WARNING: This single threaded server (wsgiref) is not meant for production." )
httpd = make_server ( config [ "host" ] , config [ "port" ] , app )
try :
    httpd . serve_forever ( )
except KeyboardInterrupt :
    _logger . warning ( "Caught Ctrl-C, shutting down..." )
return


signature:
def _run_ext_wsgiutils ( app , config , mode ) :
    """Run WsgiDAV using ext _ wsgiutils _ server from the wsgidav package ."""

implementation:
from wsgidav . server import ext_wsgiutils_server
_logger . info ( "Running WsgiDAV {} on wsgidav.ext_wsgiutils_server..." . format ( __version__ ) )
_logger . warning ( "WARNING: This single threaded server (ext-wsgiutils) is not meant for production." )
try :
    ext_wsgiutils_server . serve ( config , app )
except KeyboardInterrupt :
    _logger . warning ( "Caught Ctrl-C, shutting down..." )
return


signature:
def _fail ( self , value , context_info = None , src_exception = None , err_condition = None ) :
    """Wrapper to raise ( and log ) DAVError ."""

implementation: util . fail ( value , context_info , src_exception , err_condition )


signature:
def _send_response ( self , environ , start_response , root_res , success_code , error_list ) :
    """Send WSGI response ( single or multistatus ) .
 - If error _ list is None or [ ] , then < success _ code > is send as response .
 - If error _ list contains a single error with a URL that matches root _ res ,
 then this error is returned .
 - If error _ list contains more than one error , then ' 207 Multi - Status ' is
 returned ."""

implementation:
assert success_code in ( HTTP_CREATED , HTTP_NO_CONTENT , HTTP_OK )
if not error_list : # Status OK
    return util . send_status_response ( environ , start_response , success_code )
if len ( error_list ) == 1 and error_list [ 0 ] [ 0 ] == root_res . get_href ( ) : # Only one error that occurred on the root resource
    return util . send_status_response ( environ , start_response , error_list [ 0 ] [ 1 ] )
# Multiple errors , or error on one single child
multistatusEL = xml_tools . make_multistatus_el ( )
for refurl , e in error_list : # assert refurl . startswith ( " http : " )
    assert refurl . startswith ( "/" )
    assert isinstance ( e , DAVError )
    responseEL = etree . SubElement ( multistatusEL , "{DAV:}response" )
    etree . SubElement ( responseEL , "{DAV:}href" ) . text = refurl
    etree . SubElement ( responseEL , "{DAV:}status" ) . text = "HTTP/1.1 {}" . format ( get_http_status_string ( e ) )
return util . send_multi_status_response ( environ , start_response , multistatusEL )


signature:
def _check_write_permission ( self , res , depth , environ ) :
    """Raise DAVError ( HTTP _ LOCKED ) , if res is locked .
 If depth = = ' infinity ' , we also raise when child resources are locked ."""

implementation:
lockMan = self . _davProvider . lock_manager
if lockMan is None or res is None :
    return True
refUrl = res . get_ref_url ( )
if "wsgidav.conditions.if" not in environ :
    util . parse_if_header_dict ( environ )
# raise HTTP _ LOCKED if conflict exists
lockMan . check_write_permission ( refUrl , depth , environ [ "wsgidav.ifLockTokenList" ] , environ [ "wsgidav.user_name" ] , )


signature:
def _evaluate_if_headers ( self , res , environ ) :
    """Apply HTTP headers on < path > , raising DAVError if conditions fail .
 Add environ [ ' wsgidav . conditions . if ' ] and environ [ ' wsgidav . ifLockTokenList ' ] .
 Handle these headers :
 - If - Match , If - Modified - Since , If - None - Match , If - Unmodified - Since :
 Raising HTTP _ PRECONDITION _ FAILED or HTTP _ NOT _ MODIFIED
 - If :
 Raising HTTP _ PRECONDITION _ FAILED
 @ see http : / / www . webdav . org / specs / rfc4918 . html # HEADER _ If
 @ see util . evaluate _ http _ conditionals"""

implementation:
# Add parsed If header to environ
if "wsgidav.conditions.if" not in environ :
    util . parse_if_header_dict ( environ )
# Bail out , if res does not exist
if res is None :
    return
ifDict = environ [ "wsgidav.conditions.if" ]
# Raise HTTP _ PRECONDITION _ FAILED or HTTP _ NOT _ MODIFIED , if standard
# HTTP condition fails
last_modified = - 1
# nonvalid modified time
entitytag = "[]"
# Non - valid entity tag
if res . get_last_modified ( ) is not None :
    last_modified = res . get_last_modified ( )
if res . get_etag ( ) is not None :
    entitytag = res . get_etag ( )
if ( "HTTP_IF_MODIFIED_SINCE" in environ or "HTTP_IF_UNMODIFIED_SINCE" in environ or "HTTP_IF_MATCH" in environ or "HTTP_IF_NONE_MATCH" in environ ) :
    util . evaluate_http_conditionals ( res , last_modified , entitytag , environ )
if "HTTP_IF" not in environ :
    return
# Raise HTTP _ PRECONDITION _ FAILED , if DAV ' If ' condition fails
# TODO : handle empty locked resources
# TODO : handle unmapped locked resources
# isnewfile = not provider . exists ( mappedpath )
refUrl = res . get_ref_url ( )
lockMan = self . _davProvider . lock_manager
locktokenlist = [ ]
if lockMan :
    lockList = lockMan . get_indirect_url_lock_list ( refUrl , environ [ "wsgidav.user_name" ] )
    for lock in lockList :
        locktokenlist . append ( lock [ "token" ] )
if not util . test_if_header_dict ( res , ifDict , refUrl , locktokenlist , entitytag ) :
    self . _fail ( HTTP_PRECONDITION_FAILED , "'If' header condition failed." )
return


signature:
def do_PROPFIND ( self , environ , start_response ) :
    """TODO : does not yet support If and If HTTP Conditions
 @ see http : / / www . webdav . org / specs / rfc4918 . html # METHOD _ PROPFIND"""

implementation:
path = environ [ "PATH_INFO" ]
res = self . _davProvider . get_resource_inst ( path , environ )
# RFC : By default , the PROPFIND method without a Depth header MUST act
# as if a " Depth : infinity " header was included .
environ . setdefault ( "HTTP_DEPTH" , "infinity" )
if not environ [ "HTTP_DEPTH" ] in ( "0" , "1" , "infinity" ) :
    self . _fail ( HTTP_BAD_REQUEST , "Invalid Depth header: '{}'." . format ( environ [ "HTTP_DEPTH" ] ) , )
if environ [ "HTTP_DEPTH" ] == "infinity" and not self . allow_propfind_infinite :
    self . _fail ( HTTP_FORBIDDEN , "PROPFIND 'infinite' was disabled for security reasons." , err_condition = PRECONDITION_CODE_PropfindFiniteDepth , )
if res is None :
    self . _fail ( HTTP_NOT_FOUND )
if environ . get ( "wsgidav.debug_break" ) :
    pass
# break point
self . _evaluate_if_headers ( res , environ )
# Parse PROPFIND request
requestEL = util . parse_xml_body ( environ , allow_empty = True )
if requestEL is None : # An empty PROPFIND request body MUST be treated as a request for
# the names and values of all properties .
    requestEL = etree . XML ( "<D:propfind xmlns:D='DAV:'><D:allprop/></D:propfind>" )
if requestEL . tag != "{DAV:}propfind" :
    self . _fail ( HTTP_BAD_REQUEST )
propNameList = [ ]
propFindMode = None
for pfnode in requestEL :
    if pfnode . tag == "{DAV:}allprop" :
        if propFindMode : # RFC : allprop and name are mutually exclusive
            self . _fail ( HTTP_BAD_REQUEST )
        propFindMode = "allprop"
    # TODO : implement < include > option
    # elif pfnode . tag = = " { DAV : } include " :
    # if not propFindMode in ( None , " allprop " ) :
    # self . _ fail ( HTTP _ BAD _ REQUEST ,
    # " < include > element is only valid with ' allprop ' . " )
    # for pfpnode in pfnode :
    # propNameList . append ( pfpnode . tag )
    elif pfnode . tag == "{DAV:}name" :
        if propFindMode : # RFC : allprop and name are mutually exclusive
            self . _fail ( HTTP_BAD_REQUEST )
        propFindMode = "name"
    elif pfnode . tag == "{DAV:}prop" : # RFC : allprop and name are mutually exclusive
        if propFindMode not in ( None , "named" ) :
            self . _fail ( HTTP_BAD_REQUEST )
        propFindMode = "named"
        for pfpnode in pfnode :
            propNameList . append ( pfpnode . tag )
# - - - Build list of resource URIs
reslist = res . get_descendants ( depth = environ [ "HTTP_DEPTH" ] , add_self = True )
# if environ [ " wsgidav . verbose " ] > = 3:
# pprint ( reslist , indent = 4)
multistatusEL = xml_tools . make_multistatus_el ( )
responsedescription = [ ]
for child in reslist :
    if propFindMode == "allprop" :
        propList = child . get_properties ( "allprop" )
    elif propFindMode == "name" :
        propList = child . get_properties ( "name" )
    else :
        propList = child . get_properties ( "named" , name_list = propNameList )
    href = child . get_href ( )
    util . add_property_response ( multistatusEL , href , propList )
if responsedescription :
    etree . SubElement ( multistatusEL , "{DAV:}responsedescription" ) . text = "\n" . join ( responsedescription )
return util . send_multi_status_response ( environ , start_response , multistatusEL )


signature:
def do_PROPPATCH ( self , environ , start_response ) :
    """Handle PROPPATCH request to set or remove a property .
 @ see http : / / www . webdav . org / specs / rfc4918 . html # METHOD _ PROPPATCH"""

implementation:
path = environ [ "PATH_INFO" ]
res = self . _davProvider . get_resource_inst ( path , environ )
# Only accept Depth : 0 ( but assume this , if omitted )
environ . setdefault ( "HTTP_DEPTH" , "0" )
if environ [ "HTTP_DEPTH" ] != "0" :
    self . _fail ( HTTP_BAD_REQUEST , "Depth must be '0'." )
if res is None :
    self . _fail ( HTTP_NOT_FOUND )
self . _evaluate_if_headers ( res , environ )
self . _check_write_permission ( res , "0" , environ )
# Parse request
requestEL = util . parse_xml_body ( environ )
if requestEL . tag != "{DAV:}propertyupdate" :
    self . _fail ( HTTP_BAD_REQUEST )
# Create a list of update request tuples : ( name , value )
propupdatelist = [ ]
for ppnode in requestEL :
    propupdatemethod = None
    if ppnode . tag == "{DAV:}remove" :
        propupdatemethod = "remove"
    elif ppnode . tag == "{DAV:}set" :
        propupdatemethod = "set"
    else :
        self . _fail ( HTTP_BAD_REQUEST , "Unknown tag (expected 'set' or 'remove')." )
    for propnode in ppnode :
        if propnode . tag != "{DAV:}prop" :
            self . _fail ( HTTP_BAD_REQUEST , "Unknown tag (expected 'prop')." )
        for propertynode in propnode :
            propvalue = None
            if propupdatemethod == "remove" :
                propvalue = None
                # Mark as ' remove '
                if len ( propertynode ) > 0 : # 14.23 : All the XML elements in a ' prop ' XML
                # element inside of a ' remove ' XML element MUST be
                # empty
                    self . _fail ( HTTP_BAD_REQUEST , "prop element must be empty for 'remove'." , )
            else :
                propvalue = propertynode
            propupdatelist . append ( ( propertynode . tag , propvalue ) )
# Apply updates in SIMULATION MODE and create a result list ( name ,
# result )
successflag = True
writeresultlist = [ ]
for ( name , propvalue ) in propupdatelist :
    try :
        res . set_property_value ( name , propvalue , dry_run = True )
    except Exception as e :
        writeresult = as_DAVError ( e )
    else :
        writeresult = "200 OK"
    writeresultlist . append ( ( name , writeresult ) )
    successflag = successflag and writeresult == "200 OK"
# Generate response list of 2 - tuples ( name , value )
# < value > is None on success , or an instance of DAVError
propResponseList = [ ]
responsedescription = [ ]
if not successflag : # If dry run failed : convert all OK to FAILED _ DEPENDENCY .
    for ( name , result ) in writeresultlist :
        if result == "200 OK" :
            result = DAVError ( HTTP_FAILED_DEPENDENCY )
        elif isinstance ( result , DAVError ) :
            responsedescription . append ( result . get_user_info ( ) )
        propResponseList . append ( ( name , result ) )
else : # Dry - run succeeded : set properties again , this time in ' real ' mode
# In theory , there should be no exceptions thrown here , but this is
# real live . . .
    for ( name , propvalue ) in propupdatelist :
        try :
            res . set_property_value ( name , propvalue , dry_run = False )
            # Set value to None , so the response xml contains empty tags
            propResponseList . append ( ( name , None ) )
        except Exception as e :
            e = as_DAVError ( e )
            propResponseList . append ( ( name , e ) )
            responsedescription . append ( e . get_user_info ( ) )
# Generate response XML
multistatusEL = xml_tools . make_multistatus_el ( )
href = res . get_href ( )
util . add_property_response ( multistatusEL , href , propResponseList )
if responsedescription :
    etree . SubElement ( multistatusEL , "{DAV:}responsedescription" ) . text = "\n" . join ( responsedescription )
# Send response
return util . send_multi_status_response ( environ , start_response , multistatusEL )


signature:
def do_MKCOL ( self , environ , start_response ) :
    """Handle MKCOL request to create a new collection .
 @ see http : / / www . webdav . org / specs / rfc4918 . html # METHOD _ MKCOL"""

implementation:
path = environ [ "PATH_INFO" ]
provider = self . _davProvider
# res = provider . get _ resource _ inst ( path , environ )
# Do not understand ANY request body entities
if util . get_content_length ( environ ) != 0 :
    self . _fail ( HTTP_MEDIATYPE_NOT_SUPPORTED , "The server does not handle any body content." , )
# Only accept Depth : 0 ( but assume this , if omitted )
if environ . setdefault ( "HTTP_DEPTH" , "0" ) != "0" :
    self . _fail ( HTTP_BAD_REQUEST , "Depth must be '0'." )
if provider . exists ( path , environ ) :
    self . _fail ( HTTP_METHOD_NOT_ALLOWED , "MKCOL can only be executed on an unmapped URL." , )
parentRes = provider . get_resource_inst ( util . get_uri_parent ( path ) , environ )
if not parentRes or not parentRes . is_collection :
    self . _fail ( HTTP_CONFLICT , "Parent must be an existing collection." )
# TODO : should we check If headers here ?
# self . _ evaluate _ if _ headers ( res , environ )
# Check for write permissions on the PARENT
self . _check_write_permission ( parentRes , "0" , environ )
parentRes . create_collection ( util . get_uri_name ( path ) )
return util . send_status_response ( environ , start_response , HTTP_CREATED )


signature:
def _stream_data_chunked ( self , environ , block_size ) :
    """Get the data from a chunked transfer ."""

implementation:
# Chunked Transfer Coding
# http : / / www . servlets . com / rfcs / rfc2616 - sec3 . html # sec3.6.1
if "Darwin" in environ . get ( "HTTP_USER_AGENT" , "" ) and environ . get ( "HTTP_X_EXPECTED_ENTITY_LENGTH" ) : # Mac Finder , that does not prepend chunk - size + CRLF ,
# like it should to comply with the spec . It sends chunk
# size as integer in a HTTP header instead .
    WORKAROUND_CHUNK_LENGTH = True
    buf = environ . get ( "HTTP_X_EXPECTED_ENTITY_LENGTH" , "0" )
    length = int ( buf )
else :
    WORKAROUND_CHUNK_LENGTH = False
    buf = environ [ "wsgi.input" ] . readline ( )
    environ [ "wsgidav.some_input_read" ] = 1
    if buf == compat . b_empty :
        length = 0
    else :
        length = int ( buf , 16 )
while length > 0 :
    buf = environ [ "wsgi.input" ] . read ( block_size )
    yield buf
    if WORKAROUND_CHUNK_LENGTH :
        environ [ "wsgidav.some_input_read" ] = 1
        # Keep receiving until we read expected size or reach
        # EOF
        if buf == compat . b_empty :
            length = 0
        else :
            length -= len ( buf )
    else :
        environ [ "wsgi.input" ] . readline ( )
        buf = environ [ "wsgi.input" ] . readline ( )
        if buf == compat . b_empty :
            length = 0
        else :
            length = int ( buf , 16 )
environ [ "wsgidav.all_input_read" ] = 1


signature:
def _stream_data ( self , environ , content_length , block_size ) :
    """Get the data from a non - chunked transfer ."""

implementation:
if content_length == 0 : # TODO : review this
# XP and Vista MiniRedir submit PUT with Content - Length 0,
# before LOCK and the real PUT . So we have to accept this .
    _logger . info ( "PUT: Content-Length == 0. Creating empty file..." )
# elif content _ length < 0:
# # TODO : review this
# # If CONTENT _ LENGTH is invalid , we may try to workaround this
# # by reading until the end of the stream . This may block however !
# # The iterator produced small chunks of varying size , but not
# # sure , if we always get everything before it times out .
# _ logger . warning ( " PUT with invalid Content - Length ( % s ) . "
# " Trying to read all ( this may timeout ) . . . "
# . format ( environ . get ( " CONTENT _ LENGTH " ) ) )
# nb = 0
# try :
# for s in environ [ " wsgi . input " ] :
# environ [ " wsgidav . some _ input _ read " ] = 1
# _ logger . debug ( " PUT : read from wsgi . input . _ _ iter _ _ , len = % s " % len ( s ) )
# yield s
# nb + = len ( s )
# except socket . timeout :
# _ logger . warning ( " PUT : input timed out after writing % s bytes " % nb )
# hasErrors = True
else :
    assert content_length > 0
    contentremain = content_length
    while contentremain > 0 :
        n = min ( contentremain , block_size )
        readbuffer = environ [ "wsgi.input" ] . read ( n )
        # This happens with litmus expect - 100 test :
        if not len ( readbuffer ) > 0 :
            _logger . error ( "input.read({}) returned 0 bytes" . format ( n ) )
            break
        environ [ "wsgidav.some_input_read" ] = 1
        yield readbuffer
        contentremain -= len ( readbuffer )
    if contentremain == 0 :
        environ [ "wsgidav.all_input_read" ] = 1


signature:
def _send_resource ( self , environ , start_response , is_head_method ) :
    """If - Range
 If the entity is unchanged , send me the part ( s ) that I am missing ;
 otherwise , send me the entire new entity
 If - Range : " 737060cd8c284d8af7ad3082f209582d "
 @ see : http : / / www . w3 . org / Protocols / rfc2616 / rfc2616 - sec14 . html # sec14.27"""

implementation:
path = environ [ "PATH_INFO" ]
res = self . _davProvider . get_resource_inst ( path , environ )
if util . get_content_length ( environ ) != 0 :
    self . _fail ( HTTP_MEDIATYPE_NOT_SUPPORTED , "The server does not handle any body content." , )
elif environ . setdefault ( "HTTP_DEPTH" , "0" ) != "0" :
    self . _fail ( HTTP_BAD_REQUEST , "Only Depth: 0 supported." )
elif res is None :
    self . _fail ( HTTP_NOT_FOUND )
elif res . is_collection :
    self . _fail ( HTTP_FORBIDDEN , "Directory browsing is not enabled." "(to enable it put WsgiDavDirBrowser into middleware_stack" "option and set dir_browser -> enabled = True option.)" , )
self . _evaluate_if_headers ( res , environ )
filesize = res . get_content_length ( )
if filesize is None :
    filesize = - 1
# flag logic to read until EOF
last_modified = res . get_last_modified ( )
if last_modified is None :
    last_modified = - 1
entitytag = res . get_etag ( )
if entitytag is None :
    entitytag = "[]"
# Ranges
doignoreranges = ( not res . support_content_length ( ) or not res . support_ranges ( ) or filesize == 0 )
if ( "HTTP_RANGE" in environ and "HTTP_IF_RANGE" in environ and not doignoreranges ) :
    ifrange = environ [ "HTTP_IF_RANGE" ]
    # Try as http - date first ( Return None , if invalid date string )
    secstime = util . parse_time_string ( ifrange )
    if secstime :
        if last_modified != secstime :
            doignoreranges = True
    else : # Use as entity tag
        ifrange = ifrange . strip ( '" ' )
        if entitytag is None or ifrange != entitytag :
            doignoreranges = True
ispartialranges = False
if "HTTP_RANGE" in environ and not doignoreranges :
    ispartialranges = True
    list_ranges , _totallength = util . obtain_content_ranges ( environ [ "HTTP_RANGE" ] , filesize )
    if len ( list_ranges ) == 0 : # No valid ranges present
        self . _fail ( HTTP_RANGE_NOT_SATISFIABLE )
    # More than one range present - > take only the first range , since
    # multiple range returns require multipart , which is not supported
    # obtain _ content _ ranges supports more than one range in case the above
    # behaviour changes in future
    ( range_start , range_end , range_length ) = list_ranges [ 0 ]
else :
    ( range_start , range_end , range_length ) = ( 0 , filesize - 1 , filesize )
# Content Processing
mimetype = res . get_content_type ( )
# provider . get _ content _ type ( path )
response_headers = [ ]
if res . support_content_length ( ) : # Content - length must be of type string
    response_headers . append ( ( "Content-Length" , str ( range_length ) ) )
if res . support_modified ( ) :
    response_headers . append ( ( "Last-Modified" , util . get_rfc1123_time ( last_modified ) ) )
response_headers . append ( ( "Content-Type" , mimetype ) )
response_headers . append ( ( "Date" , util . get_rfc1123_time ( ) ) )
if res . support_etag ( ) :
    response_headers . append ( ( "ETag" , '"{}"' . format ( entitytag ) ) )
if "response_headers" in environ [ "wsgidav.config" ] :
    customHeaders = environ [ "wsgidav.config" ] [ "response_headers" ]
    for header , value in customHeaders :
        response_headers . append ( ( header , value ) )
res . finalize_headers ( environ , response_headers )
if ispartialranges : # response _ headers . append ( ( " Content - Ranges " , " bytes " + str ( range _ start ) + " - " +
# str ( range _ end ) + " / " + str ( range _ length ) ) )
    response_headers . append ( ( "Content-Range" , "bytes {}-{}/{}" . format ( range_start , range_end , filesize ) , ) )
    start_response ( "206 Partial Content" , response_headers )
else :
    start_response ( "200 OK" , response_headers )
# Return empty body for HEAD requests
if is_head_method :
    yield b""
    return
fileobj = res . get_content ( )
if not doignoreranges :
    fileobj . seek ( range_start )
contentlengthremaining = range_length
while 1 :
    if contentlengthremaining < 0 or contentlengthremaining > self . block_size :
        readbuffer = fileobj . read ( self . block_size )
    else :
        readbuffer = fileobj . read ( contentlengthremaining )
    assert compat . is_bytes ( readbuffer )
    yield readbuffer
    contentlengthremaining -= len ( readbuffer )
    if len ( readbuffer ) == 0 or contentlengthremaining == 0 :
        break
fileobj . close ( )
return


signature:
def _find ( self , url ) :
    """Return properties document for path ."""

implementation:
# Query the permanent view to find a url
vr = self . db . view ( "properties/by_url" , key = url , include_docs = True )
_logger . debug ( "find(%r) returned %s" % ( url , len ( vr ) ) )
assert len ( vr ) <= 1 , "Found multiple matches for %r" % url
for row in vr :
    assert row . doc
    return row . doc
return None


signature:
def _find_descendents ( self , url ) :
    """Return properties document for url and all children ."""

implementation:
# Ad - hoc query for URL starting with a prefix
map_fun = """function(doc) {
                var url = doc.url + "/";
                if(doc.type === 'properties' && url.indexOf('%s') === 0) {
                    emit(doc.url, { 'id': doc._id, 'url': doc.url });
                }
            }""" % ( url + "/" )
vr = self . db . query ( map_fun , include_docs = True )
for row in vr :
    yield row . doc
return


signature:
def _get_realm_entry ( self , realm , user_name = None ) :
    """Return the matching user _ map entry ( falling back to default ' * ' if any ) ."""

implementation:
realm_entry = self . user_map . get ( realm )
if realm_entry is None :
    realm_entry = self . user_map . get ( "*" )
if user_name is None or realm_entry is None :
    return realm_entry
return realm_entry . get ( user_name )


signature:
def get_domain_realm ( self , path_info , environ ) :
    """Resolve a relative url to the appropriate realm name ."""

implementation:
realm = self . _calc_realm_from_path_provider ( path_info , environ )
return realm


signature:
def require_authentication ( self , realm , environ ) :
    """Return True if this realm requires authentication ( grant anonymous access otherwise ) ."""

implementation:
realm_entry = self . _get_realm_entry ( realm )
if realm_entry is None :
    _logger . error ( 'Missing configuration simple_dc.user_mapping["{}"] (or "*"): ' "realm is not accessible!" . format ( realm ) )
return realm_entry is not True


signature:
def basic_auth_user ( self , realm , user_name , password , environ ) :
    """Returns True if this user _ name / password pair is valid for the realm ,
 False otherwise . Used for basic authentication ."""

implementation:
user = self . _get_realm_entry ( realm , user_name )
if user is not None and password == user . get ( "password" ) :
    environ [ "wsgidav.auth.roles" ] = user . get ( "roles" , [ ] )
    return True
return False


signature:
def digest_auth_user ( self , realm , user_name , environ ) :
    """Computes digest hash A1 part ."""

implementation:
user = self . _get_realm_entry ( realm , user_name )
if user is None :
    return False
password = user . get ( "password" )
environ [ "wsgidav.auth.roles" ] = user . get ( "roles" , [ ] )
return self . _compute_http_digest_a1 ( realm , user_name , password )


signature:
def get ( self , token ) :
    """Return a lock dictionary for a token .
 If the lock does not exist or is expired , None is returned .
 token :
 lock token
 Returns :
 Lock dictionary or < None >
 Side effect : if lock is expired , it will be purged and None is returned ."""

implementation:
self . _lock . acquire_read ( )
try :
    lock = self . _dict . get ( token )
    if lock is None : # Lock not found : purge dangling URL2TOKEN entries
        _logger . debug ( "Lock purged dangling: {}" . format ( token ) )
        self . delete ( token )
        return None
    expire = float ( lock [ "expire" ] )
    if expire >= 0 and expire < time . time ( ) :
        _logger . debug ( "Lock timed-out({}): {}" . format ( expire , lock_string ( lock ) ) )
        self . delete ( token )
        return None
    return lock
finally :
    self . _lock . release ( )


signature:
def create ( self , path , lock ) :
    """Create a direct lock for a resource path .
 path :
 Normalized path ( utf8 encoded string , no trailing ' / ' )
 lock :
 lock dictionary , without a token entry
 Returns :
 New unique lock token . : < lock
 * * Note : * * the lock dictionary may be modified on return :
 - lock [ ' root ' ] is ignored and set to the normalized < path >
 - lock [ ' timeout ' ] may be normalized and shorter than requested
 - lock [ ' token ' ] is added"""

implementation:
self . _lock . acquire_write ( )
try : # We expect only a lock definition , not an existing lock
    assert lock . get ( "token" ) is None
    assert lock . get ( "expire" ) is None , "Use timeout instead of expire"
    assert path and "/" in path
    # Normalize root : / foo / bar
    org_path = path
    path = normalize_lock_root ( path )
    lock [ "root" ] = path
    # Normalize timeout from ttl to expire - date
    timeout = float ( lock . get ( "timeout" ) )
    if timeout is None :
        timeout = LockStorageDict . LOCK_TIME_OUT_DEFAULT
    elif timeout < 0 or timeout > LockStorageDict . LOCK_TIME_OUT_MAX :
        timeout = LockStorageDict . LOCK_TIME_OUT_MAX
    lock [ "timeout" ] = timeout
    lock [ "expire" ] = time . time ( ) + timeout
    validate_lock ( lock )
    token = generate_lock_token ( )
    lock [ "token" ] = token
    # Store lock
    self . _dict [ token ] = lock
    # Store locked path reference
    key = "URL2TOKEN:{}" . format ( path )
    if key not in self . _dict :
        self . _dict [ key ] = [ token ]
    else : # Note : Shelve dictionary returns copies , so we must reassign
    # values :
        tokList = self . _dict [ key ]
        tokList . append ( token )
        self . _dict [ key ] = tokList
    self . _flush ( )
    _logger . debug ( "LockStorageDict.set({!r}): {}" . format ( org_path , lock_string ( lock ) ) )
    return lock
finally :
    self . _lock . release ( )


signature:
def refresh ( self , token , timeout ) :
    """Modify an existing lock ' s timeout .
 token :
 Valid lock token .
 timeout :
 Suggested lifetime in seconds ( - 1 for infinite ) .
 The real expiration time may be shorter than requested !
 Returns :
 Lock dictionary .
 Raises ValueError , if token is invalid ."""

implementation:
assert token in self . _dict , "Lock must exist"
assert timeout == - 1 or timeout > 0
if timeout < 0 or timeout > LockStorageDict . LOCK_TIME_OUT_MAX :
    timeout = LockStorageDict . LOCK_TIME_OUT_MAX
self . _lock . acquire_write ( )
try : # Note : shelve dictionary returns copies , so we must reassign
# values :
    lock = self . _dict [ token ]
    lock [ "timeout" ] = timeout
    lock [ "expire" ] = time . time ( ) + timeout
    self . _dict [ token ] = lock
    self . _flush ( )
finally :
    self . _lock . release ( )
return lock


signature:
def delete ( self , token ) :
    """Delete lock .
 Returns True on success . False , if token does not exist , or is expired ."""

implementation:
self . _lock . acquire_write ( )
try :
    lock = self . _dict . get ( token )
    _logger . debug ( "delete {}" . format ( lock_string ( lock ) ) )
    if lock is None :
        return False
    # Remove url to lock mapping
    key = "URL2TOKEN:{}" . format ( lock . get ( "root" ) )
    if key in self . _dict : # _ logger . debug ( " delete token { } from url { } " . format ( token , lock . get ( " root " ) ) )
        tokList = self . _dict [ key ]
        if len ( tokList ) > 1 : # Note : shelve dictionary returns copies , so we must
        # reassign values :
            tokList . remove ( token )
            self . _dict [ key ] = tokList
        else :
            del self . _dict [ key ]
    # Remove the lock
    del self . _dict [ token ]
    self . _flush ( )
finally :
    self . _lock . release ( )
return True


signature:
def get_lock_list ( self , path , include_root , include_children , token_only ) :
    """Return a list of direct locks for < path > .
 Expired locks are * not * returned ( but may be purged ) .
 path :
 Normalized path ( utf8 encoded string , no trailing ' / ' )
 include _ root :
 False : don ' t add < path > lock ( only makes sense , when include _ children
 is True ) .
 include _ children :
 True : Also check all sub - paths for existing locks .
 token _ only :
 True : only a list of token is returned . This may be implemented
 more efficiently by some providers .
 Returns :
 List of valid lock dictionaries ( may be empty ) ."""

implementation:
assert compat . is_native ( path )
assert path and path . startswith ( "/" )
assert include_root or include_children
def __appendLocks ( toklist ) : # Since we can do this quickly , we use self . get ( ) even if
# token _ only is set , so expired locks are purged .
    for token in toklist :
        lock = self . get ( token )
        if lock :
            if token_only :
                lockList . append ( lock [ "token" ] )
            else :
                lockList . append ( lock )
path = normalize_lock_root ( path )
self . _lock . acquire_read ( )
try :
    key = "URL2TOKEN:{}" . format ( path )
    tokList = self . _dict . get ( key , [ ] )
    lockList = [ ]
    if include_root :
        __appendLocks ( tokList )
    if include_children :
        for u , ltoks in self . _dict . items ( ) :
            if util . is_child_uri ( key , u ) :
                __appendLocks ( ltoks )
    return lockList
finally :
    self . _lock . release ( )


signature:
def _flush ( self ) :
    """Write persistent dictionary to disc ."""

implementation:
_logger . debug ( "_flush()" )
self . _lock . acquire_write ( )
# TODO : read access is enough ?
try :
    self . _dict . sync ( )
finally :
    self . _lock . release ( )


signature:
def clear ( self ) :
    """Delete all entries ."""

implementation:
self . _lock . acquire_write ( )
# TODO : read access is enough ?
try :
    was_closed = self . _dict is None
    if was_closed :
        self . open ( )
    if len ( self . _dict ) :
        self . _dict . clear ( )
        self . _dict . sync ( )
    if was_closed :
        self . close ( )
finally :
    self . _lock . release ( )


signature:
def _fail ( self , value , context_info = None , src_exception = None , err_condition = None ) :
    """Wrapper to raise ( and log ) DAVError ."""

implementation:
e = DAVError ( value , context_info , src_exception , err_condition )
if self . verbose >= 4 :
    _logger . warning ( "Raising DAVError {}" . format ( safe_re_encode ( e . get_user_info ( ) , sys . stdout . encoding ) ) )
raise e


signature:
def begin_write ( self , content_type = None ) :
    """Open content as a stream for writing .
 See DAVResource . begin _ write ( )"""

implementation:
assert not self . is_collection
if self . provider . readonly :
    raise DAVError ( HTTP_FORBIDDEN )
# _ logger . debug ( " begin _ write : { } , { } " . format ( self . _ file _ path , " wb " ) )
# GC issue 57 : always store as binary
return open ( self . _file_path , "wb" , BUFFER_SIZE )


signature:
def delete ( self ) :
    """Remove this resource or collection ( recursive ) .
 See DAVResource . delete ( )"""

implementation:
if self . provider . readonly :
    raise DAVError ( HTTP_FORBIDDEN )
os . unlink ( self . _file_path )
self . remove_all_properties ( True )
self . remove_all_locks ( True )


signature:
def copy_move_single ( self , dest_path , is_move ) :
    """See DAVResource . copy _ move _ single ( )"""

implementation:
if self . provider . readonly :
    raise DAVError ( HTTP_FORBIDDEN )
fpDest = self . provider . _loc_to_file_path ( dest_path , self . environ )
assert not util . is_equal_or_child_uri ( self . path , dest_path )
# Copy file ( overwrite , if exists )
shutil . copy2 ( self . _file_path , fpDest )
# ( Live properties are copied by copy2 or copystat )
# Copy dead properties
propMan = self . provider . prop_manager
if propMan :
    destRes = self . provider . get_resource_inst ( dest_path , self . environ )
    if is_move :
        propMan . move_properties ( self . get_ref_url ( ) , destRes . get_ref_url ( ) , with_children = False , environ = self . environ , )
    else :
        propMan . copy_properties ( self . get_ref_url ( ) , destRes . get_ref_url ( ) , self . environ )


signature:
def move_recursive ( self , dest_path ) :
    """See DAVResource . move _ recursive ( )"""

implementation:
if self . provider . readonly :
    raise DAVError ( HTTP_FORBIDDEN )
fpDest = self . provider . _loc_to_file_path ( dest_path , self . environ )
assert not util . is_equal_or_child_uri ( self . path , dest_path )
assert not os . path . exists ( fpDest )
_logger . debug ( "move_recursive({}, {})" . format ( self . _file_path , fpDest ) )
shutil . move ( self . _file_path , fpDest )
# ( Live properties are copied by copy2 or copystat )
# Move dead properties
if self . provider . prop_manager :
    destRes = self . provider . get_resource_inst ( dest_path , self . environ )
    self . provider . prop_manager . move_properties ( self . get_ref_url ( ) , destRes . get_ref_url ( ) , with_children = True , environ = self . environ , )


signature:
def set_last_modified ( self , dest_path , time_stamp , dry_run ) :
    """Set last modified time for destPath to timeStamp on epoch - format"""

implementation:
# Translate time from RFC 1123 to seconds since epoch format
secs = util . parse_time_string ( time_stamp )
if not dry_run :
    os . utime ( self . _file_path , ( secs , secs ) )
return True


signature:
def get_member_names ( self ) :
    """Return list of direct collection member names ( utf - 8 encoded ) .
 See DAVCollection . get _ member _ names ( )"""

implementation:
# On Windows NT / 2k / XP and Unix , if path is a Unicode object , the result
# will be a list of Unicode objects .
# Undecodable filenames will still be returned as string objects
# If we don ' t request unicode , for example Vista may return a ' ? '
# instead of a special character . The name would then be unusable to
# build a distinct URL that references this resource .
nameList = [ ]
# self . _ file _ path is unicode , so os . listdir returns unicode as well
assert compat . is_unicode ( self . _file_path )
for name in os . listdir ( self . _file_path ) :
    if not compat . is_unicode ( name ) :
        name = name . decode ( sys . getfilesystemencoding ( ) )
    assert compat . is_unicode ( name )
    # Skip non files ( links and mount points )
    fp = os . path . join ( self . _file_path , name )
    if not os . path . isdir ( fp ) and not os . path . isfile ( fp ) :
        _logger . debug ( "Skipping non-file {!r}" . format ( fp ) )
        continue
    # name = name . encode ( " utf8 " )
    name = compat . to_native ( name )
    nameList . append ( name )
return nameList


signature:
def get_member ( self , name ) :
    """Return direct collection member ( DAVResource or derived ) .
 See DAVCollection . get _ member ( )"""

implementation:
assert compat . is_native ( name ) , "{!r}" . format ( name )
fp = os . path . join ( self . _file_path , compat . to_unicode ( name ) )
# name = name . encode ( " utf8 " )
path = util . join_uri ( self . path , name )
if os . path . isdir ( fp ) :
    res = FolderResource ( path , self . environ , fp )
elif os . path . isfile ( fp ) :
    res = FileResource ( path , self . environ , fp )
else :
    _logger . debug ( "Skipping non-file {}" . format ( path ) )
    res = None
return res


signature:
def create_empty_resource ( self , name ) :
    """Create an empty ( length - 0 ) resource .
 See DAVResource . create _ empty _ resource ( )"""

implementation:
assert "/" not in name
if self . provider . readonly :
    raise DAVError ( HTTP_FORBIDDEN )
path = util . join_uri ( self . path , name )
fp = self . provider . _loc_to_file_path ( path , self . environ )
f = open ( fp , "wb" )
f . close ( )
return self . provider . get_resource_inst ( path , self . environ )


signature:
def create_collection ( self , name ) :
    """Create a new collection as member of self .
 See DAVResource . create _ collection ( )"""

implementation:
assert "/" not in name
if self . provider . readonly :
    raise DAVError ( HTTP_FORBIDDEN )
path = util . join_uri ( self . path , name )
fp = self . provider . _loc_to_file_path ( path , self . environ )
os . mkdir ( fp )


implementation:
if self . provider . readonly :
    raise DAVError ( HTTP_FORBIDDEN )
shutil . rmtree ( self . _file_path , ignore_errors = False )
self . remove_all_properties ( True )
self . remove_all_locks ( True )


implementation:
if self . provider . readonly :
    raise DAVError ( HTTP_FORBIDDEN )
fpDest = self . provider . _loc_to_file_path ( dest_path , self . environ )
assert not util . is_equal_or_child_uri ( self . path , dest_path )
# Create destination collection , if not exists
if not os . path . exists ( fpDest ) :
    os . mkdir ( fpDest )
try : # may raise : [ Error 5 ] Permission denied :
# u ' C : \ \ temp \ \ litmus \ \ ccdest '
    shutil . copystat ( self . _file_path , fpDest )
except Exception :
    _logger . exception ( "Could not copy folder stats: {}" . format ( self . _file_path ) )
# ( Live properties are copied by copy2 or copystat )
# Copy dead properties
propMan = self . provider . prop_manager
if propMan :
    destRes = self . provider . get_resource_inst ( dest_path , self . environ )
    if is_move :
        propMan . move_properties ( self . get_ref_url ( ) , destRes . get_ref_url ( ) , with_children = False , environ = self . environ , )
    else :
        propMan . copy_properties ( self . get_ref_url ( ) , destRes . get_ref_url ( ) , self . environ )


signature:
def _loc_to_file_path ( self , path , environ = None ) :
    """Convert resource path to a unicode absolute file path .
 Optional environ argument may be useful e . g . in relation to per - user
 sub - folder chrooting inside root _ folder _ path ."""

implementation:
root_path = self . root_folder_path
assert root_path is not None
assert compat . is_native ( root_path )
assert compat . is_native ( path )
path_parts = path . strip ( "/" ) . split ( "/" )
file_path = os . path . abspath ( os . path . join ( root_path , * path_parts ) )
if not file_path . startswith ( root_path ) :
    raise RuntimeError ( "Security exception: tried to access file outside root: {}" . format ( file_path ) )
# Convert to unicode
file_path = util . to_unicode_safe ( file_path )
return file_path