diff --git a/source/api/actions/caldav_projects.ts b/source/api/actions/caldav_projects.ts index f530a84..6d6c82e 100644 --- a/source/api/actions/caldav_projects.ts +++ b/source/api/actions/caldav_projects.ts @@ -9,12 +9,12 @@ namespace _zeitbild.api ) : void { register< + lib_plankton.xml.type_node_data, ( null | lib_plankton.xml.type_node_data - ), - lib_plankton.xml.type_node_data + ) >( rest_subject, lib_plankton.caldav.enum_method.propfind, @@ -48,6 +48,9 @@ namespace _zeitbild.api ), // "restriction": restriction_basic_auth, "restriction": restriction_none, + /** + * @todo heed stuff.input + */ "execution": async (stuff) => { const user_id : (null | type_user_id) = await _zeitbild.api.web_auth( stuff.headers["Authorization"] @@ -71,17 +74,30 @@ namespace _zeitbild.api ); } else { - return ( - _zeitbild.service.caldav.projects(user_id) - .then( - (output) => Promise.resolve( - { - "status_code": 207, - "data": output, - } + if (stuff.input === null) { + return Promise.resolve( + { + "status_code": 400, + "data": null, + } + ); + } + else { + return ( + _zeitbild.service.caldav.projects( + stuff.input, + user_id ) - ) - ); + .then( + (output) => Promise.resolve( + { + "status_code": 207, + "data": output, + } + ) + ) + ); + } } } } diff --git a/source/conf.ts b/source/conf.ts index a94484e..5ed5613 100644 --- a/source/conf.ts +++ b/source/conf.ts @@ -287,6 +287,9 @@ namespace _zeitbild.conf "nullable": false, "type": "object", "properties": { + /** + * @todo make mandatory + */ "auth_salt": { "nullable": false, "type": "string", diff --git a/source/services/caldav.ts b/source/services/caldav.ts index 1e101f2..208ce1f 100644 --- a/source/services/caldav.ts +++ b/source/services/caldav.ts @@ -78,7 +78,7 @@ namespace _zeitbild.service.caldav string, lib_plankton.webdav.type_data_prop_value > = { - // webdav 1 + // RFC2518 /** * @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.2 @@ -125,7 +125,7 @@ namespace _zeitbild.service.caldav } }, - // webdav 2 + // RFCP3744 /** * @see https://datatracker.ietf.org/doc/html/rfc3744#section-4.2 @@ -151,8 +151,15 @@ namespace _zeitbild.service.caldav ] }, - // caldav + // RFC4791 + /** + * @see https://datatracker.ietf.org/doc/html/rfc4791#section-5.2.3 + */ + "supported-calendar-component-set": { + "kind": "primitive", + "data": "VEVENT" + }, /** * @see https://datatracker.ietf.org/doc/html/rfc4791#section-6.2.1 */ @@ -161,7 +168,17 @@ namespace _zeitbild.service.caldav "data": "/caldav/project" }, - // WebDAV Current Principal Extension + // RFC4918 + + /** + * @see https://datatracker.ietf.org/doc/html/rfc4918#section-15.6 + */ + "getetag": { + "kind": "primitive", + "data": "" + }, + + // RFC5397 /** * @see https://datatracker.ietf.org/doc/html/rfc5397#section-3 @@ -170,7 +187,17 @@ namespace _zeitbild.service.caldav "kind": "href", "data": "/caldav" }, - + + // RFC6638 + + /** + * @see https://datatracker.ietf.org/doc/html/rfc6638#section-2.4.1 + */ + "calendar-user-address-set": { + "kind": "primitive", + "data": "" + }, + // unknown /* @@ -189,10 +216,6 @@ namespace _zeitbild.service.caldav "checked-out": { "kind": "none", "data": null - }, - "calendar-user-address-set": { - "kind": "none", - "data": null }, */ }; @@ -307,80 +330,181 @@ namespace _zeitbild.service.caldav /** + * @todo heed input properly */ export function projects( + input : lib_plankton.xml.type_node_data, user_id : type_user_id ) : Promise { - return ( - _zeitbild.service.calendar.overview(user_id) - .then( - (data_raw) => Promise.resolve( - data_raw - .map( - (entry) => ({ - "id": entry.id, - "name": entry.name, - "access_level": _zeitbild.value_object.access_level.to_string(entry.access_level), - }) + const answers : Record< + string, + ((stuff : any) => lib_plankton.webdav.type_data_prop_value) + > = { + "displayname": (stuff) => ({ + "kind": "primitive", + "data": stuff["name"] + }), + "resourcetype": () => ({ + "kind": "resourcetype", + "data": { + "kind": "collection", + "type": "calendar", + } + }), + }; + /** + * @todo remove + */ + lib_plankton.log.info( + "service.caldav.input", + input + ); + /** + * @todo check data structure + */ + if ( + ! ( + (input.kind === "complex") + && + (input.data.tag === "propfind") + && + (input.data.children.length === 1) + && + (input.data.children[0].kind === "complex") + && + (input.data.children[0].data.tag === "prop") + && + input.data.children[0].data.children.every( + node => (node.kind === "complex") + ) + ) + ) { + throw (new Error("wrong input structure")); + } + else { + const props : Array = ( + input + .data.children + [0] + .data.children + .map( + (node) => node.data.tag + ) + ); + /** + * @todo remove + */ + lib_plankton.log.info( + "service.caldav.props", + props + ); + return ( + _zeitbild.service.calendar.overview(user_id) + .then( + (data_raw) => Promise.resolve( + data_raw + .map( + (entry) => ({ + "id": entry.id, + "name": entry.name, + "access_level": _zeitbild.value_object.access_level.to_string(entry.access_level), + }) + ) ) ) - ) - .then( - (data) => Promise.resolve( - { - "kind": "root", - "data": { - "version": "1.0", - "encoding": "utf-8", - "content": lib_plankton.webdav.data_multistatus_encode_xml( - { - "responses": data.map( - (entry) => ({ - "href": lib_plankton.string.coin( - "/caldav/project/{{id}}", - { - "id": entry.id.toFixed(0), - } - ), - "body": { - "propstats": [ + .then( + (data) => Promise.resolve( + { + "kind": "root", + "data": { + "version": "1.0", + "encoding": "utf-8", + "content": lib_plankton.webdav.data_multistatus_encode_xml( + { + "responses": data.map( + (entry) => ({ + "href": lib_plankton.string.coin( + "/caldav/project/{{id}}", { - "prop": [ - { - "name": "D:displayname", - "value": { - "kind": "primitive", - "data": entry.name, - }, - }, - { - "name": "D:resourcetype", - "value": { - "kind": "resourcetype", - "data": { - "kind": "collection", - "type": "calendar", - } - } - }, - ], - "status": "HTTP/1.1 200 OK", - "description": null, + "id": entry.id.toFixed(0), } - ], - }, - "description": null, - }) - ), - "description": null, - } - ) + ), + "body": { + /* + "propstats": [ + { + "prop": [ + { + "name": "D:displayname", + "value": { + "kind": "primitive", + "data": entry.name, + }, + }, + { + "name": "D:resourcetype", + "value": { + "kind": "resourcetype", + "data": { + "kind": "collection", + "type": "calendar", + } + } + }, + ], + "status": "HTTP/1.1 200 OK", + "description": null, + } + ], + */ + "propstats": props.map( + prop => { + const prop_parts : Array = prop.toLowerCase().split(":"); + const prop_normalized : string = ((prop_parts.length <= 1) ? prop_parts[0] : prop_parts.slice(1).join(":")); + return ( + (! (prop_normalized in answers)) + ? + { + "prop": [ + { + "name": prop, + "value": { + "kind": "none", + "data": null + } + }, + ], + "status": "HTTP/1.1 200 OK", + "description": null, + } + : + { + "prop": [ + { + "name": prop, + "value": answers[prop_normalized](entry), + }, + ], + "status": "HTTP/1.1 404 Not Found", + "description": null, + } + ); + } + ) + }, + "description": null, + }) + ), + "description": null, + } + ) + } } - } + ) ) - ) - ); + ); + } } }