/// <summary>
 ///     Copies a file on the server to another place on the
 ///     server
 /// </summary>
 /// <param name="source">
 ///     The source path. It must be non-null and contain at
 ///     least one character.
 /// </param>
 /// <param name="destination">
 ///     The destination path. It must be non-null and contain
 ///     at least one character.
 /// </param>
 /// <param name="allowOverwrite">
 ///     Flag to allow overwrite
 /// </param>
 /// <param name="resultCode">
 ///     The result code from the server or BadRequest if an
 ///     exception was thrown.
 /// </param>
 /// <param name="resultReason">
 ///     The result message from the server or the exception
 ///     message if an exception was thrown.
 /// </param>
 /// <param name="contentType">
 ///     The reported type of the content returned from the
 ///     server.
 /// </param>
 /// <param name="resultContent">
 ///     The content of the resource or null if there was an
 ///     error.
 /// </param>
 /// <param name="sendProgress">
 ///     Used to monitor the progress of sending the request.
 /// </param>
 /// <param name="receiveProgress">
 ///     Used to monitor the progress of receiving the response.
 /// </param>
 /// <returns>
 ///     true if the call succeeded, false if a communication
 ///     error prevented the request from succeeding. If true,
 ///     the caller should examine the result code and reason
 ///     to determine whether or not the result content is
 ///     valid.
 /// </returns>
 public bool CopyResource( string source,
     string destination,
     bool allowOverwrite,
     out WebDavStatusCode resultCode,
     out string resultReason,
     out string contentType,
     out byte[] resultContent,
     ProgressContainer sendProgress,
     ProgressContainer receiveProgress)
 {
     return ChangeResourceCmd(CopyRequest,
         source,
         destination,
         allowOverwrite,
         out resultCode,
         out resultReason,
         out contentType,
         out resultContent,
         sendProgress,
         receiveProgress );
 }
        /// <summary>
        ///     Get a resource from the server.
        /// </summary>
        /// <param name="path">
        ///     The path of the resource. It must be non-null and
        ///     contain at least one character.
        /// </param>
        /// <param name="resultCode">
        ///     The result code from the server or BadRequest if an
        ///     exception was thrown.
        /// </param>
        /// <param name="resultReason">
        ///     The result message from the server or the exception
        ///     message if an exception was thrown.
        /// </param>
        /// <param name="contentType">
        ///     The reported type of the content returned from the
        ///     server.
        /// </param>
        /// <param name="resultContent">
        ///     The content of the resource or null if there was an
        ///     error.
        /// </param>
        /// <param name="sendProgress">
        ///     Used to monitor the progress of sending the request.
        /// </param>
        /// <param name="receiveProgress">
        ///     Used to monitor the progress of receiving the response.
        /// </param>
        /// <returns>
        ///     true if the call succeeded, false if a communication
        ///     error prevented the request from succeeding. If true,
        ///     the caller should examine the result code and reason
        ///     to determine whether or not the result content is
        ///     valid.
        /// </returns>
        public bool GetResource( string path,
            out WebDavStatusCode resultCode,
            out string resultReason,
            out string contentType,
            out byte[] resultContent,
            ProgressContainer sendProgress,
            ProgressContainer receiveProgress)
        {
            bool success = true;

            // Make sure the input parameters are valid.
            if( ( path == null ) || ( path.Length == 0 ) )
            {
                throw new ArgumentException( "Invalid resource path.", "path" );
            }

            // Try to get the resource.
            try
            {
                DavResponse response = SendRequest( path, GetRequest, null, null, null, sendProgress, receiveProgress );
                // Extract the response data for the caller.
                resultCode = response.StatusCode;
                resultReason = response.StatusReason;
                contentType = (string) response.Headers[ ContentTypeHeader ];
                resultContent = response.Content;
            }
            catch( SocketException e )
            {
                // Flag an exception as a bad request and send the exception message
                // back to the caller.
                resultCode = WebDavStatusCode.BadRequest;
                resultReason = e.Message;
                contentType = null;
                resultContent = null;

                success = false;
            }

            return( success );
        }
        /// <summary>
        ///     Send a request to the server. This is a low level call
        ///     for callers who need (or want) access to all headers
        ///     returned by the server.
        /// </summary>
        /// <param name="resourcePath">
        ///     The full path to the resource being acted upon.
        /// </param>
        /// <param name="method">
        ///     The name of the method to use (a valid HTTP or
        ///     WebDAV command.
        /// </param>
        /// <param name="extraHeaders">
        ///     An array of extra headers that should be sent in the
        ///     request.
        /// </param>
        /// <param name="contentType">
        ///     The type of data being sent with the request.
        /// </param>
        /// <param name="content">
        ///     The content body of the request.
        /// </param>
        /// <param name="sendProgress">
        ///     The number of bytes sent so far.
        /// </param>
        /// <param name="receiveProgress">
        ///     The number of bytes received so far.
        /// </param>
        /// <returns>
        ///     The server's response.
        /// </returns>
        protected DavResponse SendRequest( string resourcePath,
            string method,
            string[] extraHeaders,
            string contentType,
            byte[] content,
            ProgressContainer sendProgress,
            ProgressContainer receiveProgress)
        {
            // Make sure required parameters are valid.
            if( ( resourcePath == null ) || ( resourcePath.Length == 0 ) )
            {
                throw new ArgumentException(
                    "The requested resource is invalid.", "resourcePath" );
            }

            if( Array.IndexOf( SupportedMethods, method, 0, SupportedMethods.Length ) < 0 )
            {
                throw new ArgumentException(
                    "The method " + method + " is not supported.", "method" );
            }

            // Determine if the message will include some type of content.
            bool msgHasContents = ( contentType != null ) &&
                ( content != null ) && ( content.Length > 0 );

            // Start the headers with the HTTP method, resource path, and protocol
            string headers = method + " " + resourcePath + " " + ProtocolVersion + HeaderTerminator;
            headers += "Host: " + this.ServerUri.Authority + HeaderTerminator;

            // Send authorization credentials if available.
            if( ( username != null ) || ( password != null ) )
            {
                string credentialsUser = username;
                string credentialsPass = password;

                if( credentialsUser == null )
                {
                    credentialsUser = "";
                }

                if( credentialsPass == null )
                {
                    credentialsPass = "";
                }

                byte[] credentials = Encoding.UTF8.GetBytes( username + ":" + password );

                Console.WriteLine( "Credentials = " + credentials );

                string encodedCredentials = System.Convert.ToBase64String(
                     credentials , 0, credentials.Length );

                Console.WriteLine( "encoded credentials = " + encodedCredentials );

                headers = headers + BasicCredentialsHeader + ": " +
                    BasicCredentialsType + " " + encodedCredentials + HeaderTerminator;

                Console.WriteLine( "headers = " + headers );
            }

            // Add content headers, if any
            if( msgHasContents )
            {
                headers = headers + ContentLengthHeader + ": " + content.Length + HeaderTerminator;
                headers = headers + ContentTypeHeader + ": " + contentType + HeaderTerminator;
            }

            // Add user specified headers
            if( ( extraHeaders != null ) && ( extraHeaders.Length > 0 ) )
            {
                foreach( string header in extraHeaders )
                {
                    headers = headers + header + HeaderTerminator;
                }
            }

            // Finish the headers section
            headers = headers + HeaderTerminator;

            // Translate the headers into a byte array.
            byte[] headerBytes = Encoding.ASCII.GetBytes( headers );

            // Set the total message length to the length of the headers.
            int msgLength = headerBytes.Length;

            // If there is any content in the message, add the size of the content to
            // the total message size.
            if( msgHasContents )
            {
                msgLength = msgLength + content.Length;
            }

            // Create the message array
            byte[] msg = new byte[ msgLength ];

            // Copy the header bytes.
            Array.Copy( headerBytes, 0, msg, 0, headerBytes.Length );

            // If the message has contents, copy those bytes.
            if( msgHasContents )
            {
                Array.Copy( content, 0, msg, headerBytes.Length, content.Length );
            }

            //Uncomment the following call to see the message before it is sent.
            /*
               Console.WriteLine( "sending message:\n||{0}||\n",
                Encoding.UTF8.GetString( msg ) );
            */

            // Create a new TCP client to send the message and get a response.
            TcpClient comm = new TcpClient();

            Console.WriteLine("connecting to host {0} at port {1}",
                ServerUri.Host, ServerUri.Port );
            // Connect to the server
            comm.Connect( serverUri.Host, ServerUri.Port );

            NetworkStream stream = comm.GetStream();

            // Send the message.
            if( sendProgress != null )
            {
                sendProgress.TotalBytesExpected = (ulong) msg.Length;
            }

            int sentBytes;
            int bytesToSend;

            for( sentBytes = 0; sentBytes < msg.Length; sentBytes = sentBytes + bytesToSend )
            {
                bytesToSend = comm.SendBufferSize;

                if( sentBytes + bytesToSend > msg.Length )
                {
                    bytesToSend = msg.Length - sentBytes;
                }

                stream.Write( msg, sentBytes, bytesToSend );

                if( sendProgress != null )
                {
                    sendProgress.TotalBytes = (ulong) sentBytes;
                }
            }

            if( sendProgress != null )
            {
                sendProgress.TotalBytes = (ulong) sentBytes;
            }

            // Prepare to receive a response
            byte[] responseBuffer = new byte[ comm.ReceiveBufferSize ];

            int bytesRead = stream.Read( responseBuffer, 0, responseBuffer.Length );

            bool finishedHeaders = false;

            DavResponse response = null;

            // Keep reading bytes from the stream until there are none left. Each time
            // bytes are read, convert them into a string.
            while( bytesRead > 0 )
            {
                if( !finishedHeaders )
                {
                    string responseString =
                        Encoding.UTF8.GetString( responseBuffer, 0, bytesRead );

                    int currentIndex = 0;
                    int endHeaderLine = responseString.IndexOf( HeaderTerminator );
                    int endOfHeaders = responseString.IndexOf( HeaderTerminator + HeaderTerminator );

                    if( endOfHeaders == -1 )
                    {
                        endOfHeaders = responseString.Length;
                    }

                    string currentHeader = null;

                    while( ( currentIndex < endOfHeaders ) &&
                        ( currentIndex < responseString.Length ) &&
                        ( endHeaderLine >= 0 ) )
                    {
                        string line = responseString.Substring(
                            currentIndex, endHeaderLine - currentIndex );

                        // If the response hasn't been created, the first line is
                        // the status code and needs to be passed to the constructor.
                        if( response == null )
                        {
                            response = new DavResponse( line );
                        }
                        // If there is no current header, the line becomes the current
                        // header.
                        else if( currentHeader == null )
                        {
                            currentHeader = line;
                        }
                        // If the current header is not null and the new line starts
                        // with a space or tab character, it is a continuation of the
                        // current header.
                        else if( ( currentHeader != null ) &&
                            ( line.StartsWith( " " ) || line.StartsWith( "\t" ) ) )
                        {
                            currentHeader = currentHeader + line;
                        }
                        // Otherwise, the line is a new header.
                        else
                        {
                            response.AddHeader( currentHeader );
                            currentHeader = line;
                        }

                        currentIndex =
                            currentIndex + ( endHeaderLine - currentIndex ) + HeaderTerminator.Length;
                        endHeaderLine = responseString.IndexOf( HeaderTerminator, currentIndex );

                        if( ( endHeaderLine > endOfHeaders ) &&
                            ( currentIndex > endOfHeaders ) )
                        {
                            currentIndex = currentIndex + HeaderTerminator.Length;
                        }
                    }

                    // Add the last header.
                    if( currentHeader != null )
                    {
                        response.AddHeader( currentHeader );
                        finishedHeaders = true;

                        if( receiveProgress != null )
                        {
                            receiveProgress.TotalBytesExpected = (ulong) response.ExpectedContentLength;
                        }
                    }

                    // If there are additional characters after the headers that have
                    // been unhandled and either the response includes a content or
                    // a Content-Length header was not provided (ie unknown content
                    // length), add whatever bytes that were read but not processed
                    // to the response content.
                    if( ( currentIndex < responseString.Length ) &&
                        ( ( response.ExpectedContentLength > 0 ) ||
                            ( response.ExpectedContentLength == -1 ) ) )
                    {
                        byte[] processedBytes = Encoding.UTF8.GetBytes(
                            responseString.Substring( 0, currentIndex ) );

                        int contentBeginIndex = processedBytes.Length;

                        response.AppendContent( responseBuffer,
                            contentBeginIndex, bytesRead - contentBeginIndex );

                        if( receiveProgress != null )
                        {
                            receiveProgress.TotalBytes =
                                receiveProgress.TotalBytes + (ulong) ( bytesRead - contentBeginIndex );
                        }
                    }
                }
                else
                {
                    response.AppendContent( responseBuffer, bytesRead );

                    if( receiveProgress != null )
                    {
                        receiveProgress.TotalBytes = receiveProgress.TotalBytes + (ulong) bytesRead;
                    }
                }

                // Try to read more bytes from the stream.
                bytesRead = stream.Read( responseBuffer, 0, responseBuffer.Length );
            }

            // Close the connection
            comm.Close();

            //Console.WriteLine( "Receieved response:\n{0}\n--------------------\n\n",
            //    response );

            return( response );
        }
        /// <summary>
        ///     Returns the list of resources in the collection.
        /// </summary>
        /// <param name="path">
        ///     The path of the resource. It must be non-null and
        ///     contain at least one character.
        /// </param>
        /// <param name="resultCode">
        ///     The result code from the server or BadRequest if an
        ///    exception was thrown.
        /// </param>
        /// <param name="resultReason">
        ///     The result message from the server or the exception
        ///     message if an exception was thrown.
        /// </param>
        /// <param name="resources">
        ///     The resources contained in the collection, or null if
        ///     there were no resources, or the path did not resolve
        ///     to a collection itself.
        /// </param>
        /// <param name="sizes">
        ///     The sizes of the resources in the collection.
        /// </param>
        /// <param name="sendProgress">
        ///     Used to monitor the progress of sending the request.
        /// </param>
        /// <param name="receiveProgress">
        ///     Used to monitor the progress of receiving the response.
        /// </param>
        /// <returns>
        ///     true if the call succeeded, false if a communication
        ///     error prevented the request from succeeding. If true,
        ///     the caller should examine the result code and reason
        ///     to determine whether or not the properties are valid.
        /// </returns>
        public bool CollectionContents( string path,
            out WebDavStatusCode resultCode,
            out string resultReason,
            out string[] resources,
            out long[] sizes,
            ProgressContainer sendProgress,
            ProgressContainer receiveProgress)
        {
            bool success = true;
            sizes = null;
            // Make sure the input parameters are valid.
            if( ( path == null ) || ( path.Length == 0 ) )
            {
                throw new ArgumentException( "Invalid resource path.", "path" );
            }

            // Try to get the properties.
            try
            {
                string[] propertyKeys = new string[] { DavPropertyUtil.DavResourceTypeProp,
                                                     DavPropertyUtil.DavContentLengthProp};

                byte[] content = DavPropertyUtil.GetPropfindRequestContent( propertyKeys );

                string[] propfindHeaders = new string[ 1 ];
                propfindHeaders[ 0 ] = "Depth: 1";

                DavResponse response = SendRequest( path, PropfindRequest,
                    propfindHeaders, XMLTextContent, content );

                // Extract the response data for the caller.
                resultCode = response.StatusCode;
                resultReason = response.StatusReason;

                string contentType = (string) response.Headers[ ContentTypeHeader ];
                string resultContent =
                    Encoding.UTF8.GetString( response.Content, 0, response.ContentLength );

                resources = null;

                if( resultCode == WebDavStatusCode.MultiStatus )
                {
                    Hashtable resourceInfo =
                        DavPropertyUtil.ParseMultiStatus( response.Content );

                    if( resourceInfo.Count > 1 )
                    {
                        // Do not return the resource path for the parent.
                        resources = new string[ resourceInfo.Count - 1 ];
                        sizes = new long[ resourceInfo.Count - 1 ];

                        int i = 0;
                        foreach( string resKey in resourceInfo.Keys)
                        {
                            if( !resKey.Equals( path ) )
                            {
                                resources[ i ] = resKey;
                                Hashtable tab = ((Hashtable)((Hashtable)
                                    resourceInfo[resKey])[WebDavStatusCode.OK]);
                                if(tab.ContainsKey( DavPropertyUtil.DavContentLengthProp ))
                                    sizes[i] = long.Parse(tab[DavPropertyUtil.DavContentLengthProp].ToString());
                                else
                                    sizes[i] = -1;

                                i++;
                            }
                        }
                    }
                }
            }
            catch( SocketException e )
            {
                // Flag an exception as a bad request and send the exception message
                // back to the caller.
                resultCode = WebDavStatusCode.BadRequest;
                resultReason = e.Message;
                resources = null;

                success = false;
            }
            catch( XmlException e )
            {
                // Flag an exception as a bad request and send the exception message
                // back to the caller.
                resultCode = WebDavStatusCode.BadRequest;
                resultReason = e.Message;
                resources = null;

                success = false;
            }

            return( success );
        }
        /// <summary>
        ///     A general purpose test method.
        /// </summary>
        public bool Test()
        {
            try
            {
                WebDavStatusCode resultCode;
                string resultReason;
                string contentType;
                byte[] resultContent;

                bool success = GetResource( "/", out resultCode, out resultReason, out contentType, out resultContent );
                if( success )
                {
                    Console.WriteLine("Get (not monitored) result = {0}, reason = {1}, type = {2}, length = {3}",
                        resultCode, resultReason, contentType, resultContent.Length );
                    Console.WriteLine("Content = {0}",
                        Encoding.UTF8.GetString( resultContent, 0, resultContent.Length ) );
                }

                ProgressContainer sendContainer = new ProgressContainer();
                ProgressContainer receiveContainer = new ProgressContainer();
                success = GetResource( "/putty.exe",
                    out resultCode, out resultReason, out contentType, out resultContent, sendContainer, receiveContainer );
                if( success )
                {
                    Console.WriteLine("Get (monitored) result = {0}, reason = {1}, type = {2}, length = {3}",
                        resultCode, resultReason, contentType, resultContent.Length );
                }

                return( true );
            }
            catch( SocketException e )
            {
                Console.WriteLine( "Connection error: {0}", e.Message );
                return( false );
            }
        }
        /// <summary>
        ///     A template for a MOVE or COPY command
        /// </summary>
        /// <param name="command">
        ///     HTTP command to send
        /// </param>
        /// <param name="source">
        ///     The source path. It must be non-null and
        ///     contain at least one character.
        /// </param>
        /// <param name="destination">
        ///     The destination path. It must be non-null and
        ///     contain at least one character.
        /// </param>
        /// <param name="allowOverwrite">
        ///     Flag to allow overwrite
        /// </param>
        /// <param name="resultCode">
        ///     The result code from the server or BadRequest if an
        ///     exception was thrown.
        /// </param>
        /// <param name="resultReason">
        ///     The result message from the server or the
        ///     exception message if an exception was thrown.
        /// </param>
        /// <param name="contentType">
        ///     The reported type of the content returned from the
        ///     server.
        /// </param>
        /// <param name="resultContent">
        ///     The content of the resource or null if there was an
        ///     error.
        /// </param>
        /// <param name="sendProgress">
        ///     Used to monitor the progress of sending the request.
        /// </param>
        /// <param name="receiveProgress">
        ///     Used to monitor the progress of receiving the response.
        /// </param>
        /// <returns>
        ///     true if the call succeeded, false if a communication
        ///     error prevented the request from succeeding. If true,
        ///     the caller should examine the result code and reason
        ///     to determine whether or not the result content is
        ///     valid.
        /// </returns>
        protected bool ChangeResourceCmd(
            string command,
            string source,
            string destination,
            bool allowOverwrite,
            out WebDavStatusCode resultCode,
            out string resultReason,
            out string contentType,
            out byte[] resultContent,
            ProgressContainer sendProgress,
            ProgressContainer receiveProgress)
        {
            bool success = true;

            // Make sure the input parameters are valid.
            if( ( source == null ) || ( source.Length == 0 )
                || ( destination == null ) || ( destination.Length == 0 ))
            {
                throw new ArgumentException( "Invalid resource path.", "path" );
            }

            // Try to copy the resource.
            try
            {
                string[] headers = new string[3];
                destination = destination.TrimStart('/');

                headers[0] = "Host: " + this.serverUri.Host;
                headers[1] = "Destination: " + this.serverUri + destination;
                if(allowOverwrite)
                    headers[2] = "Overwrite: T";
                else
                    headers[2] = "Overwrite: F";

                DavResponse response = SendRequest( source, command,
                    headers, null, null, sendProgress, receiveProgress );

                // Extract the response data for the caller.
                resultCode = response.StatusCode;
                resultReason = response.StatusReason;
                contentType = (string) response.Headers[ ContentTypeHeader ];
                resultContent = response.Content;
            }
            catch( SocketException e )
            {
                // Flag an exception as a bad request and send the exception message
                // back to the caller.
                resultCode = WebDavStatusCode.BadRequest;
                resultReason = e.Message;
                contentType = null;
                resultContent = null;

                success = false;
            }

            return( success );
        }
        /// <summary>
        ///     Get all properties associated with a resource.
        /// </summary>
        /// <param name="path">
        ///     The path of the resource. It must be non-null and
        ///     contain at least one character.
        /// </param>
        /// <param name="resultCode">
        ///     The result code from the server or BadRequest if an
        ///     exception was thrown.
        /// </param>
        /// <param name="resultReason">
        ///     The result message from the server or the exception
        ///     message if an exception was thrown.
        /// </param>
        /// <param name="resultContent">
        ///     A hashtable with the properties of the resource or
        ///     null if there was an error.
        /// </param>
        /// <param name="sendProgress">
        ///     Used to monitor the progress of sending the request.
        /// </param>
        /// <param name="receiveProgress">
        ///     Used to monitor the progress of receiving the response.
        /// </param>
        /// <returns>
        ///     true if the call succeeded, false if a communication
        ///     error prevented the request from succeeding. If true,
        ///     the caller should examine the result code and reason
        ///     to determine whether or not the properties are valid.
        /// </returns>
        public bool PropfindAll( string path,
            out WebDavStatusCode resultCode,
            out string resultReason,
            out Hashtable properties,
            ProgressContainer sendProgress,
            ProgressContainer receiveProgress)
        {
            bool success = true;
            Hashtable prop = new Hashtable();

            // Make sure the input parameters are valid.
            if( ( path == null ) || ( path.Length == 0 ) )
            {
                throw new ArgumentException( "Invalid resource path.", "path" );
            }

            // Try to get the properties.
            try
            {
                byte[] content = DavPropertyUtil.GetPropfindAllRequestContent();

                string[] propfindHeaders = new string[ 1 ];
                propfindHeaders[ 0 ] = "Depth: 1";

                DavResponse response = SendRequest( path, PropfindRequest,
                    propfindHeaders, XMLTextContent, content, sendProgress, receiveProgress );

                // Extract the response data for the caller.
                resultCode = response.StatusCode;
                resultReason = response.StatusReason;

                string contentType = (string) response.Headers[ ContentTypeHeader ];
                string resultContent =
                    Encoding.UTF8.GetString( response.Content, 0, response.ContentLength );

                if( resultCode == WebDavStatusCode.MultiStatus )
                {
                    DavPropertyUtil.ParseMultiStatus( response.Content, prop );
                    properties = prop;
                }
                else
                {
                    properties = null;
                }
            }
            catch( SocketException e )
            {
                // Flag an exception as a bad request and send the exception message
                // back to the caller.
                resultCode = WebDavStatusCode.BadRequest;
                resultReason = e.Message;
                properties = null;

                success = false;
            }
            catch (XmlException e)
            {
                // Flag an exception as a bad request and send the exception message
                // back to the caller.
                resultCode = WebDavStatusCode.BadRequest;
                resultReason = e.Message;
                properties = null;
                success = false;
            }
            return( success );
        }
        /// <summary>
        /// Retrieve the names available properties on a resource.
        /// </summary>
        /// <param name="path">
        ///     The path of the resource. It must be non-null and
        ///     contain at least one character.
        /// </param>
        /// <param name="resultCode">
        ///     The result code from the server or BadRequest if an
        ///     exception was thrown.
        /// </param>
        /// <param name="resultReason">
        ///     The result message from the server or the exception
        ///     message if an exception was thrown.
        /// </param>
        /// <param name="propertyNames">
        ///     An array of property names. Just because a property is listed here does
        ///     not mean the caller has permission to retrieve the value.
        /// </param>
        /// <param name="resultReason">
        ///     The result message from the server or the exception
        ///     message if an exception was thrown.
        /// </param>
        /// <param name="resultContent">
        ///     A hashtable with the properties of the resource or
        ///     null if there was an error.
        /// </param>
        /// <returns>
        ///     true if the call succeeded, false if a communication
        ///     error prevented the request from succeeding. If true,
        ///     the caller should examine the result code and reason
        ///     to determine whether or not the properties are valid.
        /// </returns>
        public bool Propnames( string path, out WebDavStatusCode resultCode,
            out string resultReason, out string[] propertyNames,
            ProgressContainer sendProgress,
            ProgressContainer receiveProgress)
        {
            bool success = true;

            // Make sure the input parameters are valid.
            if( ( path == null ) || ( path.Length == 0 ) )
            {
                throw new ArgumentException( "Invalid resource path.", "path" );
            }

            // Try to get the properties.
            try
            {
                byte[] content = DavPropertyUtil.GetPropfindNamesRequestContent();

                string[] propfindHeaders = new string[ 1 ];
                propfindHeaders[ 0 ] = "Depth: 0";

                DavResponse response = SendRequest( path, PropfindRequest,
                    propfindHeaders, XMLTextContent, content, sendProgress, receiveProgress );

                // Extract the response data for the caller.
                resultCode = response.StatusCode;
                resultReason = response.StatusReason;

                string contentType = (string) response.Headers[ ContentTypeHeader ];
                string resultContent =
                    Encoding.UTF8.GetString( response.Content, 0, response.ContentLength );

                propertyNames = null;

                if( resultCode == WebDavStatusCode.MultiStatus )
                {
                    Hashtable statusTable = (Hashtable)
                        DavPropertyUtil.ParseMultiStatus( response.Content )[ path ];

                    if( statusTable.ContainsKey( WebDavStatusCode.OK ) )
                    {
                        Hashtable availableProps =
                            (Hashtable) statusTable[ WebDavStatusCode.OK ];

                        // Subtract 2 keys (the result code and result string are not props)
                        propertyNames = new string[ availableProps.Count - 2 ];
                        int i = 0;

                        foreach( string prop in availableProps.Keys )
                        {
                            if( !prop.Equals( DavPropertyUtil.DAVSharpReasonCodeKey ) &&
                                !prop.Equals( DavPropertyUtil.DAVSharpReasonStringKey ) )
                            {
                                propertyNames[ i ] = prop;
                                i++;
                            }
                        }
                    }
                }
            }
            catch( SocketException e )
            {
                // Flag an exception as a bad request and send the exception message
                // back to the caller.
                resultCode = WebDavStatusCode.BadRequest;
                resultReason = e.Message;
                propertyNames = null;

                success = false;
            }
            catch( XmlException e )
            {
                // Flag an exception as a bad request and send the exception message
                // back to the caller.
                resultCode = WebDavStatusCode.BadRequest;
                resultReason = e.Message;
                propertyNames = null;

                success = false;
            }

            return( success );
        }
        /// <summary>
        ///     Get the specified properties associated with a
        ///     resource.
        /// </summary>
        /// <param name="path">
        ///     The path of the resource. It must be non-null and
        ///     contain at least one character.
        /// </param>
        /// <param name="propertyKey">
        ///     The key of the property to be retrieved.
        /// </param>
        /// <param name="resultCode">
        ///     The result code from the server or BadRequest if an
        ///     exception was thrown.
        /// </param>
        /// <param name="resultReason">
        ///     The result message from the server or the exception
        ///     message if an exception was thrown.
        /// </param>
        /// <param name="property">
        ///     A hashtable with the properties of the resource or
        ///     null if there was an error.
        /// </param>
        /// <param name="sendProgress">
        ///     Used to monitor the progress of sending the request.
        /// </param>
        /// <param name="receiveProgress">
        ///     Used to monitor the progress of receiving the response.
        /// </param>
        /// <returns>
        ///     true if the call succeeded, false if a communication
        ///     error prevented the request from succeeding. If true,
        ///     the caller should examine the result code and reason
        ///     to determine whether or not the properties are valid.
        /// </returns>
        public bool Propfind( string path,
            string propertyKey,
            out WebDavStatusCode resultCode,
            out string resultReason,
            out object property,
            ProgressContainer sendProgress,
            ProgressContainer receiveProgress)
        {
            string[] propertyList = new string[] { propertyKey };
            Hashtable properties;

            bool success = Propfind(path,
                propertyList,
                out resultCode,
                out resultReason,
                out properties,
                sendProgress,
                receiveProgress );

            property = null;

            if( success && ( properties != null ) )
            {
                foreach( WebDavStatusCode statusCode in properties.Keys )
                {
                    Hashtable propInfo = (Hashtable) properties[ statusCode ];

                    if( propInfo.ContainsKey( propertyKey ) )
                    {
                        resultCode = (WebDavStatusCode)
                            propInfo[ DavPropertyUtil.DAVSharpReasonCodeKey ];
                        resultReason  = (string)
                            propInfo[ DavPropertyUtil.DAVSharpReasonStringKey ];
                        property = propInfo[ propertyKey ];
                    }
                }
            }

            return( success );
        }
        /// <summary>
        /// Make a new collection at the specified path.
        /// </summary>
        /// <param name="path">
        ///     The path of the resource. It must be non-null and
        ///     contain at least one character.
        /// </param>
        /// <param name="resultCode">
        ///     The result code from the server or BadRequest if an
        ///     exception was thrown.
        /// </param>
        /// <param name="resultReason">
        ///     The result message from the server or the exception
        ///     message if an exception was thrown.
        /// </param>
        /// <param name="sendProgress">
        ///     Used to monitor the progress of sending the request.
        /// </param>
        /// <param name="receiveProgress">
        ///     Used to monitor the progress of receiving the response.
        /// </param>
        /// <returns>
        ///     true if the call succeeded, false if a communication
        ///     error prevented the request from succeeding. If true,
        ///     the caller should examine the result code and reason
        ///     to determine whether or not the properties are valid.
        /// </returns>
        public bool Mkcol( string resourcePath, 
            out WebDavStatusCode resultCode, out string resultReason,
            ProgressContainer sendProgress,
            ProgressContainer receiveProgress)
        {
            bool success = true;

            // Try to get the resource.
            try
            {
                DavResponse response = SendRequest(resourcePath, MkcolRequest,
                    null, null, null, sendProgress, receiveProgress );

                // Extract the response data for the caller.
                resultCode = response.StatusCode;
                resultReason = response.StatusReason;
            }
            catch( SocketException e )
            {
                // Flag an exception as a bad request and send the exception message
                // back to the caller.
                resultCode = WebDavStatusCode.BadRequest;
                resultReason = e.Message;

                success = false;
            }
            return success;
        }
        /// <summary>
        ///     Locks a resource.
        /// </summary>
        /// <param name="path">
        ///     The path of the resource. It must be non-null and
        ///     contain at least one character.
        /// </param>
        /// <param name="resultCode">
        ///     The result code from the server or BadRequest
        ///     if an exception was thrown.
        /// </param>
        /// <param name="resultReason">
        ///     The result message from the server or the
        ///     exception message if an exception was thrown.
        /// </param>
        /// <param name="contentType">
        ///     The reported type of the content returned
        ///     from the server.
        /// </param>
        /// <param name="resultContent">
        ///     The content of the resource or null if
        ///     there was an error.
        /// </param>
        /// <param name="lockToken">
        ///     The lock token if the resource is locked or null
        ///     if the resource could not be locked.
        /// </param>
        /// <param name="sendProgress">
        ///     Used to monitor the progress of sending the request.
        /// </param>
        /// <param name="receiveProgress">
        ///     Used to monitor the progress of receiving the response.
        /// </param>
        /// <returns>
        ///     true if the call succeeded, false if a communication error
        ///     prevented the request from succeeding. If true, the caller should
        ///     examine the result code and reason to determine whether or not the
        ///     result content is valid.
        /// </returns>
        public bool LockResource(string path,
            out WebDavStatusCode resultCode,
            out string resultReason,
            out string contentType,
            out byte[] resultContent,
            out string lockToken,
            ProgressContainer sendProgress,
            ProgressContainer receiveProgress)
        {
            // Local variables
            bool success = true;
            string CR = "\n";
            string xmlFluff = XMLContentPreamble + CR
                + "<D:lockinfo xmlns:D=\"DAV:\">" + CR
                + " <D:lockscope><D:exclusive/></D:lockscope>" + CR
                + " <D:locktype><D:write/></D:locktype>" + CR
                + " <D:owner>" + CR
                + "  <D:href>http://whisper.cse.ucsc.edu:16080/csharp</D:href>" + CR
                + " </D:owner>" + CR
                + "</D:lockinfo>" + CR;
            string[] headers = new string[] {
                                                "Depth: infinity"
                                            };
            lockToken = null;

            // Make sure the input parameters are valid.
            if( ( path == null ) || ( path.Length == 0 ) )
            {
                throw new ArgumentException( "Invalid resource path.", "path" );
            }

            // Try to lock the resource.
            try
            {
                DavResponse response = SendRequest(path, LockRequest, null, XMLTextContent,
                    System.Text.Encoding.UTF8.GetBytes(xmlFluff), sendProgress, receiveProgress );

                // Extract the response data for the caller.
                resultCode = response.StatusCode;
                resultReason = response.StatusReason;
                contentType = (string) response.Headers[ ContentTypeHeader ];
                resultContent = response.Content;

                // Extract lock token from XML body
                if (response.StatusCode == WebDavStatusCode.OK)
                {
                    System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
                    doc.Load(new System.IO.StringReader(
                        System.Text.Encoding.UTF8.GetString(
                            response.Content, 0, response.ContentLength ) ) );
                    System.Xml.XmlNodeReader xmlReader = new System.Xml.XmlNodeReader(doc);
                    do { xmlReader.Read(); } while (xmlReader.Name != "D:locktoken");
                    do { xmlReader.Read(); } while (xmlReader.Name != "D:href");
                    xmlReader.Read();
                    lockToken = xmlReader.Value.Trim();
                }
            }
            catch( SocketException e )
            {
                // Flag an exception as a bad request and send the exception message
                // back to the caller.
                resultCode = WebDavStatusCode.BadRequest;
                resultReason = e.Message;
                contentType = null;
                resultContent = null;
                lockToken = null;
                success = false;
            }

            return( success );
        }
        /// <summary>
        ///     Determine if the resource at the given path is a
        ///     collection or not.
        /// </summary>
        /// <param name="path">
        ///     The path of the resource. It must be non-null and
        ///     contain at least one character.
        /// </param>
        /// <param name="resultCode">
        ///     The result code from the server or BadRequest if an
        ///     exception was thrown.
        /// </param>
        /// <param name="resultReason">
        ///     The result message from the server or the exception
        ///     message if an exception was thrown.
        /// </param>
        /// <param name="sendProgress">
        ///     Used to monitor the progress of sending the request.
        /// </param>
        /// <param name="receiveProgress">
        ///     Used to monitor the progress of receiving the response.
        /// </param>
        /// <returns>
        ///     true if the call succeeded, false if a communication
        ///     error prevented the request from succeeding. If true,
        ///     the caller should examine the result code and reason
        ///     to determine whether or not the properties are valid.
        /// </returns>
        public bool IsCollection( string path,
            out WebDavStatusCode resultCode,
            out string resultReason,
            out bool isCollection,
            ProgressContainer sendProgress,
            ProgressContainer receiveProgress)
        {
            bool success = false;

            object typeInfo;
            isCollection = false;

            if( GetResourceType( path,
                out resultCode,
                out resultReason,
                out typeInfo,
                sendProgress,
                receiveProgress ) &&
                ( typeInfo != null ) )
            {
                success = true;
                Hashtable typeInfoTable = (Hashtable) typeInfo;
                isCollection =
                    typeInfoTable.ContainsKey( DavPropertyUtil.DavCollectionProp );
            }

            return( success );
        }
        /// <summary>
        ///     Returns the type info associated with a resource. By
        ///     default, resources have an empty resourcetype.
        ///     Collections have an empty sub-element DAV:collection
        ///     to flag the resource as a collection.
        /// </summary>
        /// <param name="path">
        ///     The path of the resource. It must be non-null and
        ///     contain at least one character.
        /// </param>
        /// <param name="resultCode">
        ///     The result code from the server or BadRequest if an
        ///     exception was thrown.
        /// </param>
        /// <param name="resultReason">
        ///     The result message from the server or the exception
        ///     message if an exception was thrown.
        /// </param>
        /// <param name="typeInfo">
        ///     The resource type information as stored on the server.
        /// </param>
        /// <param name="sendProgress">
        ///     Used to monitor the progress of sending the request.
        /// </param>
        /// <param name="receiveProgress">
        ///     Used to monitor the progress of receiving the response.
        /// </param>
        /// <returns>
        ///     true if the call succeeded, false if a communication
        ///     error prevented the request from succeeding. If true,
        ///     the caller should examine the result code and reason
        ///     to determine whether or not the properties are valid.
        /// </returns>
        public bool GetResourceType( string path,
            out WebDavStatusCode resultCode,
            out string resultReason,
            out object typeInfo,
            ProgressContainer sendProgress,
            ProgressContainer receiveProgress)
        {
            bool success = Propfind( path, DavPropertyUtil.DavResourceTypeProp,
                out resultCode, out resultReason, out typeInfo, sendProgress, receiveProgress );

            return( success );
        }