/// <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()); }
/// <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); }
public async Task AllPropMethod(string url, string calendarResourceId, int?depth, List <KeyValuePair <string, string> > aditionalProperties, XmlTreeStructure multistatusTree) { //error flag //Here it is created the response body for the collection or resource //It depends if calendarResourceId == null. var primaryResponse = await AllPropFillTree(url, calendarResourceId, aditionalProperties); //The response body is added to the result xml tree. multistatusTree.AddChild(primaryResponse); //check if there was any error IXMLTreeStructure errorNode; var errorOcurred = primaryResponse.GetChildAtAnyLevel("responsedescription", out errorNode); //Now I start putting all objectResource responses if the primary target was a collection //and if depth is greater than depth 0. #region Adding the responses for resources. if (calendarResourceId == null && depth == 1 || depth == -1) { var collection = _collectionRepository.Get(url); foreach (var calendarResource in collection.CalendarResources) { //For every resource in the collection it is added a new xml "response" var resourceResponse = await AllPropFillTree(url + calendarResource.Name, calendarResource.Name, aditionalProperties); multistatusTree.AddChild(resourceResponse); //error check if (!errorOcurred) { errorOcurred = resourceResponse.GetChildAtAnyLevel("responsedescription", out errorNode); } } } if (errorOcurred) { //if an error occured it is added a new "responsedescription" with a message of the error //to the root of the tree. That is the "multistatus" xml. errorNode = new XmlTreeStructure("responsedescription", "DAV:"); errorNode.AddValue("There has been an error"); multistatusTree.AddChild(errorNode); } #endregion }
/// <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()); } }
/// <summary> /// Returns a Response XML tree for a prop request with all the property names /// and property values specified in the request. /// </summary> /// <param name="url"></param> /// <param name="calendarResourceId">Name of the resource</param> /// <param name="propertiesNameNamespace">List of requested properties (key=name; value=namespace)</param> /// <param name="principal"></param> /// <returns></returns> private async Task <XmlTreeStructure> PropFillTree(string url, string calendarResourceId, List <KeyValuePair <string, string> > propertiesNameNamespace, Principal principal) { //a "response xml element is added for each collection or resource" #region Adding the response of the collection or resource. var treeChild = new XmlTreeStructure("response", "DAV:"); #region Adding the <D:href>/api/v1/caldav/{userEmail}/calendars/{collectionName}/{calendarResourceId}?</D:href> //an href with the corresponding url is added to the response var href = new XmlTreeStructure("href", "DAV:"); href.AddValue(SystemProperties._baseUrl + url); treeChild.AddChild(href); #endregion #region Adding the propstats #region Selecting properties CalendarCollection collection; CalendarResource resource; var propertiesCol = new List <XmlTreeStructure>(); var propertiesOk = new List <XmlTreeStructure>(); var propertiesWrong = new List <XmlTreeStructure>(); var errorStack = new Stack <string>(); //the current-user-privilege-set is generated per request //it needs the DAV:acl property and the principalID Property aclProperty = null; //It take the list of requested properties and tries to get the corresponding property from db. //The methods are called for a resource or a collection accordingly its circumstances. //The properties are stored inside the propertiesCol. Where if the value is null it means that the collection could not be //retrieve. if (calendarResourceId == null) { collection = _collectionRepository.Get(url); if (propertiesNameNamespace != null) { foreach (var addProperty in propertiesNameNamespace) { //gets the property from database var property = await _collectionRepository.GetProperty(url, addProperty); //Builds the xmlTreeExtructure checking that if the value is null thats because //the property was not found. IXMLTreeStructure prop; if (property != null) { prop = property.Value == null ? new XmlTreeStructure(property.Name, property.Namespace) { Value = "" } } : XmlTreeStructure.Parse(property.Value); else { prop = new XmlTreeStructure(addProperty.Key, addProperty.Value); } propertiesCol.Add((XmlTreeStructure)prop); } //take the acl property aclProperty = collection.Properties.FirstOrDefault(x => x.Name == "acl"); } } else { resource = _resourceRespository.Get(url); if (propertiesNameNamespace != null) { foreach (var addProperty in propertiesNameNamespace) { //gets the property from database var property = await _resourceRespository.GetProperty(url, addProperty); //Builds the xmlTreeExtructure checking that if the value is null thats because //the property was not found. IXMLTreeStructure prop; if (property != null) { prop = property.Value == null ? new XmlTreeStructure(property.Name, property.Namespace) { Value = "" } } : XmlTreeStructure.Parse(property.Value); else { prop = new XmlTreeStructure(addProperty.Key, addProperty.Value); } propertiesCol.Add((XmlTreeStructure)prop); } //take the acl property aclProperty = resource.Properties.FirstOrDefault(x => x.Name == "acl"); } } //add the additional properties that are generated per request if (propertiesNameNamespace != null) { foreach (var pair in propertiesNameNamespace) { switch (pair.Key) { case "current-user-privilege-set": propertiesCol.RemoveAll(x => x.NodeName == pair.Key); propertiesCol.Add(principal.GetCurrentUserPermissions(aclProperty)); break; } } } //Here, properties are divided between recovered and error recovering foreach (var propTree in propertiesCol) { if (propTree.Value != null) { propertiesOk.Add(propTree); } else { propertiesWrong.Add(propTree); } } #endregion //For each returned status a "propstat" is created, containing a "prop" with all properties that belong to that current status. // And a "status" containing the message of the corresponding status. //Right Now there are only two "propstat" taking place OK and Wrong an therefore only two "status" //200 OK and 400 Not Found. //More should be added when ACL is working entairly. //TODO: Add the status forbidden for authentication permissions problems. #region Adding nested propOK var propstatOk = new XmlTreeStructure("propstat", "DAV:"); var propOk = new XmlTreeStructure("prop", "DAV:"); //Here i add all properties to the prop. foreach (var property in propertiesOk) { propOk.AddChild(property); } propstatOk.AddChild(propOk); //This when i group the OK properties #region Adding nested status OK var statusOk = new XmlTreeStructure("status", "DAV:"); statusOk.AddValue("HTTP/1.1 200 OK"); propstatOk.AddChild(statusOk); #endregion #endregion //Here the same is made. The Wrong properties are grouped. #region Adding nested propWrong var propstatWrong = new XmlTreeStructure("propstat", "DAV:"); var propWrong = new XmlTreeStructure("prop", "DAV:"); //Here i add all properties to the prop. foreach (var property in propertiesWrong) { propWrong.AddChild(property); } propstatWrong.AddChild(propWrong); #region Adding nested status Not Found var statusWrong = new XmlTreeStructure("status", "DAV:"); statusWrong.AddValue("HTTP/1.1 400 Not Found"); propstatWrong.AddChild(statusWrong); #endregion #region Adding responseDescription when wrong var responseDescrpWrong = new XmlTreeStructure("responsedescription", "DAV:"); responseDescrpWrong.AddValue("The properties doesn't exist"); propstatWrong.AddChild(responseDescrpWrong); #endregion #endregion //If anyone of the property groups is empty it is not included in the response. if (propertiesOk.Count > 0) { treeChild.AddChild(propstatOk); } if (propertiesWrong.Count > 0) { treeChild.AddChild(propstatWrong); } #endregion return(treeChild); #endregion }
/// <summary> /// Returns a Response XML element with all the property names /// and property values of the visible properties. /// </summary> /// <param name="url"></param> /// <param name="calendarResourceId">Name of the resource</param> /// <param name="additionalProperties">List of additional requested properties (key=name; value=namespace)</param> /// <returns></returns> private async Task <XmlTreeStructure> AllPropFillTree(string url, string calendarResourceId, List <KeyValuePair <string, string> > additionalProperties) { #region Adding the response of the collection or resource. //Adding standard structure for a "response" element. var treeChild = new XmlTreeStructure("response", "DAV:"); #region Adding the <D:href>/api/v1/collections/users|groups/principalId/{collectionName}/{calendarResourceId}?</D:href> var href = new XmlTreeStructure("href", "DAV:"); href.AddValue(SystemProperties._baseUrl + url); treeChild.AddChild(href); #endregion #region Adding the propstat #region Selecting properties var propertiesCol = new List <XmlTreeStructure>(); var propertiesOk = new List <XmlTreeStructure>(); var propertiesWrong = new List <XmlTreeStructure>(); var errorStack = new Stack <string>(); //Here all visible properties are retrieve plus a collection of extra properties that can be //defined in the request body. if (calendarResourceId == null) { var properties = await _collectionRepository.GetAllProperties(url); foreach (var property in properties) { //TODO: Check that the property is accessible beyond its visibility. var tempTree = property.Value == null ? new XmlTreeStructure(property.Name, property.Namespace) { Value = "" } : XmlTreeStructure.Parse(property.Value); propertiesCol.Add((XmlTreeStructure)tempTree); } //looking for additional properties if (additionalProperties != null && additionalProperties.Count > 0) { foreach (var addProperty in additionalProperties) { //gets the property from database var property = await _collectionRepository.GetProperty(url, addProperty); //Builds the xmlTreeExtructure checking that if the value is null thats because //the property was not found. IXMLTreeStructure prop; if (property != null) { prop = property.Value == null ? new XmlTreeStructure(property.Name, property.Namespace) { Value = "" } } : XmlTreeStructure.Parse(property.Value); else { prop = new XmlTreeStructure(addProperty.Key, addProperty.Value); } propertiesCol.Add((XmlTreeStructure)prop); } } } else { var properties = await _resourceRespository.GetAllProperties(url); foreach (var property in properties) { //TODO: Check that the property is accessible beyond its visibility. var tempTree = property.Value == null ? new XmlTreeStructure(property.Name, property.Namespace) { Value = "" } : XmlTreeStructure.Parse(property.Value); propertiesCol.Add((XmlTreeStructure)tempTree); } //looking for additional properties if (additionalProperties != null && additionalProperties.Count > 0) { foreach (var addProperty in additionalProperties) { //gets the property from database var property = await _resourceRespository.GetProperty(url, addProperty); //Builds the xmlTreeExtructure checking that if the value is null thats because //the property was not found. IXMLTreeStructure prop; if (property != null) { prop = property.Value == null ? new XmlTreeStructure(property.Name, property.Namespace) { Value = "" } } : XmlTreeStructure.Parse(property.Value); else { prop = new XmlTreeStructure(addProperty.Key, addProperty.Value); } propertiesCol.Add((XmlTreeStructure)prop); } } } //Here there are divided all properties between recovered and error recovering foreach (var propTree in propertiesCol) { if (propTree.Value != null) { propertiesOk.Add(propTree); } else { propertiesWrong.Add(propTree); } } #endregion #region Adding nested propOK //This procedure has been explained in another method. //Here the retrieve properties are grouped. var propstatOk = new XmlTreeStructure("propstat", "DAV:"); var propOk = new XmlTreeStructure("prop", "DAV:"); //Here i add all properties to the prop. foreach (var property in propertiesOk) { propOk.AddChild(property); } propstatOk.AddChild(propOk); #region Adding nested status OK var statusOk = new XmlTreeStructure("status", "DAV:"); statusOk.AddValue("HTTP/1.1 200 OK"); propstatOk.AddChild(statusOk); #endregion #endregion #region Adding nested propWrong //Here the properties that could not be retrieved are grouped. var propstatWrong = new XmlTreeStructure("propstat", "DAV:"); var propWrong = new XmlTreeStructure("prop", "DAV:"); //Here i add all properties to the prop. foreach (var property in propertiesWrong) { propWrong.AddChild(property); } propstatWrong.AddChild(propWrong); #region Adding nested status Not Found var statusWrong = new XmlTreeStructure("status", "DAV:"); statusWrong.AddValue("HTTP/1.1 400 Not Found"); propstatWrong.AddChild(statusWrong); #endregion #region Adding responseDescription when wrong //Here i add an description for explain the errors. //This should be aplied in all method with an similar structure but for the moment is only used here. //However this is not required. var responseDescrpWrong = new XmlTreeStructure("responsedescription", "DAV:"); responseDescrpWrong.AddValue("The properties doesn't exist"); propstatWrong.AddChild(responseDescrpWrong); #endregion #endregion //If any of the "status" group is empty, it is not included. if (propertiesOk.Count > 0) { treeChild.AddChild(propstatOk); } if (propertiesWrong.Count > 0) { treeChild.AddChild(propstatWrong); } #endregion return(treeChild); #endregion }
/// <summary> /// Returns a Response XML element with the name of all properties /// of a collection or resource. /// </summary> /// <param name="url"></param> /// <param name="calendarResourceId"></param> /// <returns></returns> private async Task <XmlTreeStructure> PropNameFillTree(string url, string calendarResourceId = null) { #region Adding the response of the collection or resource. //A "response" structure with all its children is build in this method. var treeChild = new XmlTreeStructure("response", "DAV:"); #region Adding the <D:href>/api/v1/caldav/{userEmail}/calendars/{collectionName}/{calendarResourceId}?</D:href> var href = new XmlTreeStructure("href", "DAV:"); href.AddValue(SystemProperties._baseUrl + url); treeChild.AddChild(href); #endregion #region Adding the propstat //in this section is where the "propstat" structure its build. var propstat = new XmlTreeStructure("propstat", "DAV:"); #region Adding nested status //each "propstat" has a "status" with the message that define it. //"propname" is always "200 OK" because you are only accessing the name of the established properties. var status = new XmlTreeStructure("status", "DAV:"); status.AddValue("HTTP/1.1 200 OK"); propstat.AddChild(status); #endregion #region Adding nested prop var prop = new XmlTreeStructure("prop", "DAV:"); List <XmlTreeStructure> properties; //Depending if the target is a collection or a resource this section //will find the object in the database and get from there all names of properties. if (calendarResourceId == null) { //var collection = _collectionRepository.Get(url).Result; properties = (await _collectionRepository.GetAllPropname(url)).Select(p => new XmlTreeStructure(p.Key, p.Value)) .ToList(); //properties = collection.GetAllPropertyNames(); } else { properties = (await _resourceRespository.GetAllPropname(url)).Select(p => new XmlTreeStructure(p.Key, p.Value)) .ToList(); } //Here i add all properties to the prop. foreach (var property in properties) { prop.AddChild(property); } propstat.AddChild(prop); #endregion treeChild.AddChild(propstat); #endregion return(treeChild); #endregion }
/// <summary> /// Take the prop node that specified the properties and /// component that are requested, extract this data from /// the system and the VCalendar and return the container /// node with this data. /// </summary> /// <param name="incomPropNode"> /// This node contains the requested data. Is the first prop node /// of the calendar-query. /// </param> /// <param name="resource">The calendar where to extract the data.</param> /// <returns>Return the prop node that contains the requested data</returns> private async Task <List <IXMLTreeStructure> > ProccessPropNode(IXMLTreeStructure incomPropNode, KeyValuePair <string, VCalendar> resource) { var output = new List <IXMLTreeStructure>(); var resPropertiesOk = new List <XmlTreeStructure>(); var resPropertiesNotExist = new List <XmlTreeStructure>(); var href = resource.Key[0] != '/' ? "/" + resource.Key : resource.Key; var calResource = _resourceRepository.Get(href); foreach (var prop in incomPropNode.Children) { //create an instance of a XMlTreeStrucure with the same name and //ns that the requested var currentPropNode = new XmlTreeStructure(prop.NodeName, prop.MainNamespace); switch (prop.NodeName) { //if the requested prop is calendar data then take the content of the //resource case "calendar-data": //see if the calendar-data describes pros to take // if does then take them if not take it all currentPropNode.AddValue(prop.Children.Any() ? resource.Value.ToString(prop) : resource.Value.ToString()); resPropertiesOk.Add(currentPropNode); break; //if not try to take the property from the resource's properties default: var currentProperty = calResource.Properties.FirstOrDefault(p => p.Name == prop.NodeName); currentPropNode.AddValue(currentProperty != null ? currentProperty.PropertyRealValue() : ""); if (currentProperty != null) { resPropertiesOk.Add(currentPropNode); } else { resPropertiesNotExist.Add(currentPropNode); } break; } } #region Adding nested propOK //This procedure has been explained in another method. //Here the retrieve properties are grouped. var propstatOK = new XmlTreeStructure("propstat", "DAV:"); var propOk = new XmlTreeStructure("prop", "DAV:"); //Here i add all properties to the prop. foreach (var property in resPropertiesOk) { propOk.AddChild(property); } propstatOK.AddChild(propOk); #endregion #region Adding nested status OK var statusOK = new XmlTreeStructure("status", "DAV:"); statusOK.AddValue("HTTP/1.1 200 OK"); propstatOK.AddChild(statusOK); #endregion #region Adding nested propWrong //Here the properties that could not be retrieved are grouped. var propstatWrong = new XmlTreeStructure("propstat", "DAV:"); var propWrong = new XmlTreeStructure("prop", "DAV:"); //Here i add all properties to the prop. foreach (var property in resPropertiesNotExist) { propWrong.AddChild(property); } propstatWrong.AddChild(propWrong); #endregion #region Adding nested status Not Found var statusWrong = new XmlTreeStructure("status", "DAV:"); statusWrong.AddValue("HTTP/1.1 400 Not Found"); propstatWrong.AddChild(statusWrong); #endregion #region Adding responseDescription when wrong //Here i add an description for explain the errors. //This should be aplied in all method with an similar structure but for the moment is only used here. //However this is not required. var responseDescrpWrong = new XmlTreeStructure("responsedescription", "DAV:"); responseDescrpWrong.AddValue("The properties doesn't exist"); propstatWrong.AddChild(responseDescrpWrong); #endregion if (resPropertiesOk.Any()) { output.Add(propstatOK); } if (resPropertiesNotExist.Any()) { output.Add(propstatWrong); } return(output); }
/// <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); }
//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()); }
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"); }