Esempio n. 1
0
        /// <summary>
        ///     Used to build a response with an error
        /// </summary>
        /// <param name="response">The response that comes from the controller</param>
        /// <param name="errorMessage">The message to put in the error.</param>
        /// <param name="errorCode">The code of the error.</param>
        /// <param name="href">The requested href.</param>
        /// <returns></returns>
        private async Task ReturnError(HttpResponse response, string errorMessage, int errorCode, string href)
        {
            //build the root of the xml
            var multistatusNode = new XmlTreeStructure("multistatus", "DAV:")
            {
                Namespaces = new Dictionary <string, string>
                {
                    { "D", "DAV:" },
                    { "C", "urn:ietf:params:xml:ns:caldav" }
                }
            };

            //each returned resource has is own response and href nodes
            var responseNode = new XmlTreeStructure("response", "DAV:");
            var hrefNode     = new XmlTreeStructure("href", "DAV:");

            hrefNode.AddValue(href);

            //href is a child pf response
            responseNode.AddChild(hrefNode);

            IXMLTreeStructure statusNode = new XmlTreeStructure("status", "DAV:");

            statusNode.AddValue($"HTTP/1.1 {errorCode} {errorMessage}");
            responseNode.AddChild(statusNode);

            multistatusNode.AddChild(responseNode);

            await response.WriteAsync(multistatusNode.ToString());
        }
Esempio n. 2
0
        /// <summary>
        ///     Take the calendar that passed the filter and
        ///     create the multi-status xml.
        /// </summary>
        /// <param name="resources">The resources to be returned</param>
        /// <param name="calDataNode">
        ///     THis is the node with name ="prop"
        ///     When used in a calendaring REPORT request, the CALDAV:calendar-data XML
        ///     element specifies which parts of calendar object resources need to be returned in the
        ///     response.If the CALDAV:calendar-data XML element doesn't contain any
        ///     CALDAV:comp element, calendar object resources will be returned in their entirety.
        /// </param>
        /// <param name="httpContext"></param>
        /// <returns>The string representation of the multi-status Xml with the results.</returns>
        public async Task ReportResponseBuilder(IEnumerable <KeyValuePair <string, VCalendar> > resources,
                                                IXMLTreeStructure calDataNode, HttpContext httpContext)
        {
            var multistatusNode = new XmlTreeStructure("multistatus", "DAV:")
            {
                Namespaces = new Dictionary <string, string>
                {
                    { "D", "DAV:" },
                    { "C", "urn:ietf:params:xml:ns:caldav" }
                }
            };

            //take the node that specified the comp and properties
            //to return


            foreach (var resource in resources)
            {
                IXMLTreeStructure statusNode;

                //each returned resource has is own response and nodes
                var responseNode = new XmlTreeStructure("response", "DAV:");
                var hrefNode     = new XmlTreeStructure("href", "DAV:");
                var href         = resource.Key[0] != '/' ? "/" + resource.Key : resource.Key;
                hrefNode.AddValue(href);

                //href is a child pf response
                responseNode.AddChild(hrefNode);

                //if the resource is null it was not foound so
                // add an error status
                if (resource.Value == null)
                {
                    statusNode = new XmlTreeStructure("status", "DAV:");
                    statusNode.AddValue("HTTP/1.1 404 Not Found");
                    responseNode.AddChild(statusNode);
                }
                else
                {
                    var propstatNode = new XmlTreeStructure("propstat", "DAV:");

                    //that the requested data
                    var propStats = await ProccessPropNode(calDataNode, resource);


                    foreach (var propStat in propStats)
                    {
                        responseNode.AddChild(propStat);
                    }
                }

                multistatusNode.AddChild(responseNode);
            }
            var responseText  = multistatusNode.ToString();
            var responseBytes = Encoding.UTF8.GetBytes(responseText);

            httpContext.Response.ContentLength = responseBytes.Length;
            await httpContext.Response.Body.WriteAsync(responseBytes, 0, responseBytes.Length);
        }
