386 lines
8.4 KiB
TypeScript
386 lines
8.4 KiB
TypeScript
|
|
namespace _zeitbild.service.caldav
|
|
{
|
|
|
|
/**
|
|
* @todo use pod for output
|
|
* @todo get api paths in props from config
|
|
* @todo consider to outsorce to plankton
|
|
*/
|
|
export function probe(
|
|
input : (null | lib_plankton.xml.type_node_data),
|
|
{
|
|
"force_props": force_props = null,
|
|
} : {
|
|
force_props ?: (null | Array<string>);
|
|
} = {
|
|
}
|
|
) : (null | lib_plankton.xml.type_node_data)
|
|
{
|
|
const http_protocol : string = "HTTP/1.1";
|
|
let props : (null | Array<string>);
|
|
if (force_props) {
|
|
props = force_props;
|
|
}
|
|
else {
|
|
if (
|
|
(input !== null)
|
|
&&
|
|
(input.kind === "complex")
|
|
&&
|
|
(
|
|
(input.data.tag.toLowerCase() === "d:propfind")
|
|
||
|
|
(input.data.tag.toLowerCase() === "propfind")
|
|
)
|
|
&&
|
|
(input.data.children.length === 1)
|
|
&&
|
|
(input.data.children[0].kind === "complex")
|
|
&&
|
|
(
|
|
(input.data.children[0].data.tag.toLowerCase() === "d:prop")
|
|
||
|
|
(input.data.children[0].data.tag.toLowerCase() === "prop")
|
|
)
|
|
) {
|
|
props = input.data.children[0].data.children.map(
|
|
node => {
|
|
switch (node.kind) {
|
|
case "complex": {
|
|
return node.data.tag;
|
|
break;
|
|
}
|
|
default: {
|
|
throw (new Error("unexpected node type for prop"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
);
|
|
props.sort();
|
|
}
|
|
else {
|
|
props = null;
|
|
}
|
|
}
|
|
if (props === null) {
|
|
lib_plankton.log.notice(
|
|
"service.caldav.probe.unexpected_input",
|
|
{
|
|
"input": input,
|
|
}
|
|
);
|
|
return null;
|
|
}
|
|
else {
|
|
const answers : Record<
|
|
string,
|
|
lib_plankton.webdav.type_data_prop_value
|
|
> = {
|
|
// webdav 1
|
|
|
|
/**
|
|
* @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.2
|
|
*/
|
|
"displayname": {
|
|
"kind": "primitive",
|
|
"data": "projects"
|
|
},
|
|
/**
|
|
* @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.4
|
|
*/
|
|
/*
|
|
"getcontentlength": {
|
|
"kind": "none",
|
|
"data": null
|
|
},
|
|
*/
|
|
/**
|
|
* @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.5
|
|
*/
|
|
/*
|
|
"getcontenttype": {
|
|
"kind": "none",
|
|
"data": null
|
|
},
|
|
*/
|
|
/**
|
|
* @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.7
|
|
*/
|
|
/*
|
|
"getlastmodified": {
|
|
"kind": "none",
|
|
"data": null
|
|
},
|
|
*/
|
|
/**
|
|
* @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.9
|
|
*/
|
|
"resourcetype": {
|
|
"kind": "resourcetype",
|
|
"data": {
|
|
"kind": "collection",
|
|
"type": "calendar",
|
|
}
|
|
},
|
|
|
|
// webdav 2
|
|
|
|
/**
|
|
* @see https://datatracker.ietf.org/doc/html/rfc3744#section-4.2
|
|
*/
|
|
"principal-url": {
|
|
"kind": "href",
|
|
"data": "/caldav"
|
|
},
|
|
/**
|
|
* @see https://datatracker.ietf.org/doc/html/rfc3744#section-5.1
|
|
*/
|
|
"owner": {
|
|
"kind": "primitive",
|
|
"data": ""
|
|
},
|
|
/**
|
|
* @see https://datatracker.ietf.org/doc/html/rfc3744#section-5.4
|
|
*/
|
|
"current-user-privilege-set": {
|
|
"kind": "privileges",
|
|
"data": [
|
|
"read"
|
|
]
|
|
},
|
|
|
|
// caldav
|
|
|
|
/**
|
|
* @see https://datatracker.ietf.org/doc/html/rfc4791#section-6.2.1
|
|
*/
|
|
"calendar-home-set": {
|
|
"kind": "href",
|
|
"data": "/caldav/project"
|
|
},
|
|
|
|
// WebDAV Current Principal Extension
|
|
|
|
/**
|
|
* @see https://datatracker.ietf.org/doc/html/rfc5397#section-3
|
|
*/
|
|
"current-user-principal": {
|
|
"kind": "href",
|
|
"data": "/caldav"
|
|
},
|
|
|
|
// unknown
|
|
|
|
/*
|
|
"calendar-color": {
|
|
"kind": "none",
|
|
"data": null
|
|
},
|
|
"executable": {
|
|
"kind": "none",
|
|
"data": null
|
|
},
|
|
"checked-in": {
|
|
"kind": "none",
|
|
"data": null
|
|
},
|
|
"checked-out": {
|
|
"kind": "none",
|
|
"data": null
|
|
},
|
|
"calendar-user-address-set": {
|
|
"kind": "none",
|
|
"data": null
|
|
},
|
|
*/
|
|
};
|
|
return {
|
|
"kind": "root",
|
|
"data": {
|
|
"version": "1.0",
|
|
"encoding": "utf-8",
|
|
"content": lib_plankton.webdav.data_multistatus_encode_xml(
|
|
{
|
|
"responses": [
|
|
{
|
|
"href": "/caldav/project",
|
|
"body": {
|
|
/**
|
|
* @todo maybe propstats needs to be filled with props (.map …)
|
|
*/
|
|
"propstats": (
|
|
false
|
|
?
|
|
[
|
|
{
|
|
"prop": (props ?? []).map(
|
|
(prop) => {
|
|
const prop_parts : Array<string> = prop.toLowerCase().split(":");
|
|
const prop_normalized : string = ((prop_parts.length <= 1) ? prop_parts[0] : prop_parts.slice(1).join(":"));
|
|
if (! (prop_normalized in answers)) {
|
|
lib_plankton.log.error(
|
|
"api.caldav_probe.unhandled_prop",
|
|
prop_normalized
|
|
);
|
|
throw (new Error("unhandled prop: " + prop_normalized));
|
|
}
|
|
else {
|
|
return {
|
|
"name": prop,
|
|
"value": (
|
|
answers[prop_normalized]
|
|
??
|
|
{
|
|
"kind": "none",
|
|
"data": null,
|
|
}
|
|
),
|
|
};
|
|
}
|
|
}
|
|
),
|
|
"status": (http_protocol + " 200 OK"),
|
|
"description": null,
|
|
},
|
|
]
|
|
:
|
|
props.map(
|
|
(prop) => {
|
|
const prop_parts : Array<string> = prop.toLowerCase().split(":");
|
|
const prop_normalized : string = (
|
|
(prop_parts.length <= 1)
|
|
?
|
|
prop_parts[0]
|
|
:
|
|
prop_parts.slice(1).join(":")
|
|
);
|
|
if (! (prop_normalized in answers)) {
|
|
lib_plankton.log.notice(
|
|
"api.caldav_probe.unhandled_prop",
|
|
prop_normalized
|
|
);
|
|
/*
|
|
throw (new Error("unhandled prop: " + prop_normalized));
|
|
*/
|
|
return {
|
|
"prop": [
|
|
{
|
|
"name": prop,
|
|
"value": {
|
|
"kind": "none",
|
|
"data": null,
|
|
}
|
|
},
|
|
],
|
|
"status": (http_protocol + " 404 Not Found"),
|
|
"description": null,
|
|
};
|
|
}
|
|
else {
|
|
return {
|
|
"prop": [
|
|
{
|
|
"name": prop,
|
|
"value": answers[prop_normalized],
|
|
},
|
|
],
|
|
"status": (http_protocol + " 200 OK"),
|
|
"description": null,
|
|
};
|
|
};
|
|
}
|
|
)
|
|
)
|
|
},
|
|
"description": null,
|
|
}
|
|
],
|
|
"description": null,
|
|
}
|
|
)
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*/
|
|
export function projects(
|
|
user_id : type_user_id
|
|
) : Promise<lib_plankton.xml.type_node_data>
|
|
{
|
|
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": [
|
|
{
|
|
"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,
|
|
}
|
|
],
|
|
},
|
|
"description": null,
|
|
})
|
|
),
|
|
"description": null,
|
|
}
|
|
)
|
|
}
|
|
}
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
}
|