示例#1
0
        public async Task ReadCalendarObjectResource(Dictionary <string, string> propertiesAndHeaders,
                                                     HttpResponse response)
        {
            #region Extracting Properties

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

            #endregion

            //An easy way of accessing the headers of the http response
            response.GetTypedHeaders();

            //StorageManagement.SetUserAndCollection(principalUrl, collectionName);
            //Must return the Etag header of the COR

            var calendarRes = _resourceRespository.Get(url);

            if (calendarRes == null || !StorageManagement.ExistCalendarObjectResource(url))
            {
                response.StatusCode = (int)HttpStatusCode.NotFound;
                return;
            }

            var resourceBody = await StorageManagement.GetCalendarObjectResource(url);

            var etagProperty = calendarRes.Properties.FirstOrDefault(x => x.Name == "getetag");
            if (etagProperty != null)
            {
                var etag = XmlTreeStructure.Parse(etagProperty.Value).Value;
                response.Headers["etag"] = etag;
            }

            await response.WriteAsync(resourceBody);
        }
示例#2
0
        public void UnitTest3()
        {
            var calStr   = @"BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VTODO
DTSTAMP:20060205T235300Z
DUE;TZID=US/Eastern:20060106T120000
LAST-MODIFIED:20060205T235308Z
SEQUENCE:1
STATUS:NEEDS-ACTION
SUMMARY:Task #2
UID:[email protected]
BEGIN:VALARM
ACTION:AUDIO
TRIGGER;RELATED=START:-PT10M
END:VALARM
END:VTODO
END:VCALENDAR";
            var xmlStr   = @"<C:filter  xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<C:comp-filter name=""VCALENDAR"" xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<C:comp-filter name=""VTODO"">
<C:comp-filter name=""VALARM"">
<C:time-range start=""20060106T100000Z""
end=""20060107T100000Z""/>
</C:comp-filter>
</C:comp-filter>
</C:comp-filter></C:filter>";
            var calendar = new VCalendar(calStr);
            var xmlTree  = XmlTreeStructure.Parse(xmlStr);
            var result   = calendar.FilterResource(xmlTree);

            Assert.True(result);
        }
示例#3
0
        public void RetrievalofAllPendingToDos()
        {
            var xmlStr   = @"<C:filter  xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<C:comp-filter name=""VCALENDAR"" xmlns:C=""urn:ietf:params:xml:ns:caldav"">
	<C:comp-filter name=""VTODO"">
<C:prop-filter name=""COMPLETED"">
<C:is-not-defined/>
</C:prop-filter>
<C:prop-filter name=""STATUS"">
<C:text-match
negate-condition=""yes"">CANCELLED</C:text-match>
</C:prop-filter>
</C:comp-filter>
</C:comp-filter></C:filter>";
            var calStr   = @"BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VTODO
DTSTAMP:20060205T235335Z
DUE;VALUE=DATE:20060104
STATUS:NEEDS-ACTION
SUMMARY:Task #1
UID:[email protected]
BEGIN:VALARM
ACTION:AUDIO
TRIGGER;RELATED=START:-PT10M
END:VALARM
END:VTODO
END:VCALENDAR";
            var calendar = new VCalendar(calStr);
            var xmlTree  = XmlTreeStructure.Parse(xmlStr);
            var result   = calendar.FilterResource(xmlTree);

            Assert.True(result);
        }
示例#4
0
        public void UnitTest2()
        {
            var xmlStr = @"<C:filter xmlns:C=""urn:ietf:params:xml:ns:caldav"">
                   <C:comp-filter name=""VCALENDAR"">
                <C:comp-filter name=""VFREEBUSY"">
                <C:time-range start=""20060102T000000Z""
                end=""20060103T000000Z""/>
                </C:comp-filter>
                </C:comp-filter>
                </C:filter>";
            var calStr = @"BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VFREEBUSY
ORGANIZER;CN=""Bernard Desruisseaux"":mailto:[email protected]
UID:[email protected]
DTSTAMP:20050530T123421Z
DTSTART:20060101T100000Z
DTEND:20060108T100000Z
FREEBUSY;FBTYPE=BUSY-TENTATIVE:20060102T100000Z/20060102T120000Z
END:VFREEBUSY
END:VCALENDAR";

            var calendar = VCalendar.Parse(calStr);
            var xmlTree  = XmlTreeStructure.Parse(xmlStr);
            var result   = calendar.FilterResource(xmlTree);

            Assert.True(result);
        }
示例#5
0
        /// <summary>
        ///     Process the report depending on the values of the header
        ///     and the body.
        /// </summary>
        /// <returns></returns>
        public async Task ProcessRequest(HttpContext httpContext)
        {
            var body = new StreamReader(httpContext.Request.Body).ReadToEnd();

            // var node = xmlBody.Children.First();
            var xmlBody = XmlTreeStructure.Parse(body);

            //take the target url that is the identifier of the collection
            var urlId = httpContext.Request.GetRealUrl();

            //take the first node of the xml and process the request
            //by the name of the first node
            switch (xmlBody.NodeName)
            {
            case "calendar-query":
                await CalendarQuery(xmlBody, urlId, httpContext);

                break;

            case "calendar-multiget":
                await CalendarMultiget(xmlBody, httpContext);

                break;

            default:
                throw new NotImplementedException(
                          $"The REPORT request {xmlBody.NodeName} with ns equal to {xmlBody.MainNamespace} is not implemented yet .");
            }
        }