Esempio n. 3
0
        /// <summary>
        ///     Build the xml of the body and write
        ///     its string representation to the HttpRespose.Body
        /// </summary>
        /// <param name="response">The response of the request.</param>
        /// <param name="principalsAndProperties">The principals with its properties.</param>
        /// <returns></returns>
        public async Task WriteBody(HttpResponse response,
                                    Dictionary <Principal, IEnumerable <Property> > principalsAndProperties)
        {
            //build the root of the xml
            var multistatusNode = new XmlTreeStructure("multistatus", "DAV:")
            {
                Namespaces = new Dictionary <string, string>
                {
                    { "D", "DAV:" },
                    { "C", "urn:ietf:params:xml:ns:caldav" }
                }
            };

            //take the node that specified the comp and properties
            //to return

            foreach (var pp in principalsAndProperties)
            {
                IXMLTreeStructure statusNode;

                //each returned resource has is own response and href nodes
                var responseNode = new XmlTreeStructure("response", "DAV:");
                var hrefNode     = new XmlTreeStructure("href", "DAV:");
                hrefNode.AddValue(pp.Key.PrincipalURL);

                //href is a child pf response
                responseNode.AddChild(hrefNode);

                //if the resource is null it was not foound so
                // add an error status
                if (pp.Value == null)
                {
                    statusNode = new XmlTreeStructure("status", "DAV:");
                    statusNode.AddValue("HTTP/1.1 404 Not Found");
                    responseNode.AddChild(statusNode);
                }
                else
                {
                    var propstatNode = new XmlTreeStructure("propstat", "DAV:");
                    var propNode     = new XmlTreeStructure("prop", "DAV:");
                    //add the properties to the prop node.
                    foreach (var property in pp.Value)
                    {
                        propNode.AddChild(XmlTreeStructure.Parse(property.Value));
                    }

                    propstatNode.AddChild(propNode);
                    //adding the status node
                    // TODO: check the status!!
                    statusNode = new XmlTreeStructure("status", "DAV:");
                    statusNode.AddValue("HTTP/1.1 200 OK");

                    propstatNode.AddChild(statusNode);

                    responseNode.AddChild(propstatNode);
                }

                multistatusNode.AddChild(responseNode);
                await response.WriteAsync(multistatusNode.ToString());
            }
        }
Esempio n. 4
0
        /// <summary>
        ///     Having the principal and the requested properties
        ///     then proccess the requestedProperties and build the
        ///     response.
        /// </summary>
        /// <param name="response">The HttpResponse from the controller.</param>
        /// <param name="requestedUrl">The principal url if anyurl </param>
        /// <param name="reqProperties">Contains the requested properties for the principal. key=name, Value = ns</param>
        /// <param name="principal">The instance of the pricipal that is requested</param>
        /// <returns>The final response to return. Has the body with the response and the</returns>
        public async Task BuildResponse(HttpResponse response, string requestedUrl,
                                        List <KeyValuePair <string, string> > reqProperties, Principal principal)
        {
            //if the principal is not authenticated then set in the response statusCode
            if (principal == null)
            {
                response.StatusCode = StatusCodes.Status401Unauthorized;
            }

            var multistatusNode = new XmlTreeStructure("multistatus", "DAV:");

            multistatusNode.Namespaces.Add("D", "DAV:");
            multistatusNode.Namespaces.Add("C", "urn:ietf:params:xml:ns:caldav");
            IEnumerable <IXMLTreeStructure> properties = null;

            //create the response node.
            var responseNode = new XmlTreeStructure("response", "DAV:");

            //create the href node
            var hrefNode = new XmlTreeStructure("href", "DAV:");

            //var url = requestedUrl.Replace(SystemProperties._baseUrl , "");
            hrefNode.AddValue(requestedUrl);

            responseNode.AddChild(hrefNode);

            //in this section is where the "propstat" structure its build.
            var propstatNode = new XmlTreeStructure("propstat", "DAV:");

            var propNode = new XmlTreeStructure("prop", "DAV:");

            //check this because the principal could not be authenticated
            if (principal != null)
            {
                //add the requested properties to the propNode
                //if the properties exist in the principal
                properties = principal.Properties
                             .Where(p => reqProperties.Contains(new KeyValuePair <string, string>(p.Name, p.Namespace)))
                             .Select(x => XmlTreeStructure.Parse(x.Value));
            }

            //check the properties that are generated per request
            //and are not contained in the principal's properties
            foreach (var reqProperty in reqProperties)
            {
                //here the additional properties for the principal that
                //are created per request
                switch (reqProperty.Key)
                {
                case "current-user-principal":
                    propNode.AddChild(PropertyCreation.CreateCurrentUserPrincipal(principal));
                    break;

                case "principal-URL":
                    propNode.AddChild(new XmlTreeStructure("principal-URL", "DAV:")
                    {
                        Value = principal.PrincipalURL
                    });
                    break;
                }
            }

            if (properties != null)
            {
                //add the properties to the propNode
                foreach (var property in properties)
                {
                    propNode.AddChild(property);
                }
            }

            var statusNode = new XmlTreeStructure("status", "DAV:")
            {
                Value = "HTTP/1.1 200 OK"
            };

            //add the propNOde and the status node to the propStatNode
            propstatNode.AddChild(propNode).AddChild(statusNode);

            responseNode.AddChild(propstatNode);

            multistatusNode.AddChild(responseNode);

            //here the multistatus xml for the body is built
            //have to write it to the response body.

            var responseText  = multistatusNode.ToString();
            var responseBytes = Encoding.UTF8.GetBytes(responseText);

            response.ContentLength = responseBytes.Length;
            await response.Body.WriteAsync(responseBytes, 0, responseBytes.Length);
        }
