Updating my HTML Application for Configuring your WebDAV Redirector Settings

A couple of years ago I wrote a blog that was titled "How to create an HTML Application to configure your WebDAV Redirector settings", where I showed how to use HTMLA to create a simple editor for most of the WebDAV Redirector settings. These settings have no other user interface, so prior to my blog post users had to manually edit the registry in order to modify their WebDAV Redirector settings.

Click image to expand

In the past two years since I wrote that blog, I have found myself using that simple application so often that I now keep it in my personal utilities folder on my SkyDrive so I can have it with me no matter where I am travelling. But that being said, I ran into an interesting situation the other day that made me want to update the application, so I thought that it was time to write a new blog with the updated changes.

Here's what happened - I had opened my application for modifying my WebDAV Redirector settings, but then something happened which distracted me, and then I headed off to lunch before I committed my changes to the registry. When I came back to my office, I noticed that my WebDAV Redirector settings application was still open and I clicked the Exit Application button. The application popped up a dialog which informed me that I had changes that hadn't been saved to the registry, but I forgot what they were. This put me in a quandary - I could simply click Yes and hope for the best, or I could click No and lose whatever changes that I had made and re-open the application to start over.

It was at that time that I thought to myself, "If only I had a Reset Values button..."

By now you can probably see where this blog is going, and here's what the new application looks like - it's pretty much the same as the last application, with the additional button that allows you to reset your values without exiting the application. (Note - the application will prompt you for confirmation if you attempt to reset the values and you have unsaved changes.)

Click image to expand

Creating the Updated HTML Application

To create this HTML Application, you need to use the same steps as my last blog: save the following HTMLA code as "WebDAV Redirector Settings.hta" to your computer, and then double-click its icon to run the application.

<html>

<head>
<title>WebDAV Redirector Settings</title>
<HTA:APPLICATION
  APPLICATIONNAME="WebDAV Redirector Settings"
  ID="WebDAV Redirector Settings"
  VERSION="1.0"
  BORDER="dialog"
  BORDERSTYLE="static"
  INNERBORDER="no"
  SYSMENU="no"
  MAXIMIZEBUTTON="no"
  MINIMIZEBUTTON="no"
  SCROLL="no"
  SCROLLFLAT="yes"
  SINGLEINSTANCE="yes"
  CONTEXTMENU="no"
  SELECTION="no"/>

<script language="vbscript">

' ----------------------------------------
' Start of main code section.
' ----------------------------------------

Option Explicit

Const intDialogWidth = 700
Const intDialogHeight = 620
Const HKEY_LOCAL_MACHINE = &H80000002
Const strWebClientKeyPath = "SYSTEM\CurrentControlSet\Services\WebClient\Parameters"
Const strLuaKeyPath = "Software\Microsoft\Windows\CurrentVersion\Policies\System"
Dim objRegistry
Dim blnHasChanges

' ----------------------------------------
' Start the application.
' ----------------------------------------