示例#6
0
        public void RecursiveSeekerTest3()
        {
            var calStr   = @"BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VTIMEZONE
LAST-MODIFIED:20040110T032845Z
TZID:US/Eastern
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:[email protected]
DTSTAMP:20060206T001220Z
DTSTART;TZID=US/Eastern:20060104T100000
DURATION:PT1H
LAST-MODIFIED:20060206T001330Z
ORGANIZER:mailto:[email protected]
SEQUENCE:1
STATUS:TENTATIVE
SUMMARY:Event #3
UID:[email protected]
X-ABC-GUID:[email protected]
END:VEVENT
END:VCALENDAR";
            var xmlStr   = @"<C:comp-filter name=""VCALENDAR"" xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<C:comp-filter name=""VTODO"">
<C:prop-filter name=""UID"">
<C:text-match collation=""i;octet""
>[email protected]</C:text-match>
</C:prop-filter>
</C:comp-filter>
</C:comp-filter>";
            var calendar = new VCalendar(calStr);
            var xmlTree  = XmlTreeStructure.Parse(xmlStr);
            IXMLTreeStructure  tree;
            ICalendarComponent comp;
            var result = calendar.ComponentSeeker(xmlTree, out tree, out comp);

            Assert.False(result);

            /*Assert.Equal("VEVENT", comp.Name);
             *          Assert.Equal("VEVENT", tree.Attributes["name"]);*/
        }
示例#7
0
        public void FilterResourceTest1()
        {
            var calStr   = @"BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VTIMEZONE
LAST-MODIFIED:20040110T032845Z
TZID:US/Eastern
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:[email protected]
DTSTAMP:20060206T001220Z
DTSTART;TZID=US/Eastern:20060104T100000
DURATION:PT1H
LAST-MODIFIED:20060206T001330Z
ORGANIZER:mailto:[email protected]
SEQUENCE:1
STATUS:TENTATIVE
SUMMARY:Event #3
UID:[email protected]
X-ABC-GUID:[email protected]
END:VEVENT
END:VCALENDAR";
            var xmlStr   = @"<C:filter  xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<C:comp-filter name=""VCALENDAR"" xmlns:C=""urn:ietf:params:xml:ns:caldav"">
	<C:comp-filter name=""VEVENT"">
		<C:prop-filter name=""ATTENDEE"">
			<C:text-match collation=""i;ascii-casemap"">mailto:[email protected]</C:text-match>
			<C:param-filter name=""PARTSTAT"">
				 <C:text-match collation=""i;ascii-casemap"">NEEDS-ACTION</C:text-match>
			</C:param-filter>
		</C:prop-filter>
	</C:comp-filter>
</C:comp-filter></C:filter>";
            var calendar = new VCalendar(calStr);
            var xmlTree  = XmlTreeStructure.Parse(xmlStr);
            var result   = calendar.FilterResource(xmlTree);

            Assert.True(result);
        }
示例#8
0
        public async Task ProcessRequest(HttpRequest request, HttpResponse response)
        {
            //check the depth of the header
            // This report is only defined when the Depth header has value "0";
            // other values result in a 400 (Bad Request) error response.
            if (request.Headers.ContainsKey("Depth"))
            {
                var depth = request.Headers["Depth"];
                if (depth != "\"0\"")
                {
                    response.StatusCode = 400;
                    return;
                }
            }

            string href = request.Path;
            //TODO: take here the email of the user by calling
            //to the authentication api
            var userEmail = "";

            response = null;
            //take the string representation of the body
            var bodyStr = request.Body.ToString();
            var xmlbody = XmlTreeStructure.Parse(bodyStr);

            switch (xmlbody.NodeName)
            {
            case "acl-principal-prop-set":
                await AclPrincipalPropSet(xmlbody, response);

                break;

            case "principal-match":
                await PrincipalMatch(xmlbody, userEmail, href, response);

                break;

            case "principal-property-search":
                await PrincipalPropertySearch(xmlbody, request, response);

                break;

            case "principal-search-property-set":
                await PrincipalSearchPropertySet(response);

                break;
            }
        }