Esempio n. 5
0
        //TODO:Nacho
        public async Task PropPatch(Dictionary <string, string> propertiesAndHeaders, string body, HttpResponse response)
        {
            #region Docummentation

            //Proppatch is the method used by WebDAV for update, create and delete properties.

            //The body structure of a Proppatch request is declare as a "proppertyupdate" xml.

            //As a child of the "proppertyupdate" there are list of "set" and "remove" indicating the
            //operations that have to be process. This element have to be process in order (top to bottom).

            //There has to be at least one expected element inside "proppertyupdate".

            //Each "set" element is composed by a "prop" element witch contains the property name and value
            //of the properties that have to created or updated (if exists or not).

            //The same happens for the "remove" elements but these don't include the value of the property inside
            //the "prop" element.

            #endregion

            #region Extracting Properties

            string calendarResourceId;
            propertiesAndHeaders.TryGetValue("calendarResourceID", out calendarResourceId);

            string url;
            propertiesAndHeaders.TryGetValue("url", out url);

            #endregion

            //Checking precondition
            PreconditionCheck = new ProppatchPrecondition(_collectionRespository, _resourceRespository);
            if (!await PreconditionCheck.PreconditionsOK(propertiesAndHeaders, response))
            {
                return;
            }

            //Creating and filling the root of the xml tree response
            //All response of a request is conformed by a "multistatus" element.
            var multistatus = new XmlTreeStructure("multistatus", "DAV:");
            multistatus.Namespaces.Add("D", "DAV:");
            multistatus.Namespaces.Add("C", "urn:ietf:params:xml:ns:caldav");

            response.ContentType = "application/xml";

            //getting the request body structure
            IXMLTreeStructure xmlTree;
            try
            {
                xmlTree = XmlTreeStructure.Parse(body);
            }
            catch (Exception)
            {
                response.StatusCode = StatusCodes.Status400BadRequest;
                return;
            }


            //checking that the request has propertyupdate node

            if (xmlTree.NodeName != "propertyupdate")
            {
                response.StatusCode = (int)HttpStatusCode.BadRequest;
                await
                response.WriteAsync(
                    @"Body in bad format, body of proppatch must contain ""propertyupdate"" xml element");

                return;
            }

            //throw new ArgumentException(@"Body in bad format, body of proppatch must contain ""propertyupdate"" xml element");

            var propertyupdate = xmlTree;
            //aliasing the list with all "set" and "remove" structures inside "propertyupdate".
            var setsAndRemoves = propertyupdate.Children;

            //propertyupdate must have at least one element
            if (setsAndRemoves.Count == 0)
            {
                response.StatusCode = (int)HttpStatusCode.BadRequest;
                await response.WriteAsync("propertyupdate must have at least one element");

                return;
                //throw new ArgumentException("propertyupdate must have at least one element");
            }


            //The structure of a response for a proppatch has a "multistatus"
            //as root inside it, there is only one response because depth is not allowed.
            //Inside the "response" is necessary to add a "propstat" for each property.
            //This "propstat" is built with a "prop" element containing just the property name
            //and a "status" with the exit status code.
            var responseTree = new XmlTreeStructure("response", "DAV:");
            multistatus.AddChild(responseTree);

            #region Adding the <D:href>/api/v1/collections/{userEmail}|{groupName}/{principalId}/{collectionName}/{calendarResourceId}?</D:href>

            var href = new XmlTreeStructure("href", "DAV:");

            href.AddValue(SystemProperties._baseUrl + url);


            responseTree.AddChild(href);

            #endregion

            //Proppatch is atomic, though when an error occurred in one property,
            //all failed, an all other properties received a "424 failed dependency".
            var hasError = false;

            //Here it is garanted that if an error occured during the processing of the operations
            //The changes will not be stored in db thanks to a rollback.


            //For each set and remove try to execute the operation if something fails
            //put the Failed Dependency Error to every property before and after the error
            //even if the operation for the property was succesfully changed.
            foreach (var setOrRemove in setsAndRemoves)
            {
                if (setOrRemove.NodeName == "set")
                {
                    hasError = await BuiltResponseForSet(url, calendarResourceId, hasError,
                                                         setOrRemove, responseTree);
                }
                else
                {
                    hasError = await BuiltResponseForRemove(url, calendarResourceId, hasError,
                                                            setOrRemove, responseTree);
                }
            }

            if (hasError)
            {
                ChangeToDependencyError(responseTree);
            }
            else
            {
                await _collectionRespository.SaveChangeAsync();
            }

            response.StatusCode = 207;
            await response.WriteAsync(multistatus.ToString());
        }