Sub Window_OnLoad
  On Error Resume Next
  ' Set up the UI dimensions.
  Self.resizeTo intDialogWidth,intDialogHeight
  Self.moveTo (Screen.AvailWidth - intDialogWidth) / 2, _
    (Screen.AvailHeight - intDialogHeight) / 2
  ' Retrieve the current settings.
  Document.all.TheBody.ClassName = "hide"
  Set objRegistry = GetObject( _
    "winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
  Call CheckForLUA()
  Call GetValues()
  Document.All.TheBody.ClassName = "show"
End Sub

' ----------------------------------------
' Check for User Access Control
' ----------------------------------------

Sub CheckForLUA()
  If GetRegistryDWORD(strLuaKeyPath,"EnableLUA",1)<> 0 Then
    MsgBox "User Access Control (UAC) is enabled on this computer." & _
      vbCrLf & vbCrLf & "UAC must be disabled in order to edit " & _
      "the registry and restart the service for the WebDAV Redirector. " & _
      "Please disable UAC before running this application again. " & _
      "This application will now exit.", _
      vbCritical, "User Access Control"
    Self.close
  End If 
End Sub

' ----------------------------------------
' Exit the application.
' ----------------------------------------

Sub ExitApplication()
  If blnHasChanges = False Then
    If MsgBox("Are you sure you want to exit?", _
      vbQuestion Or vbYesNo Or vbDefaultButton2, _
      "Exit Application") = vbNo Then
      Exit Sub
    End If
  Else
    Dim intRetVal
    intRetVal = MsgBox("You have unsaved changes. " & _
      "Do you want to save them before you exit?", _
      vbQuestion Or vbYesNoCancel Or vbDefaultButton1, _
      "Reset Application")
    If intRetVal = vbYes Then
      Call SetValues()
    ElseIf intRetVal = vbCancel Then
      Exit Sub
    End If
  End If
  Self.close
End Sub

' ----------------------------------------
' Reset the application.
' ----------------------------------------

Sub ResetApplication()
  If blnHasChanges = True Then
    Dim intRetVal
    intRetVal = MsgBox("You have unsaved changes. " & _
      "Do you want to save them before you reset the values?", _
      vbQuestion Or vbYesNoCancel Or vbDefaultButton1, _
      "Reset Application")
    If intRetVal = vbYes Then
      Call SetValues()
    ElseIf intRetVal = vbCancel Then
      Exit Sub
    End If
  End If
  Call GetValues()
End Sub

' ----------------------------------------
' Flag the application as having changes.
' ----------------------------------------

Sub FlagChanges()
  blnHasChanges = True
End Sub

' ----------------------------------------
' Retrieve the settings from the registry.
' ----------------------------------------

Sub GetValues()
  On Error Resume Next
  Dim tmpCount,tmpArray,tmpString
  ' Get the radio button values
  Call SetRadioValue(Document.all.BasicAuthLevel, _
    GetRegistryDWORD(strWebClientKeyPath, _
    "BasicAuthLevel",1))
  Call SetRadioValue(Document.all.SupportLocking, _
    GetRegistryDWORD(strWebClientKeyPath, _
    "SupportLocking",1))
  ' Get the text box values
  Document.all.InternetServerTimeoutInSec.Value = _
    GetRegistryDWORD(strWebClientKeyPath, _
    "InternetServerTimeoutInSec",30)
  Document.all.FileAttributesLimitInBytes.Value = _
    GetRegistryDWORD(strWebClientKeyPath, _
    "FileAttributesLimitInBytes",1000000)
  Document.all.FileSizeLimitInBytes.Value = _
    GetRegistryDWORD(strWebClientKeyPath, _
    "FileSizeLimitInBytes",50000000)
  Document.all.LocalServerTimeoutInSec.Value = _
    GetRegistryDWORD(strWebClientKeyPath, _
    "LocalServerTimeoutInSec",15)
  Document.all.SendReceiveTimeoutInSec.Value = _
    GetRegistryDWORD(strWebClientKeyPath, _
    "SendReceiveTimeoutInSec",60)
  Document.all.ServerNotFoundCacheLifeTimeInSec.Value = _
    GetRegistryDWORD(strWebClientKeyPath, _
    "ServerNotFoundCacheLifeTimeInSec",60)
  ' Get the text area values
  tmpArray = GetRegistryMULTISZ( _
    strWebClientKeyPath,"AuthForwardServerList")
  For tmpCount = 0 To UBound(tmpArray)
    tmpString = tmpString & tmpArray(tmpCount) & vbTab
  Next
  If Len(tmpString)>0 Then
    Document.all.AuthForwardServerList.Value = _
      Replace(Left(tmpString,Len(tmpString)-1),vbTab,vbCrLf)
  End If
  blnHasChanges = False
End Sub

' ----------------------------------------
' Save the settings in the registry.
' ----------------------------------------

Sub SetValues()
  On Error Resume Next
  ' Set the radio button values
  Call SetRegistryDWORD( _
    strWebClientKeyPath, _
    "BasicAuthLevel", _
    GetRadioValue(Document.all.BasicAuthLevel))
  Call SetRegistryDWORD( _
    strWebClientKeyPath, _
    "SupportLocking", _
    GetRadioValue(Document.all.SupportLocking))
  ' Set the text box values
  Call SetRegistryDWORD( _
    strWebClientKeyPath, _
    "InternetServerTimeoutInSec", _
    Document.all.InternetServerTimeoutInSec.Value)
  Call SetRegistryDWORD( _
    strWebClientKeyPath, _
    "FileAttributesLimitInBytes", _
    Document.all.FileAttributesLimitInBytes.Value)
  Call SetRegistryDWORD( _
    strWebClientKeyPath, _
    "FileSizeLimitInBytes", _
    Document.all.FileSizeLimitInBytes.Value)
  Call SetRegistryDWORD( _
    strWebClientKeyPath, _
    "LocalServerTimeoutInSec", _
    Document.all.LocalServerTimeoutInSec.Value)
  Call SetRegistryDWORD( _
    strWebClientKeyPath, _
    "SendReceiveTimeoutInSec", _
    Document.all.SendReceiveTimeoutInSec.Value)
  Call SetRegistryDWORD( _
    strWebClientKeyPath, _
    "ServerNotFoundCacheLifeTimeInSec", _
    Document.all.ServerNotFoundCacheLifeTimeInSec.Value)
  ' Set the text area values
  Call SetRegistryMULTISZ( _
    strWebClientKeyPath, _
    "AuthForwardServerList", _
    Split(Document.all.AuthForwardServerList.Value,vbCrLf))
  ' Prompt to restart the WebClient service
  If MsgBox("Do you want to restart the WebDAV Redirector " & _
    "service so your settings will take effect?", _
    vbQuestion Or vbYesNo Or vbDefaultButton2, _
    "Restart WebDAV Redirector") = vbYes Then
    ' Restart the WebClient service.
    Call RestartWebClient()
  End If
  Call GetValues()
End Sub

' ----------------------------------------
' Start the WebClient service.
' ----------------------------------------

Sub RestartWebClient()
  On Error Resume Next
  Dim objWMIService,colServices,objService
  Document.All.TheBody.ClassName = "hide"
  Set objWMIService = GetObject( _
    "winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
  Set colServices = objWMIService.ExecQuery( _
    "Select * from Win32_Service Where Name='WebClient'")
  For Each objService in colServices
    objService.StopService()
    objService.StartService()
  Next
  Document.All.TheBody.ClassName = "show"
End Sub

' ----------------------------------------
' Retrieve a DWORD value from the registry.
' ----------------------------------------

Function GetRegistryDWORD( _
    ByVal tmpKeyPath, _
    ByVal tmpValueName, _
    ByVal tmpDefaultValue)
  On Error Resume Next
  Dim tmpDwordValue
  If objRegistry.GetDWORDValue( _
      HKEY_LOCAL_MACHINE, _
      tmpKeyPath, _
      tmpValueName, _
      tmpDwordValue)=0 Then
    GetRegistryDWORD = CLng(tmpDwordValue)
  Else
    GetRegistryDWORD = CLng(tmpDefaultValue)
  End If
End Function

' ----------------------------------------
' Set a DWORD value in the registry.
' ----------------------------------------

Sub SetRegistryDWORD( _
    ByVal tmpKeyPath, _
    ByVal tmpValueName, _
    ByVal tmpDwordValue)
  On Error Resume Next
  Call objRegistry.SetDWORDValue( _
    HKEY_LOCAL_MACHINE, _
    tmpKeyPath, _
    tmpValueName, _
    CLng(tmpDwordValue))
End Sub

' ----------------------------------------
' Retrieve a MULTISZ value from the registry.
' ----------------------------------------

Function GetRegistryMULTISZ( _
    ByVal tmpKeyPath, _
    ByVal tmpValueName)
  On Error Resume Next
  Dim tmpMultiSzValue
  If objRegistry.GetMultiStringValue( _
      HKEY_LOCAL_MACHINE, _
      tmpKeyPath, _
      tmpValueName, _
      tmpMultiSzValue)=0 Then
    GetRegistryMULTISZ = tmpMultiSzValue
  Else
    GetRegistryMULTISZ = Array()
  End If
End Function

' ----------------------------------------
' Set a MULTISZ value in the registry.
' ----------------------------------------

Sub SetRegistryMULTISZ( _
    ByVal tmpKeyPath, _
    ByVal tmpValueName, _
    ByVal tmpMultiSzValue)
  On Error Resume Next
  Call objRegistry.SetMultiStringValue( _
    HKEY_LOCAL_MACHINE, _
    tmpKeyPath, _
    tmpValueName, _
    tmpMultiSzValue)
End Sub

' ----------------------------------------
' Retrieve the value of a radio button group.
' ----------------------------------------

Function GetRadioValue(ByVal tmpRadio)
  On Error Resume Next
  Dim tmpCount
  For tmpCount = 0 To (tmpRadio.Length-1)
    If tmpRadio(tmpCount).Checked Then
      GetRadioValue = CLng(tmpRadio(tmpCount).Value)
      Exit For
    End If
  Next
End Function

' ----------------------------------------
' Set the value for a radio button group.
' ----------------------------------------

Sub SetRadioValue(ByVal tmpRadio, ByVal tmpValue)
  On Error Resume Next
  Dim tmpCount
  For tmpCount = 0 To (tmpRadio.Length-1)
    If CLng(tmpRadio(tmpCount).Value) = CLng(tmpValue) Then
      tmpRadio(tmpCount).Checked = True
      Exit For
    End If
  Next
End Sub

' ----------------------------------------
'
' ----------------------------------------

Sub Validate(tmpField)
  Dim tmpRegEx, tmpMatches
  Set tmpRegEx = New RegExp
  tmpRegEx.Pattern = "[0-9]"
  tmpRegEx.IgnoreCase = True
  tmpRegEx.Global = True
  Set tmpMatches = tmpRegEx.Execute(tmpField.Value)
  If tmpMatches.Count = Len(CStr(tmpField.Value)) Then
    If CDbl(tmpField.Value) => 0 And _
      CDbl(tmpField.Value) =< 4294967295 Then
      Exit Sub
    End If
  End If
  MsgBox "Please enter a whole number between 0 and 4294967295.", _
    vbCritical, "Validation Error"
  tmpField.Focus
End Sub

' ----------------------------------------
'
' ----------------------------------------

Sub BasicAuthWarning()
  MsgBox "WARNING:" & vbCrLf  & vbCrLf & _
    "Using Basic Authentication over non-SSL connections can cause " & _
    "serious security issues. Usernames and passwords are transmitted " & _
    "in clear text, therefore the use of Basic Authentication with " & _
    "WebDAV is disabled by default for non-SSL connections. That " & _
    "being said, this setting can override the default behavior for " & _
    "Basic Authentication, but it is strongly discouraged.", _
    vbCritical, "Basic Authentication Warning"
End Sub

' ----------------------------------------
' End of main code section.
' ----------------------------------------

</script>
<style>
body { color:#000000; background-color:#cccccc;
  font-family:'Segoe UI',Tahoma,Verdana,Arial; font-size:9pt; }
fieldset { padding:10px; width:640px; }
.button { width:150px; }
.textbox { width:200px; height:22px; text-align:right; }
.textarea { width:300px; height:50px; text-align:left; }
.radio { margin-left:-5px; margin-top: -2px; }
.hide { display:none; }
.show { display:block; }
select { width:300px; text-align:left; }
table { border-collapse:collapse; empty-cells:hide; }
h1 { font-size:14pt; }
th { font-size:9pt; text-align:left; vertical-align:top; padding:2px; }
td { font-size:9pt; text-align:left; vertical-align:top; padding:2px; }
big { font-size:11pt; }
small { font-size:8pt; }
</style>
</head>

<body id="TheBody" class="hide">

<h1 align="center" id="TheTitle" style="margin-bottom:-20px;">WebDAV Redirector Settings</h1>
<div align="center">
<p style="margin-bottom:-20px;"><i><small><b>Note</b>: See <a target="_blank" href="http://go.microsoft.com/fwlink/?LinkId=324291">Using the WebDAV Redirector</a> for additional details.</small></i></p>
  <form>
    <center>
    <table border="0" cellpadding="2" cellspacing="2" style="width:600px;">
      <tr>
        <td style="width:600px;text-align:left"><fieldset title="Security Settings">
        <legend>&nbsp;<b>Security Settings</b>&nbsp;</legend>
        These values affect the security behavior for the WebDAV Redirector.<br>
        <table style="width:600px;">
          <tr title="Specifies whether the WebDAV Redirector can use Basic Authentication to communicate with a server.">
            <td style="width:300px">
            <table border="0">
              <tr>
                <td style="width:300px"><b>Basic Authentication Level</b></td>
              </tr>
              <tr>
                <td style="width:300px;"><span style="width:280px;padding-left:20px;"><small><i><b>Note</b>: Using basic authentication can cause <u>serious security issues</u> as the username and password are transmitted in clear text, therefore the use of basic authentication over WebDAV is disabled by default unless the connection is using SSL.</i></small></span></td>
              </tr>
            </table>
            </td>
            <td style="width:300px">
            <table style="width:300px">
              <tr>
                <td style="width:020px"><input class="radio" type="radio" value="0" name="BasicAuthLevel" onchange="VBScript:FlagChanges()" id="BasicAuthLevel0"></td>
                <td style="width:280px"><label for="BasicAuthLevel0">Basic Authentication is disabled</label></td>
              </tr>
              <tr>
                <td style="width:020px"><input class="radio" type="radio" value="1" checked name="BasicAuthLevel" onchange="VBScript:FlagChanges()" id="BasicAuthLevel1"></td>
                <td style="width:280px"><label for="BasicAuthLevel1">Basic Authentication is enabled for SSL web sites only</label></td>
              </tr>
              <tr>
                <td style="width:020px"><input class="radio" type="radio" value="2" name="BasicAuthLevel" onchange="VBScript:FlagChanges()" id="BasicAuthLevel2" onClick="VBScript:BasicAuthWarning()"></td>
                <td style="width:280px"><label for="BasicAuthLevel2">Basic Authentication is enabled for SSL and non-SSL web sites</label></td>
              </tr>
            </table>
            </td>
          </tr>
          <tr title="Specifies a list of local URLs for forwarding credentials that bypasses any proxy settings. (Note: This requires Windows Vista SP1 or later.)">
            <td style="width:300px">
            <table border="0">
              <tr>
                <td style="width:300px"><b>Authentication Forwarding Server List</b></td>
              </tr>
              <tr>
                <td style="width:300px;"><span style="width:280px;padding-left:20px;"><small><i><b>Note</b>: Include one server name per line.</i></small></span></td>
              </tr>
            </table>
            </td>
            <td style="width:300px"><textarea class="textarea" name="AuthForwardServerList" onchange="VBScript:FlagChanges()"></textarea></td>
          </tr>
          <tr title="Specifies whether the WebDAV Redirector supports locking.">
            <td style="width:300px"><b>Support for WebDAV Locking</b></td>
            <td style="width:300px">
            <table style="width:300px">
              <tr>
                <td style="width:020px"><input class="radio" type="radio" value="1" checked name="SupportLocking" onchange="VBScript:FlagChanges()" id="SupportLocking1"></td>
                <td style="width:280px"><label for="SupportLocking1">Enable Lock Support</label></td>
              </tr>
              <tr>
                <td style="width:020px"><input class="radio" type="radio" value="0" name="SupportLocking" onchange="VBScript:FlagChanges()" id="SupportLocking0"></td>
                <td style="width:280px"><label for="SupportLocking0">Disable Lock Support</label></td>
              </tr>
            </table>
            </td>
          </tr>
        </table>
        </fieldset> </td>
      </tr>
      <tr>
        <td style="width:600px;text-align:left"><fieldset title="Time-outs">
        <legend>&nbsp;<b>Time-outs and Maximum Sizes</b>&nbsp;</legend>
        These values affect the behavior for WebDAV Client/Server operations.<br>
        <table border="0" style="width:600px;">
          <tr title="Specifies the connection time-out for the WebDAV Redirector uses when communicating with non-local WebDAV servers.">
            <td style="width:300px"><b>Internet Server Time-out</b> <small>(In Seconds)</small></td>
            <td style="width:300px"><input class="textbox" type="text" name="InternetServerTimeoutInSec" onchange="VBScript:FlagChanges()" onblur="VBScript:Validate(Me)" value="30"></td>
          </tr>
          <tr title="Specifies the connection time-out for the WebDAV Redirector uses when communicating with a local WebDAV server.">
            <td style="width:300px"><b>Local Server Time-out</b> <small>(In Seconds)</small></td>
            <td style="width:300px"><input class="textbox" type="text" name="LocalServerTimeoutInSec" onchange="VBScript:FlagChanges()" onblur="VBScript:Validate(Me)" value="15"></td>
          </tr>
          <tr title="Specifies the time-out in seconds that the WebDAV Redirector uses after issuing a request.">
            <td style="width:300px"><b>Send/Receive Time-out</b> <small>(In Seconds)</small></td>
            <td style="width:300px"><input class="textbox" type="text" name="SendReceiveTimeoutInSec" onchange="VBScript:FlagChanges()" onblur="VBScript:Validate(Me)" value="60"></td>
          </tr>
          <tr title="Specifies the period of time that a server is cached as non-WebDAV by the WebDAV Redirector. If a server is found in this list, a fail is returned immediately without attempting to contact the server.">
            <td style="width:300px"><b>Server Not Found Cache Time-out</b> <small>(In Seconds)</small></td>
            <td style="width:300px"><input class="textbox" type="text" name="ServerNotFoundCacheLifeTimeInSec" onchange="VBScript:FlagChanges()" onblur="VBScript:Validate(Me)" value="60"></td>
          </tr>
          <tr title="Specifies the maximum size in bytes that the WebDAV Redirector allows for file transfers.">
            <td style="width:300px"><b>Maximum File Size</b> <small>(In Bytes)</small></td>
            <td style="width:300px"><input class="textbox" type="text" name="FileSizeLimitInBytes" onchange="VBScript:FlagChanges()" onblur="VBScript:Validate(Me)" value="50000000"></td>
          </tr>
          <tr title="Specifies the maximum size that is allowed by the WebDAV Redirector for all properties on a specific collection.">
            <td style="width:300px"><b>Maximum Attributes Size</b> <small>(In Bytes)</small></td>
            <td style="width:300px"><input class="textbox" type="text" name="FileAttributesLimitInBytes" onchange="VBScript:FlagChanges()" onblur="VBScript:Validate(Me)" value="1000000"></td>
          </tr>
        </table>
        </fieldset> </td>
      </tr>
      <tr>
        <td style="text-align:center">
        <table border="0">
          <tr>
            <td style="text-align:center"><input class="button" type="button" value="Apply Settings" onclick="VBScript:SetValues()">
            <td style="text-align:center"><input class="button" type="button" value="Reset Values" onclick="VBScript:ResetApplication()">
            <td style="text-align:center"><input class="button" type="button" value="Exit Application" onclick="VBScript:ExitApplication()">
          </tr>
        </table>
        </td>
      </tr>
    </table>
    </center>
  </form>
</div>

</body>

</html>
Additional Notes

As with the last version of this HTML Application, you will need to run this application as an administrator in order to save the settings to the registry and restart the WebDAV Redirector service.

Have fun! ;-]


Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

How to Create a Blind Drop WebDAV Share

I had an interesting WebDAV question earlier today that I had not considered before: how can someone create a "Blind Drop Share" using WebDAV? By way of explanation, a Blind Drop Share is a path where users can copy files, but never see the files that are in the directory. You can setup something like this by using NTFS permissions, but that environment can be a little difficult to set up and maintain.

With that in mind, I decided to research a WebDAV-specific solution that didn't require mangling my NTFS permissions. In the end it was pretty easy to achieve, so I thought that it would make a good blog for anyone who wants to do this.

A Little Bit About WebDAV

NTFS permissions contain access controls that configure the directory-listing behavior for files and folders; if you modify those settings, you can control who can see files and folders when they connect to your shared resources. However, there are no built-in features for the WebDAV module which ships with IIS that will approximate the NTFS behavior. But that being said, there is an interesting WebDAV quirk that you can use that will allow you to restrict directory listings, and I will explain how that works.

WebDAV uses the PROPFIND command to retrieve the properties for files and folders, and the WebDAV Redirector will use the response from a PROPFIND command to display a directory listing. (Note: Official WebDAV terminology has no concept of files and folders, those physical objects are respectively called Resources and Collections in WebDAV jargon. But that being said, I will use files and folders throughout this blog post for ease of understanding.)

In any event, one of the HTTP headers that WebDAV uses with the PROPFIND command is the Depth header, which is used to specify how deep the folder/collection traversal should go:

  • If you sent a PROPFIND command for the root of your website with a Depth:0 header/value, you would get the properties for just the root directory - with no files listed; a Depth:0 header/value only retrieves properties for the single resource that you requested.
  • If you sent a PROPFIND command for the root of your website with a Depth:1 header/value, you would get the properties for every file and folder in the root of your website; a Depth:1 header/value retrieves properties for the resource that you requested and all siblings.
  • If you sent a PROPFIND command for the root of your website with a Depth:infinity header/value, you would get the properties for every file and folder in your entire website; a Depth:infinity header/value retrieves properties for every resource regardless of its depth in the hierarchy. (Note that retrieving directory listings with infinite depth are disabled by default in IIS 7 and IIS 8 because it can be CPU intensive.)

By analyzing the above information, it should be obvious that what you need to do is to restrict users to using a Depth:0 header/value. But that's where this scenario gets interesting: if your end-users are using the Windows WebDAV Redirector or other similar technology to map a drive to your HTTP website, you have no control over the value of the Depth header. So how can you restrict that?

In the past I would have written custom native-code HTTP module or ISAPI filter to modify the value of the Depth header; but once you understand how WebDAV works, you can use the URL Rewrite module to modify the headers of incoming HTTP requests to accomplish some pretty cool things - like modifying the values WebDAV-related HTTP headers.

Adding URL Rewrite Rules to Modify the WebDAV Depth Header

Here's how I configured URL Rewrite to set the value of the Depth header to 0, which allowed me to create a "Blind Drop" WebDAV site:

  1. Open the URL Rewrite feature in IIS Manager for your website.
    Click image to expand
  2. Click the Add Rules link in the Actionspane.
    Click image to expand
  3. When the Add Rules dialog box appears, highlight Blank rule and click OK.
    Click image to expand
  4. When the Edit Inbound Rulepage appears, configure the following settings:
    1. Name the rule "Modify Depth Header".
      Click image to expand
    2. In the Match URLsection:
      1. Choose Matches the Pattern in the Requested URL drop-down menu.
      2. Choose Wildcards in the Using drop-down menu.
      3. Type a single asterisk "*" in the Pattern text box.
      Click image to expand
    3. Expand the Server Variables collection and click Add.
      Click image to expand
    4. When the Set Server Variabledialog box appears:
      1. Type "HTTP_DEPTH" in the Server variable name text box.
      2. Type "0" in the Value text box.
      3. Make sure that Replace the existing value checkbox is checked.
      4. Click OK.
    5. In the Action group, choose None in the Action typedrop-down menu.
      Click image to expand
    6. Click Apply in the Actions pane, and then click Back to Rules.
      Click image to expand
  5. Click View Server Variables in the Actionspane.
    Click image to expand
  6. When the Allowed Server Variablespage appears, configure the following settings:
    1. Click Add in the Actionspane.
      Click image to expand
    2. When the Add Server Variabledialog box appears:
      1. Type "HTTP_DEPTH" in the Server variable name text box.
      2. Click OK.
    3. Click Back to Rules in the Actionspane.
      Click image to expand

If all of these changes were saved to your applicationHost.config file, the resulting XML might resemble the following example - with XML comments added by me to highlight some of the major sections:

<location path="Default Web Site">
    <system.webServer>
    
        <-- Start of Security Settings -->
        <security>
            <authentication>
                <anonymousAuthentication enabled="false" />
                <basicAuthentication enabled="true" />
            </authentication>
            <requestFiltering>
                <fileExtensions applyToWebDAV="false" />
                <verbs applyToWebDAV="false" />
                <hiddenSegments applyToWebDAV="false" />
            </requestFiltering>
        </security>
        
        <-- Start of WebDAV Settings -->
        <webdav>
            <authoringRules>
                <add roles="administrators" path="*" access="Read, Write, Source" />
            </authoringRules>
            <authoring enabled="true">
                <properties allowAnonymousPropfind="false" allowInfinitePropfindDepth="true">
                    <clear />
                    <add xmlNamespace="*" propertyStore="webdav_simple_prop" />
                </properties>
            </authoring>
        </webdav>
        
        <-- Start of URL Rewrite Settings -->
        <rewrite>
            <rules>
                <rule name="Modify Depth Header" enabled="true" patternSyntax="Wildcard">
                    <match url="*" />
                    <serverVariables>
                        <set name="HTTP_DEPTH" value="0" />
                    </serverVariables>
                    <action type="None" />
                </rule>
            </rules>
            <allowedServerVariables>
                <add name="HTTP_DEPTH" />
            </allowedServerVariables>
        </rewrite>
        
    </system.webServer>
</location>

In all likelihood, some of these settings will be stored in your applicationHost.config file, and the remaining settings will be stored in the web.config file of your website.

Testing the URL Rewrite Settings

If you did not have the URL Rewrite rule in place, or if you disabled the rule, then your web server might respond like the following example if you used the WebDAV Redirector to map a drive to your website from a command prompt:

C:\>net use z: http://www.contoso.com/
Enter the user name for 'www.contoso.com': www.contoso.com\robert
Enter the password for www.contoso.com:
The command completed successfully.

C:\>z:

Z:\>dir
Volume in drive Z has no label.
Volume Serial Number is 0000-0000

Directory of Z:\

09/16/2013 08:55 PM <DIR> .
09/16/2013 08:55 PM <DIR> ..
09/14/2013 12:39 AM <DIR> aspnet_client
09/16/2013 08:06 PM <DIR> scripts
09/16/2013 07:55 PM 66 default.aspx
09/14/2013 12:38 AM 98,757 iis-85.png
09/14/2013 12:38 AM 694 iisstart.htm
09/16/2013 08:55 PM 75 web.config
              4 File(s) 99,592 bytes
              8 Dir(s) 956,202,631,168 bytes free

Z:\>

However, when you have the URL Rewrite correctly configured and enabled, connecting to the same website will resemble the following example - notice how no files or folders are listed:

C:\>net use z: http://www.contoso.com/
Enter the user name for 'www.contoso.com': www.contoso.com\robert
Enter the password for www.contoso.com:
The command completed successfully.

C:\>z:

Z:\>dir
Volume in drive Z has no label.
Volume Serial Number is 0000-0000

Directory of Z:\

09/16/2013 08:55 PM <DIR> .
09/16/2013 08:55 PM <DIR> ..
              0 File(s) 0 bytes
              2 Dir(s) 956,202,803,200 bytes free

Z:\>

Despite the blank directory listing, you can still retrieve the properties for any file or folder if you know that it exists. So if you were to use the mapped drive from the preceding example, you could still use an explicit directory command for any object that you had uploaded or created:

Z:\>dir default.aspx
Volume in drive Z has no label.
Volume Serial Number is 0000-0000

Directory of Z:\

09/16/2013 07:55 PM 66 default.aspx
              1 File(s) 66 bytes
              0 Dir(s) 956,202,799,104 bytes free

Z:\>dir scripts
Volume in drive Z has no label.
Volume Serial Number is 0000-0000

Directory of Z:\scripts

09/16/2013 07:52 PM <DIR> .
09/16/2013 07:52 PM <DIR> ..
              0 File(s) 0 bytes
              2 Dir(s) 956,202,799,104 bytes free

Z:\>

The same is true for creating directories and files; you can create them, but they will not show up in the directory listings after you have created them unless you reference them explicitly:

Z:\>md foobar

Z:\>dir
Volume in drive Z has no label.
Volume Serial Number is 0000-0000

Directory of Z:\

09/16/2013 11:52 PM <DIR> .
09/16/2013 11:52 PM <DIR> ..
              0 File(s) 0 bytes
              2 Dir(s) 956,202,618,880 bytes free

Z:\>cd foobar

Z:\foobar>copy NUL foobar.txt
        1 file(s) copied.

Z:\foobar>dir
Volume in drive Z has no label.
Volume Serial Number is 0000-0000

Directory of Z:\foobar

09/16/2013 11:52 PM <DIR> .
09/16/2013 11:52 PM <DIR> ..
              0 File(s) 0 bytes
              2 Dir(s) 956,202,303,488 bytes free

Z:\foobar>dir foobar.txt
Volume in drive Z has no label.
Volume Serial Number is 0000-0000

Directory of Z:\foobar

09/16/2013 11:53 PM 0 foobar.txt
              1 File(s) 0 bytes
              0 Dir(s) 956,202,299,392 bytes free

Z:\foobar>

That wraps it up for today's post, although I should point out that if you see any errors when you are using the WebDAV Redirector, you should take a look at the Troubleshooting the WebDAV Redirector section of my Using the WebDAV Redirector article; I have done my best to list every error and resolution that I have discovered over the past several years.


Note: This blog was originally posted at http://blogs.msdn.com/robert_mcmurray/

Higher Learning

Many years ago - more years than I care to admit - I worked in the IT department for a local community college in Tucson, AZ. I worked with a great bunch of people during my time at that institution, and now that I have returned to Tucson, it's fun to get reacquainted with my old colleagues and catch up on what's been happening in everyone's lives.

With that in mind, I recently had the opportunity to meet one of my old coworkers for lunch. Our destination was near the University of Arizona, so I parked my car in one of the university's parking garages and set out across the university campus on foot. As I was walking past the mathematics buildings, I happened to overhear one side of an exasperated conversation that a young twenty-something was having on her cell phone. The main source of her consternation appeared to be: "My class has a test in it every day, and the professor never teaches us what's on the test!"

My immediate thought was: "That's good; you're supposed to study and learn the material, then you'll already know what's on the test." This made me laugh first, but after further analysis of the situation, I don't think that it's all that funny. I think that this twenty-something's expectations are a byproduct of today's standardized testing - she expects to be taught what's on the test instead of actually learning the material.

If that's the case, then it's a pretty bad testimony about the state of education in America today.