示例#9
0
        public void UnitTest4()
        {
            var doc  = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
<C:calendar-query xmlns:D=""DAV:""
xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<D:prop>
<D:getetag/>
<C:calendar-data>
<C:comp name=""VCALENDAR"">
<C:prop name=""VERSION""/>
<C:comp name=""VEVENT"">
<C:prop name=""SUMMARY""/>
<C:prop name=""UID""/>
<C:prop name=""DTSTART""/>
<C:prop name=""DTEND""/>
<C:prop name=""DURATION""/>
<C:prop name=""RRULE""/>
<C:prop name=""RDATE""/>
<C:prop name=""EXRULE""/>
<C:prop name=""EXDATE""/>
<C:prop name=""RECURRENCE-ID""/>
</C:comp>
<C:comp name=""VTIMEZONE""/>
</C:comp>
</C:calendar-data>
</D:prop>
<C:filter>
<C:comp-filter name=""VCALENDAR"">
<C:comp-filter name=""VEVENT"">
<C:time-range start=""20060104T000000Z""
end=""20060105T000000Z""/>
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>";
            var xDoc = XDocument.Parse(doc);

            xDoc.ToString();
            var temp1 = xDoc.Root.Attributes().Where(x => x.IsNamespaceDeclaration);
            var item  = xDoc.CreateWriter();

            var result = XmlTreeStructure.Parse(doc);
            IXMLTreeStructure filter;

            Assert.True(result.GetChildAtAnyLevel("filter", out filter));
            Assert.Equal(filter.GetChild("comp-filter").Attributes["name"], "VCALENDAR");
        }
示例#10
0
        public void IXmlTreeStrucureToString()
        {
            var doc = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
<C:calendar-query xmlns:D=""DAV:""
xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<D:prop>
<D:getetag/>
<C:calendar-data>
<C:comp name=""VCALENDAR"">
<C:prop name=""VERSION""/>
<C:comp name=""VEVENT"">
<C:prop name=""SUMMARY""/>
<C:prop name=""UID""/>
<C:prop name=""DTSTART""/>
<C:prop name=""DTEND""/>
<C:prop name=""DURATION""/>
<C:prop name=""RRULE""/>
<C:prop name=""RDATE""/>
<C:prop name=""EXRULE""/>
<C:prop name=""EXDATE""/>
<C:prop name=""RECURRENCE-ID""/>
</C:comp>
<C:comp name=""VTIMEZONE""/>
</C:comp>
</C:calendar-data>
</D:prop>
<C:filter>
<C:comp-filter name=""VCALENDAR"">
<C:comp-filter name=""VEVENT"">
<C:time-range start=""20060104T000000Z""
end=""20060105T000000Z""/>
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>";
            var xmlTreeStructure  = XmlTreeStructure.Parse(doc);
            var xmlTreeStructure2 = XmlTreeStructure.Parse(xmlTreeStructure.ToString());
            var xmlStr1           = xmlTreeStructure.ToString();
            var xmlStr2           = xmlTreeStructure2.ToString();

            Assert.Equal(xmlStr1, xmlStr2);
        }
示例#11
0
        public void PartialRetrievalofEventsbyTimeRange()
        {
            var xmlStr   = @"<C:filter  xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<C:comp-filter name=""VCALENDAR"" xmlns:C=""urn:ietf:params:xml:ns:caldav"">
	<C:comp-filter name=""VEVENT"">
		<C:time-range start=""20060104T000000Z"" end=""20060105T000000Z""/>
	</C:comp-filter>
</C:comp-filter></C:filter>";
            var calStr   = @"BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VTIMEZONE
LAST-MODIFIED:20040110T032845Z
TZID:US/Eastern
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=US/Eastern:20060102T120000
DURATION:PT1H
RRULE:FREQ=DAILY;COUNT=5
SUMMARY:Event #2
UID:[email protected]
END:VEVENT
END:VCALENDAR";
            var calendar = new VCalendar(calStr);
            var xmlTree  = XmlTreeStructure.Parse(xmlStr);
            var result   = calendar.FilterResource(xmlTree);

            Assert.True(result);
        }
示例#12
0
        public void UnitTest3()
        {
            var doc    = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
<C:calendar-query xmlns:D=""DAV:""
xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<D:prop>
<D:getetag/>
<C:calendar-data>
<C:comp name=""VCALENDAR"">
<C:prop name=""VERSION""/>
<C:comp name=""VEVENT"">
<C:prop name=""SUMMARY""/>
<C:prop name=""UID""/>
<C:prop name=""DTSTART""/>
<C:prop name=""DTEND""/>
<C:prop name=""DURATION""/>
<C:prop name=""RRULE""/>
<C:prop name=""RDATE""/>
<C:prop name=""EXRULE""/>
<C:prop name=""EXDATE""/>
<C:prop name=""RECURRENCE-ID""/>
</C:comp>
<C:comp name=""VTIMEZONE""/>
</C:comp>
</C:calendar-data>
</D:prop>
<C:filter>
<C:comp-filter name=""VCALENDAR"">
<C:comp-filter name=""VEVENT"">
<C:time-range start=""20060104T000000Z""
end=""20060105T000000Z""/>
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>";
            var result = XmlTreeStructure.Parse(doc);
            IXMLTreeStructure filter;

            Assert.True(result.GetChildAtAnyLevel("filter", out filter));
            Assert.NotNull(filter.GetChild("comp-filter"));
        }
        public async Task <bool> PreconditionsOK(Dictionary <string, string> propertiesAndHeaders, HttpResponse response)
        {
            #region Extracting Properties

            var body = propertiesAndHeaders["body"];
            var url  = propertiesAndHeaders["url"];

            #endregion

            if (fs.ExistCalendarCollection(url) || await _collectionRepository.Exist(url))
            {
                response.StatusCode = (int)HttpStatusCode.Forbidden;
                response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
  <resource-must-be-null/>  
</error>");

                return(false);
            }

            if (!string.IsNullOrEmpty(body))
            {
                var bodyTree = XmlTreeStructure.Parse(body);
                if (bodyTree == null)
                {
                    response.StatusCode = (int)HttpStatusCode.Forbidden;
                    response.Body.Write("Wrong Body");
                    return(false);
                }
                if (bodyTree.NodeName != "mkcalendar")
                {
                    response.StatusCode = (int)HttpStatusCode.Forbidden;
                    response.Body.Write("Wrong Body");

                    return(false);
                }
            }

            return(true);
        }