Esempio n. 6
0
        public async Task MkCalendar(Dictionary <string, string> propertiesAndHeaders, string body, HttpResponse response)
        {
            #region Extracting Properties

            string principalId;
            propertiesAndHeaders.TryGetValue("principalId", out principalId);

            string url;
            propertiesAndHeaders.TryGetValue("url", out url);

            #endregion

            propertiesAndHeaders.Add("body", body);

            PreconditionCheck = new MKCalendarPrecondition(StorageManagement, _collectionRespository);
            PosconditionCheck = new MKCalendarPosCondition(StorageManagement, _collectionRespository);

            //Checking that all precondition pass

            //Cheking Preconditions
            if (!await PreconditionCheck.PreconditionsOK(propertiesAndHeaders, response))
            {
                return;
            }

            //I create here the collection already but i wait for other comprobations before save the database.
            await CreateDefaultCalendar(propertiesAndHeaders);

            response.StatusCode = (int)HttpStatusCode.Created;


            //If it has not body and  Posconditions are OK, it is created with default values.
            if (string.IsNullOrEmpty(body))
            {
                if (!await PosconditionCheck.PosconditionOk(propertiesAndHeaders, response))
                {
                    await DeleteCalendarCollection(propertiesAndHeaders, response);

                    response.StatusCode = (int)HttpStatusCode.Forbidden;
                    await response.WriteAsync("Poscondition Failed");

                    return;
                }
                await _collectionRespository.SaveChangeAsync();

                return;
            }

            //If a body exist the it is parsed like an XmlTree
            var mkCalendarTree = XmlTreeStructure.Parse(body);

            //if it does not have set property it is treated as a empty body.
            if (mkCalendarTree.Children.Count == 0)
            {
                if (!await PosconditionCheck.PosconditionOk(propertiesAndHeaders, response))
                {
                    await DeleteCalendarCollection(propertiesAndHeaders, response);

                    response.StatusCode = (int)HttpStatusCode.Forbidden;
                    await response.WriteAsync("Poscondition Failed");

                    return;
                }
                await _collectionRespository.SaveChangeAsync();

                return;
            }

            //now it is assumed that the body contains a set
            var setTree = mkCalendarTree.GetChild("set");

            #region Response Construction in case of error

            //this only if error during processing.
            //Creating and filling the root of the xml tree response
            //All response of a request is conformed by a "multistatus" element.
            var multistatus = new XmlTreeStructure("multistatus", "DAV:");
            multistatus.Namespaces.Add("D", "DAV:");
            multistatus.Namespaces.Add("C", "urn:ietf:params:xml:ns:caldav");

            var responseTree = new XmlTreeStructure("response", "DAV:");
            multistatus.AddChild(responseTree);

            var href = new XmlTreeStructure("href", "DAV:");
            href.AddValue(SystemProperties._baseUrl + url);

            #endregion

            //Check if any error occurred during body processing.
            var hasError = await BuiltResponseForSet(url, null, false, setTree, responseTree);

            if (hasError)
            {
                await DeleteCalendarCollection(propertiesAndHeaders, response);

                response.ContentType = "application/xml";

                ChangeToDependencyError(responseTree);

                response.StatusCode = 207;
                await response.WriteAsync(multistatus.ToString());

                return;
            }

            //Checking Preconditions
            if (await PosconditionCheck.PosconditionOk(propertiesAndHeaders, response))
            {
                await _collectionRespository.SaveChangeAsync();

                return;
                // return createdMessage;
            }

            await DeleteCalendarCollection(propertiesAndHeaders, response);

            response.StatusCode = (int)HttpStatusCode.Forbidden;
            await response.WriteAsync("Poscondition Failed");
        }
