@@ -21,6 +21,7 @@ import 'package:flow_api/models/event/model.dart';
2121import 'package:flow_api/models/extra.dart' ;
2222import 'package:flow_api/services/database.dart' ;
2323import 'package:xml/xml.dart' ;
24+ import 'package:collection/collection.dart' ;
2425
2526import '../../../models/request.dart' ;
2627import 'model.dart' ;
@@ -57,10 +58,11 @@ class CalDavRemoteService extends RemoteService<CalDavStorage> {
5758 Future <void > synchronize () async {
5859 await super .synchronize ();
5960 final client = http.Client ();
60- final request = http.Request ('REPORT' , Uri .parse (remoteStorage.url));
61- request.headers['Depth' ] = '1' ;
62- request.headers['Content-Type' ] = 'application/xml; charset=utf-8' ;
63- request.body = '''
61+ try {
62+ final request = http.Request ('REPORT' , Uri .parse (remoteStorage.url));
63+ request.headers['Depth' ] = '1' ;
64+ request.headers['Content-Type' ] = 'application/xml; charset=utf-8' ;
65+ request.body = '''
6466<?xml version="1.0" encoding="utf-8" ?>
6567<C:calendar-query xmlns:D="DAV:"
6668 xmlns:C="urn:ietf:params:xml:ns:caldav">
@@ -76,37 +78,49 @@ class CalDavRemoteService extends RemoteService<CalDavStorage> {
7678 </C:filter>
7779</C:calendar-query>
7880''' ;
79- // Add auth basic
80- request.headers['Authorization' ] = _getAuthHeader ();
81- final response = await client.send (request);
82- final xmlDocument = XmlDocument .parse (
83- await response.stream.bytesToString (),
84- );
85- // Get /d:multistatus/d:response/d:propstat/d:prop/cal:calendar-data
86- final data =
87- xmlDocument.getElement ("d:multistatus" )? .findElements ("d:response" ) ??
88- [];
89- final converter = ICalConverter ();
90- for (var element in data) {
91- final href = element.getElement ("d:href" )? .innerText;
92- final prop = element.getElement ("d:propstat" )? .getElement ("d:prop" );
93- if (href == null ) continue ;
94- final text = prop? .getElement ("cal:calendar-data" )? .innerText;
95- if (text == null ) continue ;
96- final etag = prop? .getElement ("d:getetag" )? .innerText;
97- if (etag == null ) continue ;
98- final name = href.substring (href.lastIndexOf ('/' ) + 1 );
99- final id = createUniqueUint8List ();
100- converter.read (
101- text.split ('\n ' ),
102- event: Event (
103- name: name,
104- id: id,
105- ).addExtra (CalDavExtraProperties (etag: etag, path: href)),
106- notebook: Notebook (id: id, name: name),
81+ // Add auth basic
82+ request.headers['Authorization' ] = _getAuthHeader ();
83+ final response = await client.send (request);
84+ if (response.statusCode < 200 || response.statusCode >= 300 ) {
85+ throw http.ClientException (
86+ 'Failed to synchronize CalDAV source: ${response .statusCode }' ,
87+ Uri .parse (remoteStorage.url),
88+ );
89+ }
90+ final xmlDocument = XmlDocument .parse (
91+ await response.stream.bytesToString (),
10792 );
93+ // Get /multistatus/response/propstat/prop/calendar-data,
94+ // regardless of which XML prefixes the server chooses.
95+ final data = xmlDocument.rootElement.name.local == 'multistatus'
96+ ? xmlDocument.rootElement.findElementsByLocalName ('response' )
97+ : const < XmlElement > [];
98+ final converter = ICalConverter ();
99+ for (var element in data) {
100+ final href = element.getElementByLocalName ('href' )? .innerText;
101+ final prop = element
102+ .getElementByLocalName ('propstat' )
103+ ? .getElementByLocalName ('prop' );
104+ if (href == null ) continue ;
105+ final text = prop? .getElementByLocalName ('calendar-data' )? .innerText;
106+ if (text == null ) continue ;
107+ final etag = prop? .getElementByLocalName ('getetag' )? .innerText;
108+ if (etag == null ) continue ;
109+ final name = href.substring (href.lastIndexOf ('/' ) + 1 );
110+ final id = createUniqueUint8List ();
111+ converter.read (
112+ text.split ('\n ' ),
113+ event: Event (
114+ name: name,
115+ id: id,
116+ ).addExtra (CalDavExtraProperties (etag: etag, path: href)),
117+ notebook: Notebook (id: id, name: name),
118+ );
119+ }
120+ if (converter.data != null ) import (converter.data! );
121+ } finally {
122+ client.close ();
108123 }
109- if (converter.data != null ) import (converter.data! );
110124 }
111125
112126 String _getAuthHeader () =>
@@ -165,6 +179,15 @@ class CalDavRemoteService extends RemoteService<CalDavStorage> {
165179 get groupLabel => local.groupLabel;
166180}
167181
182+ extension _XmlElementLocalNameLookup on XmlElement {
183+ Iterable <XmlElement > findElementsByLocalName (String localName) => children
184+ .whereType <XmlElement >()
185+ .where ((element) => element.name.local == localName);
186+
187+ XmlElement ? getElementByLocalName (String localName) =>
188+ findElementsByLocalName (localName).firstOrNull;
189+ }
190+
168191class EventModelCalDavConnector <I > extends ModelConnector <I , Event > {
169192 final CalDavRemoteService remote;
170193 final ModelConnector <I , Event > service;
0 commit comments