示例#14
0
        /// <summary>
        ///     Call the method to perform a PROFIND over a
        ///     principal.
        ///     Initially the client could do a PROFIND over
        ///     the server to discover all the user calendars
        ///     or could PORFIND directly over a calendar URL.
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns>The request</returns>
        public async Task Profind(HttpContext httpContext)
        {
            var requestPath  = httpContext.Request.Path;
            var streamReader = new StreamReader(httpContext.Request.Body);
            //read the body of the request
            var bodyString = streamReader.ReadToEnd();

            //try to authenticate the request either with the cookies or the user credentials
            var principal = _authenticate.AuthenticateRequest(httpContext);

            //if the principal is null then there is some problem with the authentication
            //so return
            //if (principal == null)
            //    return;

            var body = XmlTreeStructure.Parse(bodyString);

            //take the requested properties
            var reqProperties = ExtractPropertiesNameMainNS(body);

            await BuildResponse(httpContext.Response, requestPath, reqProperties, principal);
        }
示例#15
0
        /// <summary>
        ///     Get the permission for the given principal
        ///     in some resource.
        /// </summary>
        /// <param name="principal">The principal that wants to know his permissions.</param>
        /// <param name="property">The resource or collection's DAV:acl property</param>
        /// <returns>Return an I</returns>
        public static XmlTreeStructure GetCurrentUserPermissions(this Principal principal, Property property)
        {
            var pUrl = principal.PrincipalURL;
            var aclP = XDocument.Parse(property.Value).Root;
            IEnumerable <XElement> principalGrantPermissions = null;
            XName aceName = "ace";

            //take the permission for the principal if any
            var descendants  = aclP?.Descendants();
            var aces         = descendants.Where(x => x.Name.LocalName == aceName);
            var principalAce = aces.FirstOrDefault(ace => ace.Descendants()
                                                   .FirstOrDefault(x => x.Name.LocalName == "href")?.Value == pUrl);

            if (principalAce != null)
            {
                principalGrantPermissions = principalAce.Descendants()
                                            .FirstOrDefault(x => x.Name.LocalName == "grant")?.Elements();
            }

            //take the permission for all users if any

            var output = new XElement("current-user-privilege-set", new XAttribute(XNamespace.Xmlns + "D", "DAV:"));


            //add the permission to the response
            if (principalGrantPermissions != null)
            {
                foreach (var permission in principalGrantPermissions)
                {
                    output.Add(permission);
                }
            }
            var outputStr = output.ToString();
            var xmlTree   = XmlTreeStructure.Parse(outputStr) as XmlTreeStructure;

            return(xmlTree);
        }
示例#16
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());
            }
        }
示例#17
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");
        }
示例#18
0
        public void UnitTest1()
        {
            var xmlStr   = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
<C:calendar-query xmlns:D=""DAV:""
xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<D:prop>
<D:getetag/>
<C:calendar-data>
<C:comp name=""VCALENDAR"">
<C:prop name=""VERSION""/>
<C:comp name=""VEVENT"">
<C:prop name=""SUMMARY""/>
<C:prop name=""UID""/>
<C:prop name=""DTSTART""/>
<C:prop name=""DTEND""/>
<C:prop name=""DURATION""/>
<C:prop name=""RRULE""/>
<C:prop name=""RDATE""/>
<C:prop name=""EXRULE""/>
<C:prop name=""EXDATE""/>
<C:prop name=""RECURRENCE-ID""/>
</C:comp>
<C:comp name=""VTIMEZONE""/>
</C:comp>
</C:calendar-data>
</D:prop>
<C:filter>
<C:comp-filter name=""VCALENDAR"">
<C:comp-filter name=""VEVENT"">
<C:time-range start=""20060104T000000Z""
end=""20060105T000000Z""/>
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>";
            var calStr   = @"BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VTIMEZONE
LAST-MODIFIED:20040110T032845Z
TZID:US/Eastern
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=US/Eastern:20060102T120000
DURATION:PT1H
RRULE:FREQ=DAILY;COUNT=5
SUMMARY:Event #2
UID:[email protected]
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=US/Eastern:20060104T140000
DURATION:PT1H
RECURRENCE-ID;TZID=US/Eastern:20060104T120000
SUMMARY:Event #2 bis
UID:[email protected]
END:VEVENT
BEGIN:VEVENT
DTSTART;TZID=US/Eastern:20060106T140000
DURATION:PT1H
RECURRENCE-ID;TZID=US/Eastern:20060106T120000
SUMMARY:Event #2 bis bis
UID:[email protected]
END:VEVENT
END:VCALENDAR";
            var calendar = new VCalendar(calStr);
            var xmlTree  = XmlTreeStructure.Parse(xmlStr);
            var result   = calendar.FilterResource(xmlTree);

            Assert.True(result);
        }