Esempio n. 7
0
        //private CalDavContext db { get; }

        #endregion

        #region PROPFIND methods

        //TODO: Nacho
        /// <summary>
        ///     This PROFIND is used for the collection and the resources.
        /// </summary>
        /// <param name="propertiesAndHeaders">Put here: resourceURL, depth, calendarResourceId</param>
        /// <param name="body">The request body from the client.</param>
        /// <param name="response">
        ///     The Response property in the controller. We fill up the response object
        ///     with out response.
        /// </param>
        public async Task PropFind(Dictionary <string, string> propertiesAndHeaders, string body, HttpResponse response)
        {
            #region Extracting Properties

            string calendarResourceId;
            propertiesAndHeaders.TryGetValue("calendarResourceID", out calendarResourceId);

            string url;
            propertiesAndHeaders.TryGetValue("url", out url);

            //Taking depth form headers.
            //Depth 0 means that it looks for prop only in the collection
            //Depth 1 means that it looks in their childs too.
            //And infinitum that looks in the entirely tree.
            int    depth;
            string strDepth;
            propertiesAndHeaders.TryGetValue("depth", out strDepth);
            try
            {
                depth = strDepth != null?int.Parse(strDepth) : 0;
            }
            catch (Exception)
            {
                depth = -1;
            }

            #endregion

            //Creating and filling the root of the xml tree response
            //All response are composed of a "multistatus" xml element
            //witch contains a "response" element for each collection and resource analized witch url is included in a "href" element as a child of "response".
            //As a child of the "response" there is a list of "propstat". One for each different status obtained
            //trying to get the specified properties.
            //Inside every "propstatus" there are a xml element "prop" with all the properties that match with
            //the given "status" and a "status" xml containing the message of his "propstat".

            //Todo respuesta de propfind esta compuesta de un elemento xml "multistatus",
            //El cual contiene un elemento xml "response" por cada colleccion o recurso analizado.
            //Dentro de cada "response" hay una lista de "propstat", uno por cada status distinto obtenido
            //al intentar recobrar las propiedades especificadas.
            //Dentro de cada "propstatus" hay un xml "prop" con todas las propiedades que mapean con el
            //status correspondiente y un xml "status" que tiene el mensaje del estado de dicho "propstat".

            //checking Precondtions
            PreconditionCheck = new PropfindPrecondition(_collectionRespository, _resourceRespository);
            if (!await PreconditionCheck.PreconditionsOK(propertiesAndHeaders, response))
            {
                return;
            }

            response.StatusCode  = 207;
            response.ContentType = "application/xml";
            var responseTree = new XmlTreeStructure("multistatus", "DAV:");
            responseTree.Namespaces.Add("D", "DAV:");
            responseTree.Namespaces.Add("C", "urn:ietf:params:xml:ns:caldav");
            responseTree.Namespaces.Add("S", _namespacesSimple["S"]);

            //Tool that contains the methods for propfind.
            PropFindMethods = new CalDavPropfind(_collectionRespository, _resourceRespository);

            //if the body is empty assume that is an allprop request.
            if (string.IsNullOrEmpty(body))
            {
                await PropFindMethods.AllPropMethod(url, calendarResourceId, depth, null, responseTree);

                await response.WriteAsync(responseTree.ToString());

                return;
            }

            //parsing the body into a xml tree
            var xmlTree = XmlTreeStructure.Parse(body);

            //Managing if the body was ok
            if (xmlTree.NodeName != "propfind")
            {
                response.StatusCode = (int)HttpStatusCode.BadRequest;
                return;
            }

            //Finding the right method of propfind, it is found in the first child of the tree.
            //This methods take the response tree and they completed it with the necessary values and structure.
            var propType = xmlTree.Children[0];
            switch (propType.NodeName)
            {
            case "prop":
                var props = ExtractPropertiesNameMainNS((XmlTreeStructure)xmlTree);

                //take the principalId from the properties
                var principalId = propertiesAndHeaders["principalId"];
                var principal   = _principalRepository.GetByIdentifier(principalId);
                await PropFindMethods.PropMethod(url, calendarResourceId, depth, props, responseTree, principal);

                break;

            case "allprop":
                var additionalProperties = ExtractIncludePropertiesNameMainNS((XmlTreeStructure)xmlTree);
                await PropFindMethods.AllPropMethod(url, calendarResourceId,
                                                    depth, additionalProperties, responseTree);

                break;

            case "propname":
                await PropFindMethods.PropNameMethod(url, calendarResourceId, depth, responseTree);

                break;

            default:
                response.StatusCode = (int)HttpStatusCode.BadRequest;
                return;
            }
            var responseText  = responseTree.ToString();
            var responseBytes = Encoding.UTF8.GetBytes(responseText);
            response.ContentLength = responseBytes.Length;
            await response.WriteAsync(responseText);
        }