示例#19
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());
        }
示例#20
0
        public async Task <bool> DeleteCalendarObjectResource(Dictionary <string, string> propertiesAndHeaders,
                                                              HttpResponse response)
        {
            #region Extracting Properties

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

            string ifmatch;
            var    ifMatchEtags = new List <string>();
            propertiesAndHeaders.TryGetValue("If-Match", out ifmatch);
            if (ifmatch != null)
            {
                ifMatchEtags = ifmatch.Split(',').ToList();
            }

            #endregion

            //if the collection doesnt exist in the user folder
            // the can't do anything

            var collectionUrl = url?.Remove(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
            if (!StorageManagement.ExistCalendarCollection(collectionUrl) &&
                !await _collectionRespository.Exist(collectionUrl))
            {
                return(true);
            }

            var resource =
                _resourceRespository.Get(url);

            if (ifMatchEtags.Count > 0)
            {
                if (resource != null)
                {
                    var resourceEtag =
                        XmlTreeStructure.Parse(resource.Properties.FirstOrDefault(x => x.Name == "getetag")?.Value)
                        .Value;
                    if (resourceEtag != null && ifMatchEtags.Contains(resourceEtag))
                    {
                        response.StatusCode = (int)HttpStatusCode.NoContent;
                        await _resourceRespository.Remove(resource);


                        //updating the ctag
                        var stack = new Stack <string>();
                        await
                        _collectionRespository.CreateOrModifyProperty(collectionUrl, "getctag",
                                                                      _namespacesSimple["S"],
                                                                      $@"<S:getctag {_namespaces["S"]} >{Guid.NewGuid()}</S:getctag>", stack, true);


                        return(StorageManagement.DeleteCalendarObjectResource(url));
                    }
                }
            }


            if (resource != null)
            {
                response.StatusCode = (int)HttpStatusCode.NoContent;
                await _resourceRespository.Remove(resource);

                //updating the ctag
                var stack = new Stack <string>();
                await _collectionRespository.CreateOrModifyProperty(collectionUrl, "getctag", _namespacesSimple["S"],
                                                                    $@"<S:getctag {_namespaces["S"]} >{Guid.NewGuid()}</S:getctag>", stack, true);


                return(StorageManagement.DeleteCalendarObjectResource(url));
            }

            return(StorageManagement.DeleteCalendarObjectResource(url));
        }
示例#21
0
        //TODO: Poner esto en la capa de datos
        public async Task AddCalendarObjectResource(Dictionary <string, string> propertiesAndHeaders,
                                                    HttpResponse response)
        {
            #region Extracting Properties

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

            string ifmatch;
            var    ifMatchEtags = new List <string>();
            propertiesAndHeaders.TryGetValue("If-Match", out ifmatch);
            if (ifmatch != null)
            {
                ifMatchEtags = ifmatch.Split(',').ToList();
            }


            string ifnonematch;
            var    ifNoneMatchEtags = new List <string>();
            propertiesAndHeaders.TryGetValue("If-None-Match", out ifnonematch);
            if (ifnonematch != null)
            {
                ifNoneMatchEtags = ifnonematch.Split(',').ToList();
            }
            string body;
            propertiesAndHeaders.TryGetValue("body", out body);

            #endregion

            //Note: calendar object resource = COR

            //CheckAllPreconditions

            PreconditionCheck = new PutPrecondition(StorageManagement, _collectionRespository, _resourceRespository);
            if (!await PreconditionCheck.PreconditionsOK(propertiesAndHeaders, response))
            {
                return;
            }

            var resourceExist = await _resourceRespository.Exist(url);

            //If the ifmatch is included i look for the etag in the resource, but first the resource has to exist.
            //If all is ok and the if-match etag matches the etag in the resource then i update the resource.
            //If the if-match dont match then i set that the precondition failed.
            if (ifMatchEtags.Count > 0)
            {
                if (resourceExist)
                {
                    var resource     = _resourceRespository.Get(url);
                    var resourceEtag =
                        XmlTreeStructure.Parse(resource.Properties.FirstOrDefault(x => x.Name == "getetag")?.Value)
                        .Value;
                    if (ifMatchEtags.Contains(resourceEtag))
                    {
                        await UpdateCalendarObjectResource(propertiesAndHeaders, response);

                        return;
                    }
                    response.StatusCode = (int)HttpStatusCode.PreconditionFailed;
                    return;
                }
            }

            if (ifNoneMatchEtags.Count > 0 && ifNoneMatchEtags.Contains("*"))
            {
                if (!resourceExist)
                {
                    await CreateCalendarObjectResource(propertiesAndHeaders, response);

                    return;
                }
                response.StatusCode = (int)HttpStatusCode.PreconditionFailed;
                return;
            }

            if (resourceExist && StorageManagement.ExistCalendarObjectResource(url))
            {
                await UpdateCalendarObjectResource(propertiesAndHeaders, response);

                return;
            }
            await CreateCalendarObjectResource(propertiesAndHeaders, response);

            //return HTTP 201 Created
        }
示例#22
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);
        }
示例#23
0
        /// <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
        }
示例#24
0
        /// <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
        }
示例#25
0
        public async Task <bool> PreconditionsOK(Dictionary <string, string> propertiesAndHeaders, HttpResponse response)
        {
            #region Extracting Properties

            var url = propertiesAndHeaders["url"];

            var       contentSize = propertiesAndHeaders["content-length"];
            var       body        = propertiesAndHeaders["body"];
            VCalendar iCalendar;
            try
            {
                iCalendar = new VCalendar(body); //lo que no estoy seguro que en el body solo haya el iCal string
            }
            catch (Exception)
            {
                response.StatusCode = (int)HttpStatusCode.BadRequest;
                return(false);
            }

            #endregion

            //check that resourceId don't exist but the collection does.
            if (
                !StorageManagement.ExistCalendarCollection(url.Remove(url.LastIndexOf("/", StringComparison.Ordinal) + 1)))
            {
                response.StatusCode = (int)HttpStatusCode.NotFound;
                return(false);
            }


            //check that if the resource exist then all its components different of VTIMEZONE has to have the same UID
            //if the resource not exist can not be another resource with the same uid.
            if (!StorageManagement.ExistCalendarObjectResource(url))
            {
                var component = iCalendar.CalendarComponents.FirstOrDefault(comp => comp.Key != "VTIMEZONE").Value;
                var uid       = component.FirstOrDefault()?.Properties["UID"].StringValue;
                // var resource = db.GetCalendarResource(userEmail, collectionName, calendarResourceId);
                var collection =
                    _collectionRepository.Get(url.Remove(url.LastIndexOf("/", StringComparison.Ordinal) + 1));
                foreach (var calendarresource in collection.CalendarResources)
                {
                    if (uid == calendarresource.Uid)
                    {
                        response.StatusCode = (int)HttpStatusCode.Conflict;
                        response.Body.Write(
                            $@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<no-uid-conflict xmlns='urn:ietf:params:xml:ns:caldav'>
<href xmlns='DAV:'>{
                                SystemProperties._baseUrl + calendarresource
                                    .Href}</href>
</no-uid-conflict>
</error>");
                        return(false);
                    }
                }
            }
            else
            {
                //If the resource exist the procedure is update and for that the uid has to be the same.
                var components        = iCalendar.CalendarComponents.FirstOrDefault(comp => comp.Key != "VTIMEZONE").Value;
                var calendarComponent = components.FirstOrDefault();
                if (calendarComponent != null)
                {
                    var uid = calendarComponent.Properties["UID"].StringValue;

                    var resource = _resourceRespository.Get(url);

                    if (resource.Uid != null && resource.Uid != uid)
                    {
                        response.StatusCode = (int)HttpStatusCode.Conflict;
                        response.Body.Write(
                            $@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<no-uid-conflict xmlns='urn:ietf:params:xml:ns:caldav'>
<href xmlns='DAV:'>{
                                SystemProperties._baseUrl + resource
                                    .Href}</href>
</no-uid-conflict>
</error>");
                        return(false);
                    }
                }
            }


            if (propertiesAndHeaders.ContainsKey("If-Match"))
            {
                //check that the value do exist
                if (!StorageManagement.ExistCalendarObjectResource(url))
                {
                    response.StatusCode = (int)HttpStatusCode.PreconditionFailed;
                    return(false);
                }
            }

            if (propertiesAndHeaders.ContainsKey("If-None-Match"))
            {
                //check that the value do not exist
                if (StorageManagement.ExistCalendarObjectResource(url))
                {
                    response.StatusCode = (int)HttpStatusCode.PreconditionFailed;
                    return(false);
                }
            }


            //it does not contain more than two calendar components
            //and if it has 2, one must be VTIMEZONE
            if (iCalendar.CalendarComponents.Count > 2)
            {
                if (!iCalendar.CalendarComponents.ContainsKey("VTIMEZONE"))
                {
                    response.StatusCode = (int)HttpStatusCode.Conflict;
                    response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<valid-calendar-object-resource xmlns='urn:ietf:params:xml:ns:caldav'></valid-calendar-object-resource>
<error-description xmlns='http://twistedmatrix.com/xml_namespace/dav/'>
VTimezone Calendar Component Must be present.
</error-description>
</error>");
                    return(false);
                }

                var calendarComponents =
                    iCalendar.CalendarComponents.FirstOrDefault(comp => comp.Key != "VTIMEZONE").Value;

                //A Calendar Component can be separated in multiples calendar components but all MUST
                //have the same UID.
                var calendarComponent = calendarComponents.FirstOrDefault();
                if (calendarComponent != null)
                {
                    var uid = calendarComponent.Properties["UID"].StringValue;
                    foreach (var component in calendarComponents)
                    {
                        var uidComp = component.Properties["UID"].StringValue;
                        if (uid != uidComp)
                        {
                            response.StatusCode = (int)HttpStatusCode.Conflict;
                            response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<valid-calendar-object-resource xmlns='urn:ietf:params:xml:ns:caldav'></valid-calendar-object-resource>
<error-description xmlns='http://twistedmatrix.com/xml_namespace/dav/'>
If the count of calendar components execeds 2 including VTimezone the rest must have the same Uid and the same type.
</error-description>
</error>");
                            return(false);
                        }
                    }
                }

                //                response.StatusCode = (int)HttpStatusCode.Conflict;
                //                response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
                //<error xmlns='DAV:'>
                //<valid-calendar-object-resource xmlns='urn:ietf:params:xml:ns:caldav'></valid-calendar-object-resource>
                //<error-description xmlns='http://twistedmatrix.com/xml_namespace/dav/'>
                //Wrong amount of calendar components
                //</error-description>
                //</error>");
                //                return false;
            }

            //precondition responsible of check that an VTIMEZONE is obligatory
            if (iCalendar.CalendarComponents.Count == 2)
            {
                if (!iCalendar.CalendarComponents.ContainsKey("VTIMEZONE"))
                {
                    response.StatusCode = (int)HttpStatusCode.Conflict;
                    response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<valid-calendar-object-resource xmlns='urn:ietf:params:xml:ns:caldav'></valid-calendar-object-resource>
<error-description xmlns='http://twistedmatrix.com/xml_namespace/dav/'>
VTimezone Calendar Component Must be present.
</error-description>
</error>");
                    return(false);
                }
            }


            //var uidCalendar = ((ComponentProperty<string>)iCalendar.Properties["UID"]).Value;
            ////Check that if the operation is create there is not another element in the collection with the same UID
            //if (!StorageManagement.ExistCalendarObjectResource(calendarResourceId))
            //{
            //    using (db)
            //    {
            //        if ((from calendarResource in db.CalendarResources
            //             where calendarResource.Uid == uidCalendar
            //             select calendarResource).Count() > 0)
            //            return false;

            //    }
            //}
            ////Check if the operation is update the element to be updated must have the same UID.
            //else
            //{
            //    using (db)
            //    {
            //        if ((from calendarResource in db.CalendarResources
            //             where calendarResource.Uid == uidCalendar
            //             select calendarResource).Count() == 0)
            //            return false;

            //    }
            //}

            var methodProp = iCalendar.GetComponentProperties("METHOD");
            //iCalendar object MUST NOT implement METHOD property
            if (!string.IsNullOrEmpty(methodProp?.StringValue))
            {
                response.StatusCode = (int)HttpStatusCode.Conflict;
                response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<valid-calendar-object-resource xmlns='urn:ietf:params:xml:ns:caldav'></valid-calendar-object-resource>
<error-description xmlns='http://twistedmatrix.com/xml_namespace/dav/'>
Method prop must not be present
</error-description>
</error>");
                return(false);
            }

            //This precondition is the one in charge of check that the size of the body of the resource
            //included in the request dont exceeds the max-resource-size property of the colletion.
            int contentSizeInt;
            //for that i need that the controller has as request header content-size available
            if (!string.IsNullOrEmpty(contentSize) && int.TryParse(contentSize, out contentSizeInt))
            {
                var collection =
                    _collectionRepository.Get(url.Remove(url.LastIndexOf("/", StringComparison.Ordinal) + 1));
                //here the max-resource-property of the collection is called.
                var maxSize =
                    collection.Properties.FirstOrDefault(
                        p => p.Name == "max-resource-size" && p.Namespace == "urn:ietf:params:xml:ns:caldav");
                int maxSizeInt;
                if (int.TryParse(XmlTreeStructure.Parse(maxSize?.Value).Value, out maxSizeInt) &&
                    contentSizeInt > maxSizeInt)
                {
                    response.StatusCode = (int)HttpStatusCode.Conflict;
                    response.Body.Write(@"<?xml version='1.0' encoding='UTF-8'?>
<error xmlns='DAV:'>
<max-resource-size xmlns='urn:ietf:params:xml:ns:caldav'></max-resource-size>
<error-description xmlns='http://twistedmatrix.com/xml_namespace/dav/'>
Content size exceeds max size allowed.
</error-description>
</error>");
                    return(false);
                }
            }


            //TODO: Checking that all DateTime values are less-equal than
            //the max-date-time

            //TODO: Checking that all DateTime values are grater-equal than
            //the min-date-time

            //TODO: Checking that the number of recurring instances is less-equal
            //than the max-instances property value.

            return(await Task.FromResult(true));
        }
 /// <summary>
 ///     Returns the real value of the given property.
 /// </summary>
 /// <param name="property"></param>
 /// <returns></returns>
 public static string PropertyRealValue(this Property property)
 {
     return(XmlTreeStructure.Parse(property.Value).Value);
 }
示例#27
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);
        }
示例#28
0
        public void ToStringTest()
        {
            var       calString    = @"BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
X-WR-CALNAME:[email protected]
X-WR-TIMEZONE:America/Los_Angeles
BEGIN:VTIMEZONE
TZID:America/Los_Angeles
X-LIC-LOCATION:America/Los_Angeles
BEGIN:DAYLIGHT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
TZNAME:PDT
DTSTART:19700308T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
TZNAME:PST
DTSTART:19701101T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
RDATE:19450603T010000
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=America/Los_Angeles:20120629T130000
DTEND;TZID=America/Los_Angeles:20120629T140000
DTSTAMP:20120629T112428Z
UID:[email protected]
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
CREATED:20120629T111935Z
DESCRIPTION:foo
LAST-MODIFIED:20120629T112428Z
LOCATION:Barcelona
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Demo B2G Calendar
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:EMAIL
DESCRIPTION:This is an event reminder
SUMMARY:Alarm notification
ATTENDEE:mailto:[email protected]
TRIGGER:-P0DT0H30M0S
END:VALARM
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:This is an event reminder
TRIGGER:-P0DT0H30M0S
END:VALARM
END:VEVENT
END:VCALENDAR
";
            VCalendar calendar     = VCalendar.Parse(calString);
            var       xmlDoc       = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
<C:calendar-query xmlns:D=""DAV:""
xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<D:prop>
<D:getetag/>
<C:calendar-data>
<C:comp name=""VCALENDAR"">
<C:prop name=""VERSION""/>
<C:comp name=""VEVENT"">
<C:prop name=""SUMMARY""/>
<C:prop name=""UID""/>
<C:prop name=""DTSTART""/>
<C:prop name=""DTEND""/>
</C:comp>
<C:comp name=""VTIMEZONE""/>
</C:comp>
</C:calendar-data>
</D:prop>
<C:filter>
<C:comp-filter name=""VCALENDAR"">
<C:comp-filter name=""VEVENT"">
	<C:time-range start=""20060104T000000Z""
end=""20060105T000000Z""/>
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>";
            var       xmlTree      = XmlTreeStructure.Parse(xmlDoc);
            var       node         = xmlTree.GetChildAtAnyLevel("calendar-data");
            var       newCalString = calendar.ToString(node);
            var       newCal       = new VCalendar(newCalString);

            Assert.Equal(2, newCal.CalendarComponents.Count);
            Assert.Contains("VEVENT", newCal.CalendarComponents.Keys);
            Assert.Contains("VTIMEZONE", newCal.CalendarComponents.Keys);
            Assert.Equal(4, newCal.CalendarComponents["VEVENT"].First().Properties.Count);
        }
示例#29
0
        public void UnitTest3()
        {
            var calStr = @"BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VTIMEZONE
LAST-MODIFIED:20040110T032845Z
TZID:US/Eastern
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:[email protected]
ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:[email protected]
DTSTAMP:20060206T001220Z
DTSTART;TZID=US/Eastern:20060104T100000
DURATION:PT1H
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
LAST-MODIFIED:20060206T001330Z
ORGANIZER:mailto:[email protected]
SEQUENCE:1
STATUS:TENTATIVE
SUMMARY:Event #3
UID:[email protected]
X-ABC-GUID:[email protected]
END:VEVENT
END:VCALENDAR";

            var result = VCalendar.Parse(calStr);

            var xmlStr = @"
<C:calendar-data xmlns:C=""urn:ietf:params:xml:ns:caldav"">
<C:comp name=""VCALENDAR"">
<C:prop name=""VERSION""/>
<C:comp name=""VEVENT"">
<C:prop name=""SUMMARY""/>
<C:prop name=""UID""/>
<C:prop name=""DTSTART""/>
<C:prop name=""DTEND""/>
<C:prop name=""DURATION""/>
<C:prop name=""RRULE""/>
<C:prop name=""ATTENDEE""/>
<C:prop name=""EXRULE""/>
<C:prop name=""EXDATE""/>
<C:prop name=""RECURRENCE-ID""/>
</C:comp>
<C:comp name=""VTIMEZONE""/>
</C:comp>
</C:calendar-data>";

            var calString = result.ToString(XmlTreeStructure.Parse(xmlStr));
        }