mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 12:22:16 +00:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c2a6006c7 | ||
|
|
4600f03b45 | ||
|
|
41fc1e6ea5 | ||
|
|
b56591c482 | ||
|
|
d639b18005 | ||
|
|
6046439fc7 | ||
|
|
f9de8a4687 | ||
|
|
8dfb47b28f | ||
|
|
eb720ded99 | ||
|
|
89ef7b2ced | ||
|
|
6e0129130e | ||
|
|
c646986c56 | ||
|
|
503cbe3699 | ||
|
|
79c66a0b46 | ||
|
|
e5687c6e43 | ||
|
|
79b67a17c3 | ||
|
|
7d18faff69 | ||
|
|
753f8e90d3 | ||
|
|
701fa9dd9c | ||
|
|
31b17cfe7f | ||
|
|
d802a0085a | ||
|
|
786b15f5b9 | ||
|
|
f5d097ac55 | ||
|
|
668fa86e3c | ||
|
|
23d2024644 | ||
|
|
15aadcf1be | ||
|
|
4a3b7d7ce6 | ||
|
|
1a2f3b8f8a | ||
|
|
9e8c218308 | ||
|
|
f2adce739b | ||
|
|
0415664ff3 | ||
|
|
677e0082fa | ||
|
|
a387885b0a | ||
|
|
990b953055 | ||
|
|
36b47a645d | ||
|
|
aa02d11f58 | ||
|
|
1c31323512 | ||
|
|
03ae492483 | ||
|
|
0c48507f0c | ||
|
|
829d4a4385 | ||
|
|
4fe28c5b0f | ||
|
|
529f36ad99 | ||
|
|
ca5891314c | ||
|
|
e653c68cae | ||
|
|
26941c621b | ||
|
|
86ab6ef75e | ||
|
|
0669d4e683 | ||
|
|
0c432d70f9 | ||
|
|
54997ef865 | ||
|
|
1a1deeb5a2 | ||
|
|
87899738f6 | ||
|
|
ab90e5129c | ||
|
|
a9cb397f57 | ||
|
|
35e78bfb44 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,2 +1,3 @@
|
||||
# Otherwise GitHub thinks this is an HTML project
|
||||
crates/frontend/public/assets/licenses.html linguist-detectable=false
|
||||
crates/frontend/public/assets/js/* linguist-detectable=false
|
||||
|
||||
4
.github/workflows/docker-publish.yml
vendored
4
.github/workflows/docker-publish.yml
vendored
@@ -41,12 +41,10 @@ jobs:
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
# As long as we don't have releases everything on the main branch shall be tagged as latest
|
||||
tags: |
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -12,3 +12,7 @@ principals.toml
|
||||
.env
|
||||
|
||||
site
|
||||
|
||||
# Frontend
|
||||
**/node_modules
|
||||
**/.vite
|
||||
|
||||
12
.sqlx/query-0195268daddd2d171577c93d1bae1b8937405bcefffa8f1f9b9c9f7f2084088f.json
generated
Normal file
12
.sqlx/query-0195268daddd2d171577c93d1bae1b8937405bcefffa8f1f9b9c9f7f2084088f.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "DELETE FROM app_tokens WHERE (principal, id) = (?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0195268daddd2d171577c93d1bae1b8937405bcefffa8f1f9b9c9f7f2084088f"
|
||||
}
|
||||
12
.sqlx/query-02a9260d0ff496a6bf226fc8238ae332f8eb18dddbd80d31989c074804f31dee.json
generated
Normal file
12
.sqlx/query-02a9260d0ff496a6bf226fc8238ae332f8eb18dddbd80d31989c074804f31dee.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "UPDATE calendars SET deleted_at = NULL WHERE (principal, id) = (?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "02a9260d0ff496a6bf226fc8238ae332f8eb18dddbd80d31989c074804f31dee"
|
||||
}
|
||||
12
.sqlx/query-0fd3167a58cbfb4ee44249dbc346d2d9077adfa04c35c8c6f2a1e24720baf753.json
generated
Normal file
12
.sqlx/query-0fd3167a58cbfb4ee44249dbc346d2d9077adfa04c35c8c6f2a1e24720baf753.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO app_tokens\n (id, principal, token, displayname)\n VALUES (?, ?, ?, ?)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0fd3167a58cbfb4ee44249dbc346d2d9077adfa04c35c8c6f2a1e24720baf753"
|
||||
}
|
||||
12
.sqlx/query-10325688a6601f6205cde9d9e2d582ca87a46607a1d889af155debc3073d78e1.json
generated
Normal file
12
.sqlx/query-10325688a6601f6205cde9d9e2d582ca87a46607a1d889af155debc3073d78e1.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "UPDATE calendarobjects SET deleted_at = NULL, updated_at = datetime() WHERE (principal, cal_id, id) = (?, ?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "10325688a6601f6205cde9d9e2d582ca87a46607a1d889af155debc3073d78e1"
|
||||
}
|
||||
56
.sqlx/query-130986d03d4d78ceeb15aff6f4d6304f0be0100e4bffad9cc3f6c1a2c6c4b297.json
generated
Normal file
56
.sqlx/query-130986d03d4d78ceeb15aff6f4d6304f0be0100e4bffad9cc3f6c1a2c6c4b297.json
generated
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT principal, id, synctoken, displayname, description, deleted_at, push_topic\n FROM addressbooks\n WHERE (principal, id) = (?, ?)\n AND ((deleted_at IS NULL) OR ?) ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "principal",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "synctoken",
|
||||
"ordinal": 2,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "displayname",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "deleted_at",
|
||||
"ordinal": 5,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "push_topic",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "130986d03d4d78ceeb15aff6f4d6304f0be0100e4bffad9cc3f6c1a2c6c4b297"
|
||||
}
|
||||
12
.sqlx/query-16a7e0cb4527060339c168ee2528416036e401f75a03100b6bfbee687b978520.json
generated
Normal file
12
.sqlx/query-16a7e0cb4527060339c168ee2528416036e401f75a03100b6bfbee687b978520.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "UPDATE addressbooks SET principal = ?, id = ?, displayname = ?, description = ?, push_topic = ?\n WHERE (principal, id) = (?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 7
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "16a7e0cb4527060339c168ee2528416036e401f75a03100b6bfbee687b978520"
|
||||
}
|
||||
38
.sqlx/query-1ebaf3fd99bee2382abc931a1eeb29badc3aabcf6b8fd58e4cf92721588a9966.json
generated
Normal file
38
.sqlx/query-1ebaf3fd99bee2382abc931a1eeb29badc3aabcf6b8fd58e4cf92721588a9966.json
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT id, displayname AS name, token, created_at AS \"created_at: _\" FROM app_tokens WHERE principal = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "token",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "created_at: _",
|
||||
"ordinal": 3,
|
||||
"type_info": "Datetime"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "1ebaf3fd99bee2382abc931a1eeb29badc3aabcf6b8fd58e4cf92721588a9966"
|
||||
}
|
||||
44
.sqlx/query-23a07f4a732f95ff7483cd1cfe3b74af4fe6b97546a631bc96d03bdc3d764ed0.json
generated
Normal file
44
.sqlx/query-23a07f4a732f95ff7483cd1cfe3b74af4fe6b97546a631bc96d03bdc3d764ed0.json
generated
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT id, displayname, principal_type, password_hash, json_group_array(member_of) AS \"memberships: Json<Vec<Option<String>>>\"\n FROM principals\n LEFT JOIN memberships ON principals.id == memberships.principal\n GROUP BY principals.id\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "displayname",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "principal_type",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "password_hash",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "memberships: Json<Vec<Option<String>>>",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "23a07f4a732f95ff7483cd1cfe3b74af4fe6b97546a631bc96d03bdc3d764ed0"
|
||||
}
|
||||
26
.sqlx/query-246ec675667992c1297c29348d46496a884c59adb8b64b569d36f4ce10f88f47.json
generated
Normal file
26
.sqlx/query-246ec675667992c1297c29348d46496a884c59adb8b64b569d36f4ce10f88f47.json
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT id, vcf FROM addressobjects WHERE (principal, addressbook_id, id) = (?, ?, ?) AND ((deleted_at IS NULL) OR ?)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "vcf",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "246ec675667992c1297c29348d46496a884c59adb8b64b569d36f4ce10f88f47"
|
||||
}
|
||||
12
.sqlx/query-2834e16e6a7acb58141a2433f7735d5e2bf913c30f9f3e7bd9fecc7d4376be0f.json
generated
Normal file
12
.sqlx/query-2834e16e6a7acb58141a2433f7735d5e2bf913c30f9f3e7bd9fecc7d4376be0f.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "DELETE FROM calendars WHERE (principal, id) = (?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "2834e16e6a7acb58141a2433f7735d5e2bf913c30f9f3e7bd9fecc7d4376be0f"
|
||||
}
|
||||
12
.sqlx/query-2b145d094188fab69371d98520a034c69c0b61583d4e245388d3879d290619d0.json
generated
Normal file
12
.sqlx/query-2b145d094188fab69371d98520a034c69c0b61583d4e245388d3879d290619d0.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "UPDATE addressobjects SET deleted_at = NULL, updated_at = datetime() WHERE (principal, addressbook_id, id) = (?, ?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "2b145d094188fab69371d98520a034c69c0b61583d4e245388d3879d290619d0"
|
||||
}
|
||||
12
.sqlx/query-2f043f62a7c0eae1023e319f0bc8f35dfdcf6a8247e03b1de3e2cabb2d3ab8ae.json
generated
Normal file
12
.sqlx/query-2f043f62a7c0eae1023e319f0bc8f35dfdcf6a8247e03b1de3e2cabb2d3ab8ae.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n REPLACE INTO principals\n (id, displayname, principal_type, password_hash)\n VALUES (?, ?, ?, ?)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "2f043f62a7c0eae1023e319f0bc8f35dfdcf6a8247e03b1de3e2cabb2d3ab8ae"
|
||||
}
|
||||
12
.sqlx/query-3885219f7e132876fa8bdfd31c0761d54fbcfe2846ffd8e4e94feb40547205be.json
generated
Normal file
12
.sqlx/query-3885219f7e132876fa8bdfd31c0761d54fbcfe2846ffd8e4e94feb40547205be.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "DELETE FROM addressbooks WHERE (principal, id) = (?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3885219f7e132876fa8bdfd31c0761d54fbcfe2846ffd8e4e94feb40547205be"
|
||||
}
|
||||
12
.sqlx/query-3a1dbfbe9d22a62f1830d004548b7e805bcb9fdd24b49c8c9efa93df149b1002.json
generated
Normal file
12
.sqlx/query-3a1dbfbe9d22a62f1830d004548b7e805bcb9fdd24b49c8c9efa93df149b1002.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "DELETE FROM principals WHERE id = ?",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3a1dbfbe9d22a62f1830d004548b7e805bcb9fdd24b49c8c9efa93df149b1002"
|
||||
}
|
||||
20
.sqlx/query-3b00b59f047e534a7f7f654984dc880f4aa9281aae5974722d2f22ec6d15cb32.json
generated
Normal file
20
.sqlx/query-3b00b59f047e534a7f7f654984dc880f4aa9281aae5974722d2f22ec6d15cb32.json
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT principal FROM memberships WHERE member_of = ?",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "principal",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "3b00b59f047e534a7f7f654984dc880f4aa9281aae5974722d2f22ec6d15cb32"
|
||||
}
|
||||
12
.sqlx/query-3e1cca532372e891ab3e604ecb79311d8cd64108d4f238db4c79e9467a3b6d2e.json
generated
Normal file
12
.sqlx/query-3e1cca532372e891ab3e604ecb79311d8cd64108d4f238db4c79e9467a3b6d2e.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "REPLACE INTO calendarobjects (principal, cal_id, id, ics, first_occurence, last_occurence, etag, object_type) VALUES (?, ?, ?, ?, date(?), date(?), ?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 8
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3e1cca532372e891ab3e604ecb79311d8cd64108d4f238db4c79e9467a3b6d2e"
|
||||
}
|
||||
26
.sqlx/query-41b415bfb07113cab4dc5d556d39d1d040025c33dfc24e276eb0b2a27ea1799f.json
generated
Normal file
26
.sqlx/query-41b415bfb07113cab4dc5d556d39d1d040025c33dfc24e276eb0b2a27ea1799f.json
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT DISTINCT object_id, max(0, synctoken) as \"synctoken!: i64\" from addressobjectchangelog\n WHERE synctoken > ?\n ORDER BY synctoken ASC\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "object_id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "synctoken!: i64",
|
||||
"ordinal": 1,
|
||||
"type_info": "Null"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "41b415bfb07113cab4dc5d556d39d1d040025c33dfc24e276eb0b2a27ea1799f"
|
||||
}
|
||||
12
.sqlx/query-508adcac37e0d6751924f65e9aed24c0185ce3b1bc39fd0eab7426f581aafe02.json
generated
Normal file
12
.sqlx/query-508adcac37e0d6751924f65e9aed24c0185ce3b1bc39fd0eab7426f581aafe02.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "INSERT INTO addressbooks (principal, id, displayname, description, push_topic)\n VALUES (?, ?, ?, ?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 5
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "508adcac37e0d6751924f65e9aed24c0185ce3b1bc39fd0eab7426f581aafe02"
|
||||
}
|
||||
12
.sqlx/query-5132ee8198f155242aa332a10019c48ec334884bcf7841c8aa03fd5eb11351d9.json
generated
Normal file
12
.sqlx/query-5132ee8198f155242aa332a10019c48ec334884bcf7841c8aa03fd5eb11351d9.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "INSERT INTO calendars (principal, id, displayname, description, \"order\", color, subscription_url, timezone, timezone_id, push_topic, comp_event, comp_todo, comp_journal)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 13
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5132ee8198f155242aa332a10019c48ec334884bcf7841c8aa03fd5eb11351d9"
|
||||
}
|
||||
12
.sqlx/query-526c4a9326db7f026eee15cca358943b0546fe56fc09204f1dfe90e2614f99b9.json
generated
Normal file
12
.sqlx/query-526c4a9326db7f026eee15cca358943b0546fe56fc09204f1dfe90e2614f99b9.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO addressobjectchangelog (principal, addressbook_id, object_id, \"operation\", synctoken)\n VALUES (?1, ?2, ?3, ?4, (\n SELECT synctoken FROM addressbooks WHERE (principal, id) = (?1, ?2)\n ))",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "526c4a9326db7f026eee15cca358943b0546fe56fc09204f1dfe90e2614f99b9"
|
||||
}
|
||||
26
.sqlx/query-543838c030550cb09d1af08adfeade8b7ce3575d92fddbc6e9582d141bc9e49d.json
generated
Normal file
26
.sqlx/query-543838c030550cb09d1af08adfeade8b7ce3575d92fddbc6e9582d141bc9e49d.json
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT id, ics FROM calendarobjects WHERE (principal, cal_id, id) = (?, ?, ?) AND ((deleted_at IS NULL) OR ?)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "ics",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "543838c030550cb09d1af08adfeade8b7ce3575d92fddbc6e9582d141bc9e49d"
|
||||
}
|
||||
26
.sqlx/query-54c9c0e36a52e6963f11c6aa27f13aafb4204b8aa34b664fd825bd447db80e86.json
generated
Normal file
26
.sqlx/query-54c9c0e36a52e6963f11c6aa27f13aafb4204b8aa34b664fd825bd447db80e86.json
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT id, ics FROM calendarobjects WHERE principal = ? AND cal_id = ? AND deleted_at IS NULL",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "ics",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "54c9c0e36a52e6963f11c6aa27f13aafb4204b8aa34b664fd825bd447db80e86"
|
||||
}
|
||||
26
.sqlx/query-557344035d762f2d385e505c15288ab9c89c1572f06e7fb61073e335a801b9db.json
generated
Normal file
26
.sqlx/query-557344035d762f2d385e505c15288ab9c89c1572f06e7fb61073e335a801b9db.json
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT id, vcf FROM addressobjects WHERE principal = ? AND addressbook_id = ? AND deleted_at IS NULL",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "vcf",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "557344035d762f2d385e505c15288ab9c89c1572f06e7fb61073e335a801b9db"
|
||||
}
|
||||
12
.sqlx/query-6327bee90e5df01536a0ddb15adcc37af3027f6902aa3786365c5ab2fbf06bda.json
generated
Normal file
12
.sqlx/query-6327bee90e5df01536a0ddb15adcc37af3027f6902aa3786365c5ab2fbf06bda.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "INSERT INTO calendarobjects (principal, cal_id, id, ics, first_occurence, last_occurence, etag, object_type) VALUES (?, ?, ?, ?, date(?), date(?), ?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 8
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6327bee90e5df01536a0ddb15adcc37af3027f6902aa3786365c5ab2fbf06bda"
|
||||
}
|
||||
20
.sqlx/query-69b92e393e55b0d49d1671abf53d06551452846dd94d54ed67d85eb3ace6b568.json
generated
Normal file
20
.sqlx/query-69b92e393e55b0d49d1671abf53d06551452846dd94d54ed67d85eb3ace6b568.json
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n UPDATE calendars\n SET synctoken = synctoken + 1\n WHERE (principal, id) = (?1, ?2)\n RETURNING synctoken",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "synctoken",
|
||||
"ordinal": 0,
|
||||
"type_info": "Integer"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "69b92e393e55b0d49d1671abf53d06551452846dd94d54ed67d85eb3ace6b568"
|
||||
}
|
||||
12
.sqlx/query-6d08d3a014743da9b445ab012437ec11f81fd86d3b02fc1df07a036c6b47ace2.json
generated
Normal file
12
.sqlx/query-6d08d3a014743da9b445ab012437ec11f81fd86d3b02fc1df07a036c6b47ace2.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "INSERT OR REPLACE INTO davpush_subscriptions (id, topic, expiration, push_resource, public_key, public_key_type, auth_secret) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 7
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6d08d3a014743da9b445ab012437ec11f81fd86d3b02fc1df07a036c6b47ace2"
|
||||
}
|
||||
12
.sqlx/query-77ad562b6d78115ebb726d58cf1e4de69df169c1872cb452488ff8c43ed983a9.json
generated
Normal file
12
.sqlx/query-77ad562b6d78115ebb726d58cf1e4de69df169c1872cb452488ff8c43ed983a9.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n INSERT INTO calendarobjectchangelog (principal, cal_id, object_id, \"operation\", synctoken)\n VALUES (?1, ?2, ?3, ?4, (\n SELECT synctoken FROM calendars WHERE (principal, id) = (?1, ?2)\n ))",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "77ad562b6d78115ebb726d58cf1e4de69df169c1872cb452488ff8c43ed983a9"
|
||||
}
|
||||
12
.sqlx/query-7d4348f04ea5ac82e0f362240fb677740288c963c24b85de11bad011ec5da4bc.json
generated
Normal file
12
.sqlx/query-7d4348f04ea5ac82e0f362240fb677740288c963c24b85de11bad011ec5da4bc.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "DELETE FROM addressobjects WHERE addressbook_id = ? AND id = ?",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7d4348f04ea5ac82e0f362240fb677740288c963c24b85de11bad011ec5da4bc"
|
||||
}
|
||||
12
.sqlx/query-7e874304170bef19ceb6f96b3d9803ce6d4553cc2bd57b05d1e546e857f995cf.json
generated
Normal file
12
.sqlx/query-7e874304170bef19ceb6f96b3d9803ce6d4553cc2bd57b05d1e546e857f995cf.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "UPDATE calendarobjects SET deleted_at = datetime(), updated_at = datetime() WHERE (principal, cal_id, id) = (?, ?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7e874304170bef19ceb6f96b3d9803ce6d4553cc2bd57b05d1e546e857f995cf"
|
||||
}
|
||||
56
.sqlx/query-809600b79c012e77c5efabe5d355a8b188f23826a723030807dedc96fd24fdcc.json
generated
Normal file
56
.sqlx/query-809600b79c012e77c5efabe5d355a8b188f23826a723030807dedc96fd24fdcc.json
generated
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT id, topic, expiration, push_resource, public_key, public_key_type, auth_secret\n FROM davpush_subscriptions\n WHERE (topic) = (?)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "topic",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "expiration",
|
||||
"ordinal": 2,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "push_resource",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "public_key",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "public_key_type",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "auth_secret",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "809600b79c012e77c5efabe5d355a8b188f23826a723030807dedc96fd24fdcc"
|
||||
}
|
||||
12
.sqlx/query-879e2717335db3b04884fc91173c8507272f1804b27b6a7f61cbe1fbb01265cd.json
generated
Normal file
12
.sqlx/query-879e2717335db3b04884fc91173c8507272f1804b27b6a7f61cbe1fbb01265cd.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "DELETE FROM memberships WHERE (principal, member_of) = (?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "879e2717335db3b04884fc91173c8507272f1804b27b6a7f61cbe1fbb01265cd"
|
||||
}
|
||||
44
.sqlx/query-95dce97b2e3224c327690c36777e3ece84a9529551696198b745dd8c743c8a38.json
generated
Normal file
44
.sqlx/query-95dce97b2e3224c327690c36777e3ece84a9529551696198b745dd8c743c8a38.json
generated
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT id, displayname, principal_type, password_hash, json_group_array(member_of) AS \"memberships: Json<Vec<Option<String>>>\"\n FROM (SELECT * FROM principals WHERE id = ?) AS principals\n LEFT JOIN memberships ON principals.id == memberships.principal\n GROUP BY principals.id\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "displayname",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "principal_type",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "password_hash",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "memberships: Json<Vec<Option<String>>>",
|
||||
"ordinal": 4,
|
||||
"type_info": "Null"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "95dce97b2e3224c327690c36777e3ece84a9529551696198b745dd8c743c8a38"
|
||||
}
|
||||
12
.sqlx/query-98dba6ddce38d166ef325bbce6055d83fb0092619262c281a271bdc783a0aed9.json
generated
Normal file
12
.sqlx/query-98dba6ddce38d166ef325bbce6055d83fb0092619262c281a271bdc783a0aed9.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "REPLACE INTO addressobjects (principal, addressbook_id, id, vcf) VALUES (?, ?, ?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "98dba6ddce38d166ef325bbce6055d83fb0092619262c281a271bdc783a0aed9"
|
||||
}
|
||||
56
.sqlx/query-9be5d6df7d30a9a85aece59be810bbbb203bab874860aa05eb311259e4baaf05.json
generated
Normal file
56
.sqlx/query-9be5d6df7d30a9a85aece59be810bbbb203bab874860aa05eb311259e4baaf05.json
generated
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT principal, id, synctoken, displayname, description, deleted_at, push_topic\n FROM addressbooks\n WHERE principal = ? AND deleted_at IS NULL",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "principal",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "synctoken",
|
||||
"ordinal": 2,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "displayname",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "deleted_at",
|
||||
"ordinal": 5,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "push_topic",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "9be5d6df7d30a9a85aece59be810bbbb203bab874860aa05eb311259e4baaf05"
|
||||
}
|
||||
12
.sqlx/query-a4371f228f94afd8e6f4ac4b0f7d95b6bf86268b64e714fa7ca587eae9e5df15.json
generated
Normal file
12
.sqlx/query-a4371f228f94afd8e6f4ac4b0f7d95b6bf86268b64e714fa7ca587eae9e5df15.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "UPDATE calendars SET deleted_at = datetime() WHERE (principal, id) = (?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a4371f228f94afd8e6f4ac4b0f7d95b6bf86268b64e714fa7ca587eae9e5df15"
|
||||
}
|
||||
12
.sqlx/query-a8b4258868d238d9c226c44aec8c3c4d90d8e3ca526d4d60b340e868bbfd9ddb.json
generated
Normal file
12
.sqlx/query-a8b4258868d238d9c226c44aec8c3c4d90d8e3ca526d4d60b340e868bbfd9ddb.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "INSERT INTO addressobjects (principal, addressbook_id, id, vcf) VALUES (?, ?, ?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a8b4258868d238d9c226c44aec8c3c4d90d8e3ca526d4d60b340e868bbfd9ddb"
|
||||
}
|
||||
12
.sqlx/query-ac0f9e29ea8079c6900ffb0ebde699309fe8db1ac9f95c58bc9ce051b7d70299.json
generated
Normal file
12
.sqlx/query-ac0f9e29ea8079c6900ffb0ebde699309fe8db1ac9f95c58bc9ce051b7d70299.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "UPDATE addressobjects SET deleted_at = datetime(), updated_at = datetime() WHERE (principal, addressbook_id, id) = (?, ?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ac0f9e29ea8079c6900ffb0ebde699309fe8db1ac9f95c58bc9ce051b7d70299"
|
||||
}
|
||||
20
.sqlx/query-b89be55d6f76591f68520bbdf0ebb6d688b01ee63ae36936692cf1b4f434c7ee.json
generated
Normal file
20
.sqlx/query-b89be55d6f76591f68520bbdf0ebb6d688b01ee63ae36936692cf1b4f434c7ee.json
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n UPDATE addressbooks\n SET synctoken = synctoken + 1\n WHERE (principal, id) = (?1, ?2)\n RETURNING synctoken",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "synctoken",
|
||||
"ordinal": 0,
|
||||
"type_info": "Integer"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "b89be55d6f76591f68520bbdf0ebb6d688b01ee63ae36936692cf1b4f434c7ee"
|
||||
}
|
||||
104
.sqlx/query-bb2fa030f2e7c7afdb38c5c54cb31de5293be332d86cf643977d479999542553.json
generated
Normal file
104
.sqlx/query-bb2fa030f2e7c7afdb38c5c54cb31de5293be332d86cf643977d479999542553.json
generated
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT *\n FROM calendars\n WHERE (principal, id) = (?, ?)\n AND ((deleted_at IS NULL) OR ?) ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "principal",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "synctoken",
|
||||
"ordinal": 2,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "displayname",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "order",
|
||||
"ordinal": 5,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "timezone",
|
||||
"ordinal": 7,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "timezone_id",
|
||||
"ordinal": 8,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "deleted_at",
|
||||
"ordinal": 9,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "subscription_url",
|
||||
"ordinal": 10,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "push_topic",
|
||||
"ordinal": 11,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "comp_event",
|
||||
"ordinal": 12,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "comp_todo",
|
||||
"ordinal": 13,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "comp_journal",
|
||||
"ordinal": 14,
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 3
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "bb2fa030f2e7c7afdb38c5c54cb31de5293be332d86cf643977d479999542553"
|
||||
}
|
||||
26
.sqlx/query-c550dbf3d5ce7069f28d767ea9045e477ef8d29d6186851760757a06dec42339.json
generated
Normal file
26
.sqlx/query-c550dbf3d5ce7069f28d767ea9045e477ef8d29d6186851760757a06dec42339.json
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT id, ics FROM calendarobjects\n WHERE principal = ? AND cal_id = ? AND deleted_at IS NULL\n AND (last_occurence IS NULL OR ? IS NULL OR last_occurence >= date(?))\n AND (first_occurence IS NULL OR ? IS NULL OR first_occurence <= date(?))\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "ics",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 6
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "c550dbf3d5ce7069f28d767ea9045e477ef8d29d6186851760757a06dec42339"
|
||||
}
|
||||
26
.sqlx/query-cbe4be47b2ca1eba485de258f522dec14540a6a9bf383fcde294e8fe14160f22.json
generated
Normal file
26
.sqlx/query-cbe4be47b2ca1eba485de258f522dec14540a6a9bf383fcde294e8fe14160f22.json
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT DISTINCT object_id, max(0, synctoken) as \"synctoken!: i64\" from calendarobjectchangelog\n WHERE synctoken > ?\n ORDER BY synctoken ASC\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "object_id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "synctoken!: i64",
|
||||
"ordinal": 1,
|
||||
"type_info": "Null"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "cbe4be47b2ca1eba485de258f522dec14540a6a9bf383fcde294e8fe14160f22"
|
||||
}
|
||||
104
.sqlx/query-cce62f7829bd688cd8c7928b587bc31f0e50865c214b1df113350bea2c254237.json
generated
Normal file
104
.sqlx/query-cce62f7829bd688cd8c7928b587bc31f0e50865c214b1df113350bea2c254237.json
generated
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT *\n FROM calendars\n WHERE principal = ? AND deleted_at IS NOT NULL",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "principal",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "synctoken",
|
||||
"ordinal": 2,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "displayname",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "order",
|
||||
"ordinal": 5,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "timezone",
|
||||
"ordinal": 7,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "timezone_id",
|
||||
"ordinal": 8,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "deleted_at",
|
||||
"ordinal": 9,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "subscription_url",
|
||||
"ordinal": 10,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "push_topic",
|
||||
"ordinal": 11,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "comp_event",
|
||||
"ordinal": 12,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "comp_todo",
|
||||
"ordinal": 13,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "comp_journal",
|
||||
"ordinal": 14,
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "cce62f7829bd688cd8c7928b587bc31f0e50865c214b1df113350bea2c254237"
|
||||
}
|
||||
104
.sqlx/query-cedfb82b38fdd0c7681b9873b1008abee4a2f4ca16abad1b837f256d0bf416b1.json
generated
Normal file
104
.sqlx/query-cedfb82b38fdd0c7681b9873b1008abee4a2f4ca16abad1b837f256d0bf416b1.json
generated
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT *\n FROM calendars\n WHERE principal = ? AND deleted_at IS NULL",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "principal",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "synctoken",
|
||||
"ordinal": 2,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "displayname",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "order",
|
||||
"ordinal": 5,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "timezone",
|
||||
"ordinal": 7,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "timezone_id",
|
||||
"ordinal": 8,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "deleted_at",
|
||||
"ordinal": 9,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "subscription_url",
|
||||
"ordinal": 10,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "push_topic",
|
||||
"ordinal": 11,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "comp_event",
|
||||
"ordinal": 12,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "comp_todo",
|
||||
"ordinal": 13,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "comp_journal",
|
||||
"ordinal": 14,
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "cedfb82b38fdd0c7681b9873b1008abee4a2f4ca16abad1b837f256d0bf416b1"
|
||||
}
|
||||
12
.sqlx/query-d65c9c40606e59dd816a51b9b9ac60fd2ff81aaa358fcc038134e9a68ba45ad7.json
generated
Normal file
12
.sqlx/query-d65c9c40606e59dd816a51b9b9ac60fd2ff81aaa358fcc038134e9a68ba45ad7.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "UPDATE calendars SET principal = ?, id = ?, displayname = ?, description = ?, \"order\" = ?, color = ?, timezone = ?, timezone_id = ?, push_topic = ?, comp_event = ?, comp_todo = ?, comp_journal = ?\n WHERE (principal, id) = (?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 14
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d65c9c40606e59dd816a51b9b9ac60fd2ff81aaa358fcc038134e9a68ba45ad7"
|
||||
}
|
||||
12
.sqlx/query-e5cf59ade11c09d90899cc9f87754a78a0ca9f7781fd252ebdaf4d2180fca3ba.json
generated
Normal file
12
.sqlx/query-e5cf59ade11c09d90899cc9f87754a78a0ca9f7781fd252ebdaf4d2180fca3ba.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "DELETE FROM calendarobjects WHERE cal_id = ? AND id = ?",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e5cf59ade11c09d90899cc9f87754a78a0ca9f7781fd252ebdaf4d2180fca3ba"
|
||||
}
|
||||
56
.sqlx/query-e5ded4814aae1fc033bb90d27c745f76d3799958d929e8e8d16aa3ceff98e72d.json
generated
Normal file
56
.sqlx/query-e5ded4814aae1fc033bb90d27c745f76d3799958d929e8e8d16aa3ceff98e72d.json
generated
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT principal, id, synctoken, displayname, description, deleted_at, push_topic\n FROM addressbooks\n WHERE principal = ? AND deleted_at IS NOT NULL",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "principal",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "synctoken",
|
||||
"ordinal": 2,
|
||||
"type_info": "Integer"
|
||||
},
|
||||
{
|
||||
"name": "displayname",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "deleted_at",
|
||||
"ordinal": 5,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "push_topic",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "e5ded4814aae1fc033bb90d27c745f76d3799958d929e8e8d16aa3ceff98e72d"
|
||||
}
|
||||
12
.sqlx/query-e912be3352702bbf035b3ee6e9f239bf75a1ffef20211f1b1e895a67a2310960.json
generated
Normal file
12
.sqlx/query-e912be3352702bbf035b3ee6e9f239bf75a1ffef20211f1b1e895a67a2310960.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "DELETE FROM davpush_subscriptions WHERE id = ? ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e912be3352702bbf035b3ee6e9f239bf75a1ffef20211f1b1e895a67a2310960"
|
||||
}
|
||||
12
.sqlx/query-e947709ba03b108765082d1c4cff3dd8cb485fba5819ac914e20cb8e97037da9.json
generated
Normal file
12
.sqlx/query-e947709ba03b108765082d1c4cff3dd8cb485fba5819ac914e20cb8e97037da9.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "REPLACE INTO memberships (principal, member_of) VALUES (?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e947709ba03b108765082d1c4cff3dd8cb485fba5819ac914e20cb8e97037da9"
|
||||
}
|
||||
56
.sqlx/query-ef6cf801df2237b82b55754f1b0a5da51089810fe7a0feb0d68ea801b4e2721c.json
generated
Normal file
56
.sqlx/query-ef6cf801df2237b82b55754f1b0a5da51089810fe7a0feb0d68ea801b4e2721c.json
generated
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "SELECT id, topic, expiration, push_resource, public_key, public_key_type, auth_secret\n FROM davpush_subscriptions\n WHERE (id) = (?)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "topic",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "expiration",
|
||||
"ordinal": 2,
|
||||
"type_info": "Datetime"
|
||||
},
|
||||
{
|
||||
"name": "push_resource",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "public_key",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "public_key_type",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "auth_secret",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 1
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "ef6cf801df2237b82b55754f1b0a5da51089810fe7a0feb0d68ea801b4e2721c"
|
||||
}
|
||||
12
.sqlx/query-fbd776efdbacf6ce039b5d9760b0de181a6f4e066c52bcac4c106118f5435fe7.json
generated
Normal file
12
.sqlx/query-fbd776efdbacf6ce039b5d9760b0de181a6f4e066c52bcac4c106118f5435fe7.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "UPDATE addressbooks SET deleted_at = datetime() WHERE (principal, id) = (?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "fbd776efdbacf6ce039b5d9760b0de181a6f4e066c52bcac4c106118f5435fe7"
|
||||
}
|
||||
12
.sqlx/query-fcc493f5e491abdac2c2d7d5f636c770e449daf8f5ab4873950ab126edfcce7b.json
generated
Normal file
12
.sqlx/query-fcc493f5e491abdac2c2d7d5f636c770e449daf8f5ab4873950ab126edfcce7b.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "UPDATE addressbooks SET deleted_at = NULL WHERE (principal, id) = (?, ?)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "fcc493f5e491abdac2c2d7d5f636c770e449daf8f5ab4873950ab126edfcce7b"
|
||||
}
|
||||
381
Cargo.lock
generated
381
Cargo.lock
generated
@@ -13,9 +13,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
@@ -49,9 +49,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
version = "0.6.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -64,33 +64,33 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.8"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa"
|
||||
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
@@ -162,9 +162,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "askama_web"
|
||||
version = "0.14.3"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a91fdeb04bf77d96234780cdd58fc221eb10de7031e1782a22f40fc8ac1a313"
|
||||
checksum = "83731f1a2286209c2b679445e8faaa53270646a90c509bf92729e966d198cb6b"
|
||||
dependencies = [
|
||||
"askama",
|
||||
"askama_web_derive",
|
||||
@@ -206,9 +206,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "atomic"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994"
|
||||
checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
@@ -221,9 +221,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
@@ -379,15 +379,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.17.0"
|
||||
version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.23.0"
|
||||
version = "1.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
|
||||
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -403,18 +403,18 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.25"
|
||||
version = "1.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951"
|
||||
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
@@ -469,9 +469,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.39"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f"
|
||||
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -479,9 +479,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.39"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51"
|
||||
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -491,9 +491,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.32"
|
||||
version = "4.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
|
||||
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -503,15 +503,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
@@ -759,6 +759,23 @@ dependencies = [
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ece"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2ea1d2f2cc974957a4e2575d8e5bb494549bab66338d6320c2789abcfff5746"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"byteorder",
|
||||
"hex",
|
||||
"hkdf",
|
||||
"lazy_static",
|
||||
"once_cell",
|
||||
"openssl",
|
||||
"sha2",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519"
|
||||
version = "2.2.3"
|
||||
@@ -903,6 +920,21 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
@@ -1036,7 +1068,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@@ -1104,9 +1136,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.3"
|
||||
version = "0.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
@@ -1119,7 +1151,7 @@ version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
|
||||
dependencies = [
|
||||
"hashbrown 0.15.3",
|
||||
"hashbrown 0.15.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1222,6 +1254,12 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range-header"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.10.1"
|
||||
@@ -1257,9 +1295,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.6"
|
||||
version = "0.27.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d"
|
||||
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
|
||||
dependencies = [
|
||||
"http",
|
||||
"hyper",
|
||||
@@ -1287,9 +1325,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.13"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8"
|
||||
checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
@@ -1474,7 +1512,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.3",
|
||||
"hashbrown 0.15.4",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -1551,9 +1589,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.172"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -1616,6 +1654,18 @@ version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
|
||||
|
||||
[[package]]
|
||||
name = "matchit-serde"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/lennart-k/matchit-serde?rev=f0591d13#f0591d139ea1c88fa4ee397f3fcb4225fad4c6dc"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"matchit",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.6"
|
||||
@@ -1628,9 +1678,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
@@ -1650,9 +1700,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.8"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
@@ -1664,7 +1714,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -1803,6 +1853,54 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.5.0+3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry"
|
||||
version = "0.30.0"
|
||||
@@ -2272,9 +2370,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quinn-udp"
|
||||
version = "0.5.12"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842"
|
||||
checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
@@ -2295,9 +2393,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.2.0"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
@@ -2360,13 +2458,33 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.12"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
|
||||
dependencies = [
|
||||
"ref-cast-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast-impl"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
@@ -2419,9 +2537,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.12.19"
|
||||
version = "0.12.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119"
|
||||
checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
@@ -2436,11 +2554,9 @@ dependencies = [
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
@@ -2617,9 +2733,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
@@ -2638,7 +2754,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical"
|
||||
version = "0.1.0"
|
||||
version = "0.4.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -2681,7 +2797,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_caldav"
|
||||
version = "0.1.0"
|
||||
version = "0.4.4"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -2716,7 +2832,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_carddav"
|
||||
version = "0.1.0"
|
||||
version = "0.4.4"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -2748,7 +2864,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_dav"
|
||||
version = "0.1.0"
|
||||
version = "0.4.4"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -2759,6 +2875,8 @@ dependencies = [
|
||||
"http",
|
||||
"itertools 0.14.0",
|
||||
"log",
|
||||
"matchit",
|
||||
"matchit-serde",
|
||||
"quick-xml",
|
||||
"rustical_xml",
|
||||
"serde",
|
||||
@@ -2771,15 +2889,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_dav_push"
|
||||
version = "0.1.0"
|
||||
version = "0.4.4"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
"base64 0.22.1",
|
||||
"derive_more",
|
||||
"ece",
|
||||
"futures-util",
|
||||
"http",
|
||||
"itertools 0.14.0",
|
||||
"log",
|
||||
"openssl",
|
||||
"quick-xml",
|
||||
"rand 0.9.1",
|
||||
"reqwest",
|
||||
"rustical_dav",
|
||||
"rustical_store",
|
||||
@@ -2792,7 +2915,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_frontend"
|
||||
version = "0.1.0"
|
||||
version = "0.4.4"
|
||||
dependencies = [
|
||||
"askama",
|
||||
"askama_web",
|
||||
@@ -2816,6 +2939,7 @@ dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-sessions",
|
||||
"tracing",
|
||||
"url",
|
||||
@@ -2824,7 +2948,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_ical"
|
||||
version = "0.1.0"
|
||||
version = "0.4.4"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"chrono",
|
||||
@@ -2842,7 +2966,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_oidc"
|
||||
version = "0.1.0"
|
||||
version = "0.4.4"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -2857,7 +2981,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_store"
|
||||
version = "0.1.0"
|
||||
version = "0.4.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -2891,7 +3015,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_store_sqlite"
|
||||
version = "0.1.0"
|
||||
version = "0.4.4"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"chrono",
|
||||
@@ -2911,7 +3035,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_xml"
|
||||
version = "0.1.0"
|
||||
version = "0.4.4"
|
||||
dependencies = [
|
||||
"quick-xml",
|
||||
"thiserror 2.0.12",
|
||||
@@ -2920,9 +3044,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.27"
|
||||
version = "0.23.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
|
||||
checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
@@ -2974,6 +3098,18 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"ref-cast",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
@@ -3063,9 +3199,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.8"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -3084,15 +3220,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.12.0"
|
||||
version = "3.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa"
|
||||
checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.9.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@@ -3102,9 +3239,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "3.12.0"
|
||||
version = "3.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e"
|
||||
checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
@@ -3176,18 +3313,15 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.0"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -3251,7 +3385,7 @@ dependencies = [
|
||||
"futures-intrusive",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hashbrown 0.15.3",
|
||||
"hashbrown 0.15.4",
|
||||
"hashlink",
|
||||
"indexmap 2.9.0",
|
||||
"log",
|
||||
@@ -3467,9 +3601,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.101"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3538,12 +3672,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.8"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3668,9 +3801,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.22"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
|
||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
@@ -3680,18 +3813,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.9"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
|
||||
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.26"
|
||||
version = "0.22.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
|
||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
dependencies = [
|
||||
"indexmap 2.9.0",
|
||||
"serde",
|
||||
@@ -3703,9 +3836,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_write"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
|
||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
|
||||
[[package]]
|
||||
name = "tonic"
|
||||
@@ -3776,12 +3909,20 @@ checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"http-range-header",
|
||||
"httpdate",
|
||||
"iri-string",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
@@ -3865,9 +4006,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.28"
|
||||
version = "0.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3876,9 +4017,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.33"
|
||||
version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
@@ -4072,9 +4213,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
@@ -4184,9 +4325,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb"
|
||||
checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
@@ -4269,9 +4410,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
@@ -4441,9 +4582,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.10"
|
||||
version = "0.7.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
|
||||
checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -4506,18 +4647,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.25"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
||||
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.25"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
version = "0.4.4"
|
||||
edition = "2024"
|
||||
description = "A CalDAV server"
|
||||
repository = "https://github.com/lennart-k/rustical"
|
||||
@@ -20,6 +20,7 @@ publish = false
|
||||
|
||||
[features]
|
||||
debug = ["opentelemetry"]
|
||||
frontend-dev = ["rustical_frontend/dev"]
|
||||
opentelemetry = [
|
||||
"dep:opentelemetry",
|
||||
"dep:opentelemetry-otlp",
|
||||
@@ -33,6 +34,7 @@ opentelemetry = [
|
||||
debug = 0
|
||||
|
||||
[workspace.dependencies]
|
||||
matchit = "0.8"
|
||||
uuid = { version = "1.11", features = ["v4", "fast-rng"] }
|
||||
async-trait = "0.1"
|
||||
axum = "0.8"
|
||||
@@ -132,6 +134,11 @@ reqwest = { version = "0.12", features = [
|
||||
], default-features = false }
|
||||
openidconnect = "4.0"
|
||||
clap = { version = "4.5", features = ["derive", "env"] }
|
||||
matchit-serde = { git = "https://github.com/lennart-k/matchit-serde", rev = "f0591d13" }
|
||||
ece = { version = "2.3", default-features = false, features = [
|
||||
"backend-openssl",
|
||||
] }
|
||||
openssl = { version = "0.10", features = ["vendored"] }
|
||||
|
||||
[dependencies]
|
||||
rustical_store = { workspace = true }
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN case $TARGETPLATFORM in \
|
||||
*) echo "Unsupported platform ${TARGETPLATFORM}"; exit 1;; \
|
||||
esac
|
||||
|
||||
RUN apk add --no-cache musl-dev llvm19 clang \
|
||||
RUN apk add --no-cache musl-dev llvm19 clang perl pkgconf make \
|
||||
&& rustup target add "$(cat /tmp/rust_target)" \
|
||||
&& cargo install cargo-chef --locked \
|
||||
&& rm -rf "$CARGO_HOME/registry"
|
||||
|
||||
@@ -4,15 +4,15 @@ a CalDAV/CardDAV server
|
||||
|
||||
> [!WARNING]
|
||||
RustiCal is **not production-ready!**
|
||||
While I've started migrating to RustiCal and becoming more confident,
|
||||
please know that bugs and rough edges will still occur.
|
||||
Concretely, if you are using Apple Calendar you will want to stay away from assigning groups to users.
|
||||
I've been using RustiCal for the last few weeks and I'm slowly becoming more confident,
|
||||
however you'd be one of the first testers so expect bugs and rough edges.
|
||||
If you still want to play around with it in its current state, absolutely feel free to do so and to open up an issue if something is not working. :)
|
||||
|
||||
## Features
|
||||
|
||||
- easy to backup, everything saved in one SQLite database
|
||||
- ~~[WebDAV Push](https://github.com/bitfireAT/webdav-push/) support, so near-instant synchronisation to DAVx5~~ (currently broken)
|
||||
- also export feature in the frontend
|
||||
- [WebDAV Push](https://github.com/bitfireAT/webdav-push/) support, so near-instant synchronisation to DAVx5
|
||||
- lightweight (the container image contains only one binary)
|
||||
- adequately fast (I'd love to say blazingly fast™ :fire: but I don't have any benchmarks)
|
||||
- deleted calendars are recoverable
|
||||
@@ -30,3 +30,4 @@ a CalDAV/CardDAV server
|
||||
- GNOME Accounts, GNOME Calendar, GNOME Contacts
|
||||
- Evolution
|
||||
- Apple Calendar
|
||||
- Home Assistant integration
|
||||
|
||||
@@ -7,5 +7,6 @@ accepted = [
|
||||
"CDLA-Permissive-2.0",
|
||||
"Zlib",
|
||||
"AGPL-3.0",
|
||||
"MPL-2.0",
|
||||
]
|
||||
workarounds = ["ring", "chrono", "rustls"]
|
||||
|
||||
@@ -9,7 +9,7 @@ use ical::generator::{Emitter, IcalCalendarBuilder};
|
||||
use ical::property::Property;
|
||||
use percent_encoding::{CONTROLS, utf8_percent_encode};
|
||||
use rustical_ical::{CalendarObjectComponent, EventObject, JournalObject, TodoObject};
|
||||
use rustical_store::{CalendarStore, SubscriptionStore, auth::User};
|
||||
use rustical_store::{CalendarStore, SubscriptionStore, auth::Principal};
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use tracing::instrument;
|
||||
@@ -18,18 +18,22 @@ use tracing::instrument;
|
||||
pub async fn route_get<C: CalendarStore, S: SubscriptionStore>(
|
||||
Path((principal, calendar_id)): Path<(String, String)>,
|
||||
State(CalendarResourceService { cal_store, .. }): State<CalendarResourceService<C, S>>,
|
||||
user: User,
|
||||
user: Principal,
|
||||
) -> Result<Response, Error> {
|
||||
if !user.is_principal(&principal) {
|
||||
return Err(crate::Error::Unauthorized);
|
||||
}
|
||||
|
||||
let calendar = cal_store.get_calendar(&principal, &calendar_id).await?;
|
||||
let calendar = cal_store
|
||||
.get_calendar(&principal, &calendar_id, true)
|
||||
.await?;
|
||||
if !user.is_principal(&calendar.principal) {
|
||||
return Err(crate::Error::Unauthorized);
|
||||
}
|
||||
|
||||
let calendar = cal_store.get_calendar(&principal, &calendar_id).await?;
|
||||
let calendar = cal_store
|
||||
.get_calendar(&principal, &calendar_id, true)
|
||||
.await?;
|
||||
|
||||
let mut timezones = HashMap::new();
|
||||
let objects = cal_store.get_objects(&principal, &calendar_id).await?;
|
||||
|
||||
@@ -4,8 +4,9 @@ use crate::calendar::prop::SupportedCalendarComponentSet;
|
||||
use axum::extract::{Path, State};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use http::{Method, StatusCode};
|
||||
use rustical_dav::xml::HrefElement;
|
||||
use rustical_ical::CalendarObjectType;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
|
||||
use rustical_xml::{Unparsed, XmlDeserialize, XmlDocument, XmlRootTag};
|
||||
use tracing::instrument;
|
||||
@@ -29,6 +30,8 @@ pub struct MkcolCalendarProp {
|
||||
resourcetype: Option<Unparsed>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
supported_calendar_component_set: Option<SupportedCalendarComponentSet>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALENDARSERVER")]
|
||||
source: Option<HrefElement>,
|
||||
// Ignore that property, we don't support it but also don't want to throw an error
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
#[allow(dead_code)]
|
||||
@@ -60,7 +63,7 @@ struct MkcolRequest {
|
||||
#[instrument(skip(cal_store))]
|
||||
pub async fn route_mkcalendar<C: CalendarStore, S: SubscriptionStore>(
|
||||
Path((principal, cal_id)): Path<(String, String)>,
|
||||
user: User,
|
||||
user: Principal,
|
||||
State(CalendarResourceService { cal_store, .. }): State<CalendarResourceService<C, S>>,
|
||||
method: Method,
|
||||
body: String,
|
||||
@@ -69,12 +72,16 @@ pub async fn route_mkcalendar<C: CalendarStore, S: SubscriptionStore>(
|
||||
return Err(Error::Unauthorized);
|
||||
}
|
||||
|
||||
let request = match method.as_str() {
|
||||
let mut request = match method.as_str() {
|
||||
"MKCALENDAR" => MkcalendarRequest::parse_str(&body)?.set.prop,
|
||||
"MKCOL" => MkcolRequest::parse_str(&body)?.set.prop,
|
||||
_ => unreachable!("We never call with another method"),
|
||||
};
|
||||
|
||||
if let Some("") = request.displayname.as_deref() {
|
||||
request.displayname = None
|
||||
}
|
||||
|
||||
let calendar = Calendar {
|
||||
id: cal_id.to_owned(),
|
||||
principal: principal.to_owned(),
|
||||
@@ -86,7 +93,7 @@ pub async fn route_mkcalendar<C: CalendarStore, S: SubscriptionStore>(
|
||||
description: request.calendar_description,
|
||||
deleted_at: None,
|
||||
synctoken: 0,
|
||||
subscription_url: None,
|
||||
subscription_url: request.source.map(|href| href.href),
|
||||
push_topic: uuid::Uuid::new_v4().to_string(),
|
||||
components: request
|
||||
.supported_calendar_component_set
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub mod mkcalendar;
|
||||
// pub mod post;
|
||||
pub mod get;
|
||||
pub mod mkcalendar;
|
||||
pub mod post;
|
||||
pub mod report;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use crate::Error;
|
||||
use crate::calendar::resource::{CalendarResource, CalendarResourceService};
|
||||
use crate::calendar::CalendarResourceService;
|
||||
use crate::calendar::resource::CalendarResource;
|
||||
use axum::extract::{Path, State};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use http::{HeaderMap, StatusCode, header};
|
||||
use http::{HeaderMap, HeaderValue, StatusCode, header};
|
||||
use rustical_dav::privileges::UserPrivilege;
|
||||
use rustical_dav::resource::Resource;
|
||||
use rustical_dav_push::register::PushRegister;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
use rustical_store::{CalendarStore, Subscription, SubscriptionStore};
|
||||
use rustical_xml::XmlDocument;
|
||||
use tracing::instrument;
|
||||
@@ -14,7 +15,7 @@ use tracing::instrument;
|
||||
#[instrument(skip(resource_service))]
|
||||
pub async fn route_post<C: CalendarStore, S: SubscriptionStore>(
|
||||
Path((principal, cal_id)): Path<(String, String)>,
|
||||
user: User,
|
||||
user: Principal,
|
||||
State(resource_service): State<CalendarResourceService<C, S>>,
|
||||
body: String,
|
||||
) -> Result<Response, Error> {
|
||||
@@ -24,7 +25,7 @@ pub async fn route_post<C: CalendarStore, S: SubscriptionStore>(
|
||||
|
||||
let calendar = resource_service
|
||||
.cal_store
|
||||
.get_calendar(&principal, &cal_id)
|
||||
.get_calendar(&principal, &cal_id, false)
|
||||
.await?;
|
||||
let calendar_resource = CalendarResource {
|
||||
cal: calendar,
|
||||
@@ -73,20 +74,17 @@ pub async fn route_post<C: CalendarStore, S: SubscriptionStore>(
|
||||
.upsert_subscription(subscription)
|
||||
.await?;
|
||||
|
||||
// let location = req
|
||||
// .resource_map()
|
||||
// .url_for(&req, "subscription", &[sub_id])
|
||||
// .unwrap();
|
||||
//
|
||||
let location = "asd";
|
||||
// TODO: make nicer
|
||||
let location = format!("/push_subscription/{sub_id}");
|
||||
Ok((
|
||||
StatusCode::CREATED,
|
||||
HeaderMap::from_iter([(header::LOCATION, location)]),
|
||||
HeaderMap::from_iter([
|
||||
(header::LOCATION, HeaderValue::from_str(&location).unwrap()),
|
||||
(
|
||||
header::EXPIRES,
|
||||
HeaderValue::from_str(&expires.to_rfc2822()).unwrap(),
|
||||
),
|
||||
]),
|
||||
)
|
||||
.into_response());
|
||||
|
||||
Ok(HttpResponse::Created()
|
||||
.append_header((header::LOCATION, location.to_string()))
|
||||
.append_header((header::EXPIRES, expires.to_rfc2822()))
|
||||
.finish())
|
||||
.into_response())
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ pub async fn get_objects_calendar_multiget<C: CalendarStore>(
|
||||
if let Some(filename) = href.strip_prefix(path) {
|
||||
let filename = filename.trim_start_matches("/");
|
||||
if let Some(object_id) = filename.strip_suffix(".ics") {
|
||||
match store.get_object(principal, cal_id, object_id).await {
|
||||
match store.get_object(principal, cal_id, object_id, false).await {
|
||||
Ok(object) => result.push(object),
|
||||
Err(rustical_store::Error::NotFound) => not_found.push(href.to_owned()),
|
||||
Err(err) => return Err(err.into()),
|
||||
|
||||
@@ -21,7 +21,7 @@ use rustical_dav::{
|
||||
},
|
||||
};
|
||||
use rustical_ical::CalendarObject;
|
||||
use rustical_store::{CalendarStore, SubscriptionStore, auth::User};
|
||||
use rustical_store::{CalendarStore, SubscriptionStore, auth::Principal};
|
||||
use rustical_xml::{XmlDeserialize, XmlDocument};
|
||||
use sync_collection::handle_sync_collection;
|
||||
use tracing::instrument;
|
||||
@@ -56,7 +56,7 @@ fn objects_response(
|
||||
path: &str,
|
||||
principal: &str,
|
||||
puri: &impl PrincipalUri,
|
||||
user: &User,
|
||||
user: &Principal,
|
||||
prop: &PropfindType<CalendarObjectPropWrapperName>,
|
||||
) -> Result<MultistatusElement<CalendarObjectPropWrapper, String>, Error> {
|
||||
let mut responses = Vec::new();
|
||||
@@ -90,7 +90,7 @@ fn objects_response(
|
||||
#[instrument(skip(cal_store))]
|
||||
pub async fn route_report_calendar<C: CalendarStore, S: SubscriptionStore>(
|
||||
Path((principal, cal_id)): Path<(String, String)>,
|
||||
user: User,
|
||||
user: Principal,
|
||||
Extension(puri): Extension<CalDavPrincipalUri>,
|
||||
State(CalendarResourceService { cal_store, .. }): State<CalendarResourceService<C, S>>,
|
||||
OriginalUri(uri): OriginalUri,
|
||||
|
||||
@@ -13,7 +13,7 @@ use rustical_dav::{
|
||||
};
|
||||
use rustical_store::{
|
||||
CalendarStore,
|
||||
auth::User,
|
||||
auth::Principal,
|
||||
synctoken::{format_synctoken, parse_synctoken},
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ pub async fn handle_sync_collection<C: CalendarStore>(
|
||||
sync_collection: &SyncCollectionRequest<CalendarObjectPropWrapperName>,
|
||||
path: &str,
|
||||
puri: &impl PrincipalUri,
|
||||
user: &User,
|
||||
user: &Principal,
|
||||
principal: &str,
|
||||
cal_id: &str,
|
||||
cal_store: &C,
|
||||
|
||||
@@ -9,10 +9,10 @@ use rustical_dav::extensions::{
|
||||
use rustical_dav::privileges::UserPrivilegeSet;
|
||||
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner, SupportedReportSet};
|
||||
use rustical_dav_push::DavPushExtension;
|
||||
use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
|
||||
use rustical_ical::CalDateTime;
|
||||
use rustical_store::Calendar;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
use rustical_xml::{EnumVariants, PropName};
|
||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||
use std::str::FromStr;
|
||||
@@ -58,7 +58,7 @@ pub enum CalendarProp {
|
||||
pub enum CalendarPropWrapper {
|
||||
Calendar(CalendarProp),
|
||||
SyncToken(SyncTokenExtensionProp),
|
||||
// DavPush(DavPushExtensionProp),
|
||||
DavPush(DavPushExtensionProp),
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
|
||||
@@ -95,9 +95,11 @@ impl DavPushExtension for CalendarResource {
|
||||
impl Resource for CalendarResource {
|
||||
type Prop = CalendarPropWrapper;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type Principal = Principal;
|
||||
|
||||
const IS_COLLECTION: bool = true;
|
||||
fn is_collection(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
if self.cal.subscription_url.is_none() {
|
||||
@@ -119,7 +121,7 @@ impl Resource for CalendarResource {
|
||||
fn get_prop(
|
||||
&self,
|
||||
puri: &impl PrincipalUri,
|
||||
user: &User,
|
||||
user: &Principal,
|
||||
prop: &CalendarPropWrapperName,
|
||||
) -> Result<Self::Prop, Self::Error> {
|
||||
Ok(match prop {
|
||||
@@ -166,9 +168,9 @@ impl Resource for CalendarResource {
|
||||
CalendarPropWrapperName::SyncToken(prop) => {
|
||||
CalendarPropWrapper::SyncToken(SyncTokenExtension::get_prop(self, prop)?)
|
||||
}
|
||||
// CalendarPropWrapperName::DavPush(prop) => {
|
||||
// CalendarPropWrapper::DavPush(DavPushExtension::get_prop(self, prop)?)
|
||||
// }
|
||||
CalendarPropWrapperName::DavPush(prop) => {
|
||||
CalendarPropWrapper::DavPush(DavPushExtension::get_prop(self, prop)?)
|
||||
}
|
||||
CalendarPropWrapperName::Common(prop) => CalendarPropWrapper::Common(
|
||||
CommonPropertiesExtension::get_prop(self, puri, user, prop)?,
|
||||
),
|
||||
@@ -226,7 +228,7 @@ impl Resource for CalendarResource {
|
||||
CalendarProp::MaxDateTime(_) => Err(rustical_dav::Error::PropReadOnly),
|
||||
},
|
||||
CalendarPropWrapper::SyncToken(prop) => SyncTokenExtension::set_prop(self, prop),
|
||||
// CalendarPropWrapper::DavPush(prop) => DavPushExtension::set_prop(self, prop),
|
||||
CalendarPropWrapper::DavPush(prop) => DavPushExtension::set_prop(self, prop),
|
||||
CalendarPropWrapper::Common(prop) => CommonPropertiesExtension::set_prop(self, prop),
|
||||
}
|
||||
}
|
||||
@@ -270,7 +272,7 @@ impl Resource for CalendarResource {
|
||||
CalendarPropName::MaxDateTime => Err(rustical_dav::Error::PropReadOnly),
|
||||
},
|
||||
CalendarPropWrapperName::SyncToken(prop) => SyncTokenExtension::remove_prop(self, prop),
|
||||
// CalendarPropWrapperName::DavPush(prop) => DavPushExtension::remove_prop(self, prop),
|
||||
CalendarPropWrapperName::DavPush(prop) => DavPushExtension::remove_prop(self, prop),
|
||||
CalendarPropWrapperName::Common(prop) => {
|
||||
CommonPropertiesExtension::remove_prop(self, prop)
|
||||
}
|
||||
@@ -289,8 +291,13 @@ impl Resource for CalendarResource {
|
||||
Some(&self.cal.principal)
|
||||
}
|
||||
|
||||
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
||||
if self.cal.subscription_url.is_some() || self.read_only {
|
||||
fn get_user_privileges(&self, user: &Principal) -> Result<UserPrivilegeSet, Self::Error> {
|
||||
if self.cal.subscription_url.is_some() {
|
||||
return Ok(UserPrivilegeSet::owner_write_properties(
|
||||
user.is_principal(&self.cal.principal),
|
||||
));
|
||||
}
|
||||
if self.read_only {
|
||||
return Ok(UserPrivilegeSet::owner_read(
|
||||
user.is_principal(&self.cal.principal),
|
||||
));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::calendar::methods::get::route_get;
|
||||
use crate::calendar::methods::mkcalendar::route_mkcalendar;
|
||||
use crate::calendar::methods::post::route_post;
|
||||
use crate::calendar::methods::report::route_report_calendar;
|
||||
use crate::calendar::resource::CalendarResource;
|
||||
use crate::calendar_object::CalendarObjectResourceService;
|
||||
@@ -12,7 +13,7 @@ use axum::handler::Handler;
|
||||
use axum::response::Response;
|
||||
use futures_util::future::BoxFuture;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||
use std::convert::Infallible;
|
||||
use std::sync::Arc;
|
||||
@@ -47,16 +48,20 @@ impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarResourc
|
||||
type PathComponents = (String, String); // principal, calendar_id
|
||||
type Resource = CalendarResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type Principal = Principal;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access, calendar-proxy";
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access, calendar-proxy, webdav-push";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal, cal_id): &Self::PathComponents,
|
||||
show_deleted: bool,
|
||||
) -> Result<Self::Resource, Error> {
|
||||
let calendar = self.cal_store.get_calendar(principal, cal_id).await?;
|
||||
let calendar = self
|
||||
.cal_store
|
||||
.get_calendar(principal, cal_id, show_deleted)
|
||||
.await?;
|
||||
Ok(CalendarResource {
|
||||
cal: calendar,
|
||||
read_only: self.cal_store.is_read_only(cal_id),
|
||||
@@ -126,6 +131,13 @@ impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarResourceSer
|
||||
})
|
||||
}
|
||||
|
||||
fn post() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(route_post::<C, S>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
|
||||
fn mkcalendar() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>>
|
||||
{
|
||||
Some(|state, req| {
|
||||
|
||||
@@ -9,7 +9,7 @@ use headers::{ContentType, ETag, HeaderMapExt, IfNoneMatch};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use rustical_ical::CalendarObject;
|
||||
use rustical_store::CalendarStore;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
use std::str::FromStr;
|
||||
use tracing::instrument;
|
||||
|
||||
@@ -21,19 +21,21 @@ pub async fn get_event<C: CalendarStore>(
|
||||
object_id,
|
||||
}): Path<CalendarObjectPathComponents>,
|
||||
State(CalendarObjectResourceService { cal_store }): State<CalendarObjectResourceService<C>>,
|
||||
user: User,
|
||||
user: Principal,
|
||||
) -> Result<Response, Error> {
|
||||
if !user.is_principal(&principal) {
|
||||
return Err(crate::Error::Unauthorized);
|
||||
}
|
||||
|
||||
let calendar = cal_store.get_calendar(&principal, &calendar_id).await?;
|
||||
let calendar = cal_store
|
||||
.get_calendar(&principal, &calendar_id, false)
|
||||
.await?;
|
||||
if !user.is_principal(&calendar.principal) {
|
||||
return Err(crate::Error::Unauthorized);
|
||||
}
|
||||
|
||||
let event = cal_store
|
||||
.get_object(&principal, &calendar_id, &object_id)
|
||||
.get_object(&principal, &calendar_id, &object_id, false)
|
||||
.await?;
|
||||
|
||||
let mut resp = Response::builder().status(StatusCode::OK);
|
||||
@@ -51,7 +53,7 @@ pub async fn put_event<C: CalendarStore>(
|
||||
object_id,
|
||||
}): Path<CalendarObjectPathComponents>,
|
||||
State(CalendarObjectResourceService { cal_store }): State<CalendarObjectResourceService<C>>,
|
||||
user: User,
|
||||
user: Principal,
|
||||
mut if_none_match: Option<TypedHeader<IfNoneMatch>>,
|
||||
header_map: HeaderMap,
|
||||
body: String,
|
||||
|
||||
@@ -8,7 +8,7 @@ use rustical_dav::{
|
||||
xml::Resourcetype,
|
||||
};
|
||||
use rustical_ical::CalendarObject;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
|
||||
#[derive(Clone, From, Into)]
|
||||
pub struct CalendarObjectResource {
|
||||
@@ -25,9 +25,11 @@ impl ResourceName for CalendarObjectResource {
|
||||
impl Resource for CalendarObjectResource {
|
||||
type Prop = CalendarObjectPropWrapper;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type Principal = Principal;
|
||||
|
||||
const IS_COLLECTION: bool = false;
|
||||
fn is_collection(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
Resourcetype(&[])
|
||||
@@ -36,7 +38,7 @@ impl Resource for CalendarObjectResource {
|
||||
fn get_prop(
|
||||
&self,
|
||||
puri: &impl PrincipalUri,
|
||||
user: &User,
|
||||
user: &Principal,
|
||||
prop: &CalendarObjectPropWrapperName,
|
||||
) -> Result<Self::Prop, Self::Error> {
|
||||
Ok(match prop {
|
||||
@@ -79,7 +81,7 @@ impl Resource for CalendarObjectResource {
|
||||
Some(self.object.get_etag())
|
||||
}
|
||||
|
||||
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
||||
fn get_user_privileges(&self, user: &Principal) -> Result<UserPrivilegeSet, Self::Error> {
|
||||
Ok(UserPrivilegeSet::owner_only(
|
||||
user.is_principal(&self.principal),
|
||||
))
|
||||
|
||||
@@ -9,7 +9,7 @@ use async_trait::async_trait;
|
||||
use axum::{extract::Request, handler::Handler, response::Response};
|
||||
use futures_util::future::BoxFuture;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::{CalendarStore, auth::User};
|
||||
use rustical_store::{CalendarStore, auth::Principal};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
use tower::Service;
|
||||
@@ -46,7 +46,7 @@ impl<C: CalendarStore> ResourceService for CalendarObjectResourceService<C> {
|
||||
type Resource = CalendarObjectResource;
|
||||
type MemberType = CalendarObjectResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type Principal = Principal;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||
@@ -58,10 +58,11 @@ impl<C: CalendarStore> ResourceService for CalendarObjectResourceService<C> {
|
||||
calendar_id,
|
||||
object_id,
|
||||
}: &Self::PathComponents,
|
||||
show_deleted: bool,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
let object = self
|
||||
.cal_store
|
||||
.get_object(principal, calendar_id, object_id)
|
||||
.get_object(principal, calendar_id, object_id, show_deleted)
|
||||
.await?;
|
||||
Ok(CalendarObjectResource {
|
||||
object,
|
||||
|
||||
@@ -6,7 +6,7 @@ use principal::PrincipalResourceService;
|
||||
use rustical_dav::resource::{PrincipalUri, ResourceService};
|
||||
use rustical_dav::resources::RootResourceService;
|
||||
use rustical_store::auth::middleware::AuthenticationLayer;
|
||||
use rustical_store::auth::{AuthenticationProvider, User};
|
||||
use rustical_store::auth::{AuthenticationProvider, Principal};
|
||||
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -14,7 +14,6 @@ pub mod calendar;
|
||||
pub mod calendar_object;
|
||||
pub mod error;
|
||||
pub mod principal;
|
||||
// mod subscription;
|
||||
|
||||
pub use error::Error;
|
||||
|
||||
@@ -45,7 +44,7 @@ pub fn caldav_router<AP: AuthenticationProvider, C: CalendarStore, S: Subscripti
|
||||
Router::new()
|
||||
.nest(
|
||||
prefix,
|
||||
RootResourceService::<_, User, CalDavPrincipalUri>::new(principal_service.clone())
|
||||
RootResourceService::<_, Principal, CalDavPrincipalUri>::new(principal_service.clone())
|
||||
.axum_router()
|
||||
.layer(AuthenticationLayer::new(auth_provider))
|
||||
.layer(Extension(CalDavPrincipalUri(prefix))),
|
||||
|
||||
@@ -5,7 +5,7 @@ use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||
use rustical_dav::xml::{
|
||||
GroupMemberSet, GroupMembership, Resourcetype, ResourcetypeInner, SupportedReportSet,
|
||||
};
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
|
||||
mod service;
|
||||
pub use service::*;
|
||||
@@ -14,7 +14,7 @@ pub use prop::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PrincipalResource {
|
||||
principal: User,
|
||||
principal: Principal,
|
||||
members: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -27,26 +27,28 @@ impl ResourceName for PrincipalResource {
|
||||
impl Resource for PrincipalResource {
|
||||
type Prop = PrincipalPropWrapper;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type Principal = Principal;
|
||||
|
||||
const IS_COLLECTION: bool = true;
|
||||
fn is_collection(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
Resourcetype(&[
|
||||
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
||||
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "principal"),
|
||||
// https://github.com/apple/ccs-calendarserver/blob/13c706b985fb728b9aab42dc0fef85aae21921c3/doc/Extensions/caldav-proxy.txt
|
||||
ResourcetypeInner(
|
||||
Some(rustical_dav::namespace::NS_CALENDARSERVER),
|
||||
"calendar-proxy-write",
|
||||
),
|
||||
// ResourcetypeInner(
|
||||
// Some(rustical_dav::namespace::NS_CALENDARSERVER),
|
||||
// "calendar-proxy-write",
|
||||
// ),
|
||||
])
|
||||
}
|
||||
|
||||
fn get_prop(
|
||||
&self,
|
||||
puri: &impl PrincipalUri,
|
||||
user: &User,
|
||||
user: &Principal,
|
||||
prop: &PrincipalPropWrapperName,
|
||||
) -> Result<Self::Prop, Self::Error> {
|
||||
let principal_url = puri.principal_uri(&self.principal.id);
|
||||
@@ -111,7 +113,7 @@ impl Resource for PrincipalResource {
|
||||
Some(&self.principal.id)
|
||||
}
|
||||
|
||||
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
||||
fn get_user_privileges(&self, user: &Principal) -> Result<UserPrivilegeSet, Self::Error> {
|
||||
Ok(UserPrivilegeSet::owner_read(
|
||||
user.is_principal(&self.principal.id),
|
||||
))
|
||||
|
||||
@@ -2,7 +2,7 @@ use rustical_dav::{
|
||||
extensions::CommonPropertiesProp,
|
||||
xml::{GroupMemberSet, GroupMembership, HrefElement, SupportedReportSet},
|
||||
};
|
||||
use rustical_store::auth::user::PrincipalType;
|
||||
use rustical_store::auth::PrincipalType;
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
use strum_macros::VariantArray;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{CalDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::auth::{AuthenticationProvider, User};
|
||||
use rustical_store::auth::{AuthenticationProvider, Principal};
|
||||
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -40,7 +40,7 @@ impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore> Resour
|
||||
type MemberType = CalendarResource;
|
||||
type Resource = PrincipalResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type Principal = Principal;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access, calendar-proxy";
|
||||
@@ -48,6 +48,7 @@ impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore> Resour
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
_show_deleted: bool,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
let user = self
|
||||
.auth_provider
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::{
|
||||
HttpResponse,
|
||||
web::{self, Data, Path},
|
||||
};
|
||||
use rustical_dav::xml::multistatus::PropstatElement;
|
||||
use rustical_store::SubscriptionStore;
|
||||
use rustical_xml::{XmlRootTag, XmlSerialize};
|
||||
|
||||
use crate::calendar::resource::CalendarProp;
|
||||
|
||||
async fn handle_delete<S: SubscriptionStore>(
|
||||
store: Data<S>,
|
||||
path: Path<String>,
|
||||
) -> Result<HttpResponse, rustical_store::Error> {
|
||||
let id = path.into_inner();
|
||||
store.delete_subscription(&id).await?;
|
||||
Ok(HttpResponse::NoContent().body("Unregistered"))
|
||||
}
|
||||
|
||||
pub fn subscription_resource<S: SubscriptionStore>(sub_store: Arc<S>) -> actix_web::Resource {
|
||||
web::resource("/subscription/{id}")
|
||||
.app_data(Data::from(sub_store))
|
||||
.name("subscription")
|
||||
.delete(handle_delete::<S>)
|
||||
}
|
||||
|
||||
#[derive(XmlSerialize, XmlRootTag)]
|
||||
#[xml(root = b"push-message", ns = "rustical_dav::namespace::NS_DAVPUSH")]
|
||||
pub struct PushMessage {
|
||||
propstat: PropstatElement<CalendarProp>,
|
||||
}
|
||||
@@ -12,7 +12,7 @@ use rustical_dav::privileges::UserPrivilege;
|
||||
use rustical_dav::resource::Resource;
|
||||
use rustical_ical::AddressObject;
|
||||
use rustical_store::AddressbookStore;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
use std::str::FromStr;
|
||||
use tracing::instrument;
|
||||
|
||||
@@ -24,7 +24,7 @@ pub async fn get_object<AS: AddressbookStore>(
|
||||
object_id,
|
||||
}): Path<AddressObjectPathComponents>,
|
||||
State(AddressObjectResourceService { addr_store }): State<AddressObjectResourceService<AS>>,
|
||||
user: User,
|
||||
user: Principal,
|
||||
) -> Result<Response, Error> {
|
||||
if !user.is_principal(&principal) {
|
||||
return Err(Error::Unauthorized);
|
||||
@@ -60,7 +60,7 @@ pub async fn put_object<AS: AddressbookStore>(
|
||||
object_id,
|
||||
}): Path<AddressObjectPathComponents>,
|
||||
State(AddressObjectResourceService { addr_store }): State<AddressObjectResourceService<AS>>,
|
||||
user: User,
|
||||
user: Principal,
|
||||
mut if_none_match: Option<TypedHeader<IfNoneMatch>>,
|
||||
header_map: HeaderMap,
|
||||
body: String,
|
||||
|
||||
@@ -13,7 +13,7 @@ use rustical_dav::{
|
||||
xml::Resourcetype,
|
||||
};
|
||||
use rustical_ical::AddressObject;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
|
||||
#[derive(Clone, From, Into)]
|
||||
pub struct AddressObjectResource {
|
||||
@@ -30,9 +30,11 @@ impl ResourceName for AddressObjectResource {
|
||||
impl Resource for AddressObjectResource {
|
||||
type Prop = AddressObjectPropWrapper;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type Principal = Principal;
|
||||
|
||||
const IS_COLLECTION: bool = false;
|
||||
fn is_collection(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
Resourcetype(&[])
|
||||
@@ -41,7 +43,7 @@ impl Resource for AddressObjectResource {
|
||||
fn get_prop(
|
||||
&self,
|
||||
puri: &impl PrincipalUri,
|
||||
user: &User,
|
||||
user: &Principal,
|
||||
prop: &AddressObjectPropWrapperName,
|
||||
) -> Result<Self::Prop, Self::Error> {
|
||||
Ok(match prop {
|
||||
@@ -76,7 +78,7 @@ impl Resource for AddressObjectResource {
|
||||
Some(self.object.get_etag())
|
||||
}
|
||||
|
||||
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
||||
fn get_user_privileges(&self, user: &Principal) -> Result<UserPrivilegeSet, Self::Error> {
|
||||
Ok(UserPrivilegeSet::owner_only(
|
||||
user.is_principal(&self.principal),
|
||||
))
|
||||
|
||||
@@ -5,7 +5,7 @@ use axum::{extract::Request, handler::Handler, response::Response};
|
||||
use derive_more::derive::Constructor;
|
||||
use futures_util::future::BoxFuture;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::{AddressbookStore, auth::User};
|
||||
use rustical_store::{AddressbookStore, auth::Principal};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
use tower::Service;
|
||||
@@ -37,7 +37,7 @@ impl<AS: AddressbookStore> ResourceService for AddressObjectResourceService<AS>
|
||||
type Resource = AddressObjectResource;
|
||||
type MemberType = AddressObjectResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type Principal = Principal;
|
||||
type PrincipalUri = CardDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||
@@ -49,10 +49,11 @@ impl<AS: AddressbookStore> ResourceService for AddressObjectResourceService<AS>
|
||||
addressbook_id,
|
||||
object_id,
|
||||
}: &Self::PathComponents,
|
||||
show_deleted: bool,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
let object = self
|
||||
.addr_store
|
||||
.get_object(principal, addressbook_id, object_id, false)
|
||||
.get_object(principal, addressbook_id, object_id, show_deleted)
|
||||
.await?;
|
||||
Ok(AddressObjectResource {
|
||||
object,
|
||||
|
||||
@@ -10,7 +10,7 @@ use percent_encoding::{CONTROLS, utf8_percent_encode};
|
||||
use rustical_dav::privileges::UserPrivilege;
|
||||
use rustical_dav::resource::Resource;
|
||||
use rustical_ical::AddressObject;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
use rustical_store::{AddressbookStore, SubscriptionStore};
|
||||
use std::str::FromStr;
|
||||
use tracing::instrument;
|
||||
@@ -19,7 +19,7 @@ use tracing::instrument;
|
||||
pub async fn route_get<AS: AddressbookStore, S: SubscriptionStore>(
|
||||
Path((principal, addressbook_id)): Path<(String, String)>,
|
||||
State(AddressbookResourceService { addr_store, .. }): State<AddressbookResourceService<AS, S>>,
|
||||
user: User,
|
||||
user: Principal,
|
||||
) -> Result<Response, Error> {
|
||||
if !user.is_principal(&principal) {
|
||||
return Err(Error::Unauthorized);
|
||||
|
||||
@@ -4,7 +4,7 @@ use axum::{
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::StatusCode;
|
||||
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore, auth::User};
|
||||
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore, auth::Principal};
|
||||
use rustical_xml::{XmlDeserialize, XmlDocument, XmlRootTag};
|
||||
use tracing::instrument;
|
||||
|
||||
@@ -44,7 +44,7 @@ struct MkcolRequest {
|
||||
#[instrument(skip(addr_store))]
|
||||
pub async fn route_mkcol<AS: AddressbookStore, S: SubscriptionStore>(
|
||||
Path((principal, addressbook_id)): Path<(String, String)>,
|
||||
user: User,
|
||||
user: Principal,
|
||||
State(AddressbookResourceService { addr_store, .. }): State<AddressbookResourceService<AS, S>>,
|
||||
body: String,
|
||||
) -> Result<Response, Error> {
|
||||
@@ -52,8 +52,10 @@ pub async fn route_mkcol<AS: AddressbookStore, S: SubscriptionStore>(
|
||||
return Err(Error::Unauthorized);
|
||||
}
|
||||
|
||||
let request = MkcolRequest::parse_str(&body)?;
|
||||
let request = request.set.prop;
|
||||
let mut request = MkcolRequest::parse_str(&body)?.set.prop;
|
||||
if let Some("") = request.displayname.as_deref() {
|
||||
request.displayname = None
|
||||
}
|
||||
|
||||
let addressbook = Addressbook {
|
||||
id: addressbook_id.to_owned(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pub mod mkcol;
|
||||
// pub mod post;
|
||||
pub mod get;
|
||||
pub mod mkcol;
|
||||
pub mod post;
|
||||
pub mod put;
|
||||
pub mod report;
|
||||
|
||||
@@ -1,33 +1,40 @@
|
||||
use crate::Error;
|
||||
use crate::addressbook::resource::AddressbookResourceService;
|
||||
use actix_web::http::header;
|
||||
use actix_web::web::{Data, Path};
|
||||
use actix_web::{HttpRequest, HttpResponse};
|
||||
use crate::addressbook::AddressbookResourceService;
|
||||
use crate::addressbook::resource::AddressbookResource;
|
||||
use axum::extract::{Path, State};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use http::{HeaderMap, HeaderValue, StatusCode, header};
|
||||
use rustical_dav::privileges::UserPrivilege;
|
||||
use rustical_dav::resource::Resource;
|
||||
use rustical_dav_push::register::PushRegister;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
use rustical_store::{AddressbookStore, Subscription, SubscriptionStore};
|
||||
use rustical_xml::XmlDocument;
|
||||
use tracing::instrument;
|
||||
use tracing_actix_web::RootSpan;
|
||||
|
||||
#[instrument(parent = root_span.id(), skip(resource_service, root_span, req))]
|
||||
pub async fn route_post<A: AddressbookStore, S: SubscriptionStore>(
|
||||
path: Path<(String, String)>,
|
||||
#[instrument(skip(resource_service))]
|
||||
pub async fn route_post<AS: AddressbookStore, S: SubscriptionStore>(
|
||||
Path((principal, addr_id)): Path<(String, String)>,
|
||||
user: Principal,
|
||||
State(resource_service): State<AddressbookResourceService<AS, S>>,
|
||||
body: String,
|
||||
user: User,
|
||||
resource_service: Data<AddressbookResourceService<A, S>>,
|
||||
root_span: RootSpan,
|
||||
req: HttpRequest,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let (principal, addressbook_id) = path.into_inner();
|
||||
) -> Result<Response, Error> {
|
||||
if !user.is_principal(&principal) {
|
||||
return Err(Error::Unauthorized);
|
||||
}
|
||||
|
||||
let addressbook = resource_service
|
||||
.addr_store
|
||||
.get_addressbook(&principal, &addressbook_id, false)
|
||||
.get_addressbook(&principal, &addr_id, false)
|
||||
.await?;
|
||||
let addressbook_resource = AddressbookResource(addressbook);
|
||||
if !addressbook_resource
|
||||
.get_user_privileges(&user)?
|
||||
.has(&UserPrivilege::Read)
|
||||
{
|
||||
return Err(Error::Unauthorized);
|
||||
}
|
||||
|
||||
let request = PushRegister::parse_str(&body)?;
|
||||
let sub_id = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
@@ -44,7 +51,7 @@ pub async fn route_post<A: AddressbookStore, S: SubscriptionStore>(
|
||||
.web_push_subscription
|
||||
.push_resource
|
||||
.to_owned(),
|
||||
topic: addressbook.push_topic,
|
||||
topic: addressbook_resource.0.push_topic,
|
||||
expiration: expires.naive_local(),
|
||||
public_key: request
|
||||
.subscription
|
||||
@@ -63,13 +70,17 @@ pub async fn route_post<A: AddressbookStore, S: SubscriptionStore>(
|
||||
.upsert_subscription(subscription)
|
||||
.await?;
|
||||
|
||||
let location = req
|
||||
.resource_map()
|
||||
.url_for(&req, "subscription", &[sub_id])
|
||||
.unwrap();
|
||||
|
||||
Ok(HttpResponse::Created()
|
||||
.append_header((header::LOCATION, location.to_string()))
|
||||
.append_header((header::EXPIRES, expires.to_rfc2822()))
|
||||
.finish())
|
||||
// TODO: make nicer
|
||||
let location = format!("/push_subscription/{sub_id}");
|
||||
Ok((
|
||||
StatusCode::CREATED,
|
||||
HeaderMap::from_iter([
|
||||
(header::LOCATION, HeaderValue::from_str(&location).unwrap()),
|
||||
(
|
||||
header::EXPIRES,
|
||||
HeaderValue::from_str(&expires.to_rfc2822()).unwrap(),
|
||||
),
|
||||
]),
|
||||
)
|
||||
.into_response())
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ use http::StatusCode;
|
||||
use ical::VcardParser;
|
||||
use rustical_ical::AddressObject;
|
||||
use rustical_store::Addressbook;
|
||||
use rustical_store::{AddressbookStore, SubscriptionStore, auth::User};
|
||||
use rustical_store::{AddressbookStore, SubscriptionStore, auth::Principal};
|
||||
use tracing::instrument;
|
||||
|
||||
#[instrument(skip(addr_store))]
|
||||
pub async fn route_put<AS: AddressbookStore, S: SubscriptionStore>(
|
||||
Path((principal, addressbook_id)): Path<(String, String)>,
|
||||
State(AddressbookResourceService { addr_store, .. }): State<AddressbookResourceService<AS, S>>,
|
||||
user: User,
|
||||
user: Principal,
|
||||
body: String,
|
||||
) -> Result<Response, Error> {
|
||||
if !user.is_principal(&principal) {
|
||||
|
||||
@@ -10,7 +10,7 @@ use rustical_dav::{
|
||||
xml::{MultistatusElement, PropfindType, multistatus::ResponseElement},
|
||||
};
|
||||
use rustical_ical::AddressObject;
|
||||
use rustical_store::{AddressbookStore, auth::User};
|
||||
use rustical_store::{AddressbookStore, auth::Principal};
|
||||
use rustical_xml::XmlDeserialize;
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||
@@ -63,7 +63,7 @@ pub async fn handle_addressbook_multiget<AS: AddressbookStore>(
|
||||
prop: &PropfindType<AddressObjectPropWrapperName>,
|
||||
path: &str,
|
||||
puri: &impl PrincipalUri,
|
||||
user: &User,
|
||||
user: &Principal,
|
||||
principal: &str,
|
||||
cal_id: &str,
|
||||
addr_store: &AS,
|
||||
|
||||
@@ -9,7 +9,7 @@ use axum::{
|
||||
response::IntoResponse,
|
||||
};
|
||||
use rustical_dav::xml::{PropfindType, sync_collection::SyncCollectionRequest};
|
||||
use rustical_store::{AddressbookStore, SubscriptionStore, auth::User};
|
||||
use rustical_store::{AddressbookStore, SubscriptionStore, auth::Principal};
|
||||
use rustical_xml::{XmlDeserialize, XmlDocument};
|
||||
use sync_collection::handle_sync_collection;
|
||||
use tracing::instrument;
|
||||
@@ -37,7 +37,7 @@ impl ReportRequest {
|
||||
#[instrument(skip(addr_store))]
|
||||
pub async fn route_report_addressbook<AS: AddressbookStore, S: SubscriptionStore>(
|
||||
Path((principal, addressbook_id)): Path<(String, String)>,
|
||||
user: User,
|
||||
user: Principal,
|
||||
OriginalUri(uri): OriginalUri,
|
||||
Extension(puri): Extension<CardDavPrincipalUri>,
|
||||
State(AddressbookResourceService { addr_store, .. }): State<AddressbookResourceService<AS, S>>,
|
||||
|
||||
@@ -13,7 +13,7 @@ use rustical_dav::{
|
||||
};
|
||||
use rustical_store::{
|
||||
AddressbookStore,
|
||||
auth::User,
|
||||
auth::Principal,
|
||||
synctoken::{format_synctoken, parse_synctoken},
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ pub async fn handle_sync_collection<AS: AddressbookStore>(
|
||||
sync_collection: &SyncCollectionRequest<AddressObjectPropWrapperName>,
|
||||
path: &str,
|
||||
puri: &impl PrincipalUri,
|
||||
user: &User,
|
||||
user: &Principal,
|
||||
principal: &str,
|
||||
addressbook_id: &str,
|
||||
addr_store: &AS,
|
||||
|
||||
@@ -10,7 +10,7 @@ use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||
use rustical_dav::xml::{Resourcetype, ResourcetypeInner, SupportedReportSet};
|
||||
use rustical_dav_push::DavPushExtension;
|
||||
use rustical_store::Addressbook;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
|
||||
#[derive(Clone, Debug, From, Into)]
|
||||
pub struct AddressbookResource(pub(crate) Addressbook);
|
||||
@@ -36,9 +36,11 @@ impl DavPushExtension for AddressbookResource {
|
||||
impl Resource for AddressbookResource {
|
||||
type Prop = AddressbookPropWrapper;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type Principal = Principal;
|
||||
|
||||
const IS_COLLECTION: bool = true;
|
||||
fn is_collection(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
Resourcetype(&[
|
||||
@@ -50,7 +52,7 @@ impl Resource for AddressbookResource {
|
||||
fn get_prop(
|
||||
&self,
|
||||
puri: &impl PrincipalUri,
|
||||
user: &User,
|
||||
user: &Principal,
|
||||
prop: &AddressbookPropWrapperName,
|
||||
) -> Result<Self::Prop, Self::Error> {
|
||||
Ok(match prop {
|
||||
@@ -136,7 +138,7 @@ impl Resource for AddressbookResource {
|
||||
Some(&self.0.principal)
|
||||
}
|
||||
|
||||
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
||||
fn get_user_privileges(&self, user: &Principal) -> Result<UserPrivilegeSet, Self::Error> {
|
||||
Ok(UserPrivilegeSet::owner_only(
|
||||
user.is_principal(&self.0.principal),
|
||||
))
|
||||
|
||||
@@ -3,6 +3,7 @@ use super::methods::report::route_report_addressbook;
|
||||
use crate::address_object::AddressObjectResourceService;
|
||||
use crate::address_object::resource::AddressObjectResource;
|
||||
use crate::addressbook::methods::get::route_get;
|
||||
use crate::addressbook::methods::post::route_post;
|
||||
use crate::addressbook::methods::put::route_put;
|
||||
use crate::addressbook::resource::AddressbookResource;
|
||||
use crate::{CardDavPrincipalUri, Error};
|
||||
@@ -13,7 +14,7 @@ use axum::handler::Handler;
|
||||
use axum::response::Response;
|
||||
use futures_util::future::BoxFuture;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
use rustical_store::{AddressbookStore, SubscriptionStore};
|
||||
use std::convert::Infallible;
|
||||
use std::sync::Arc;
|
||||
@@ -50,18 +51,19 @@ impl<AS: AddressbookStore, S: SubscriptionStore> ResourceService
|
||||
type PathComponents = (String, String); // principal, addressbook_id
|
||||
type Resource = AddressbookResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type Principal = Principal;
|
||||
type PrincipalUri = CardDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook, webdav-push";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal, addressbook_id): &Self::PathComponents,
|
||||
show_deleted: bool,
|
||||
) -> Result<Self::Resource, Error> {
|
||||
let addressbook = self
|
||||
.addr_store
|
||||
.get_addressbook(principal, addressbook_id, false)
|
||||
.get_addressbook(principal, addressbook_id, show_deleted)
|
||||
.await
|
||||
.map_err(|_e| Error::NotFound)?;
|
||||
Ok(addressbook.into())
|
||||
@@ -130,6 +132,13 @@ impl<AS: AddressbookStore, S: SubscriptionStore> AxumMethods for AddressbookReso
|
||||
})
|
||||
}
|
||||
|
||||
fn post() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(route_post::<AS, S>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
|
||||
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(route_put::<AS, S>, state);
|
||||
|
||||
@@ -9,7 +9,7 @@ use rustical_dav::resources::RootResourceService;
|
||||
use rustical_store::auth::middleware::AuthenticationLayer;
|
||||
use rustical_store::{
|
||||
AddressbookStore, SubscriptionStore,
|
||||
auth::{AuthenticationProvider, User},
|
||||
auth::{AuthenticationProvider, Principal},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -44,10 +44,12 @@ pub fn carddav_router<AP: AuthenticationProvider, A: AddressbookStore, S: Subscr
|
||||
Router::new()
|
||||
.nest(
|
||||
prefix,
|
||||
RootResourceService::<_, User, CardDavPrincipalUri>::new(principal_service.clone())
|
||||
.axum_router()
|
||||
.layer(AuthenticationLayer::new(auth_provider))
|
||||
.layer(Extension(CardDavPrincipalUri(prefix))),
|
||||
RootResourceService::<_, Principal, CardDavPrincipalUri>::new(
|
||||
principal_service.clone(),
|
||||
)
|
||||
.axum_router()
|
||||
.layer(AuthenticationLayer::new(auth_provider))
|
||||
.layer(Extension(CardDavPrincipalUri(prefix))),
|
||||
)
|
||||
.route(
|
||||
"/.well-known/carddav",
|
||||
|
||||
@@ -5,7 +5,7 @@ use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||
use rustical_dav::xml::{
|
||||
GroupMemberSet, GroupMembership, HrefElement, Resourcetype, ResourcetypeInner,
|
||||
};
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::auth::Principal;
|
||||
|
||||
mod service;
|
||||
pub use service::*;
|
||||
@@ -14,7 +14,7 @@ pub use prop::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PrincipalResource {
|
||||
principal: User,
|
||||
principal: Principal,
|
||||
members: Vec<String>,
|
||||
}
|
||||
|
||||
@@ -27,9 +27,11 @@ impl ResourceName for PrincipalResource {
|
||||
impl Resource for PrincipalResource {
|
||||
type Prop = PrincipalPropWrapper;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type Principal = Principal;
|
||||
|
||||
const IS_COLLECTION: bool = true;
|
||||
fn is_collection(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
Resourcetype(&[
|
||||
@@ -41,7 +43,7 @@ impl Resource for PrincipalResource {
|
||||
fn get_prop(
|
||||
&self,
|
||||
puri: &impl PrincipalUri,
|
||||
user: &User,
|
||||
user: &Principal,
|
||||
prop: &PrincipalPropWrapperName,
|
||||
) -> Result<Self::Prop, Self::Error> {
|
||||
let principal_href = HrefElement::new(puri.principal_uri(&self.principal.id));
|
||||
@@ -97,7 +99,7 @@ impl Resource for PrincipalResource {
|
||||
Some(&self.principal.id)
|
||||
}
|
||||
|
||||
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
||||
fn get_user_privileges(&self, user: &Principal) -> Result<UserPrivilegeSet, Self::Error> {
|
||||
Ok(UserPrivilegeSet::owner_only(
|
||||
user.is_principal(&self.principal.id),
|
||||
))
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{CardDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::auth::{AuthenticationProvider, User};
|
||||
use rustical_store::auth::{AuthenticationProvider, Principal};
|
||||
use rustical_store::{AddressbookStore, SubscriptionStore};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -51,7 +51,7 @@ impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> Reso
|
||||
type MemberType = AddressbookResource;
|
||||
type Resource = PrincipalResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type Principal = Principal;
|
||||
type PrincipalUri = CardDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||
@@ -59,6 +59,7 @@ impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> Reso
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
_show_deleted: bool,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
let user = self
|
||||
.auth_provider
|
||||
|
||||
@@ -26,3 +26,5 @@ tokio.workspace = true
|
||||
http.workspace = true
|
||||
headers.workspace = true
|
||||
strum.workspace = true
|
||||
matchit.workspace = true
|
||||
matchit-serde.workspace = true
|
||||
|
||||
@@ -28,6 +28,9 @@ pub enum Error {
|
||||
|
||||
#[error("Precondition Failed")]
|
||||
PreconditionFailed,
|
||||
|
||||
#[error("Forbidden")]
|
||||
Forbidden,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
@@ -49,6 +52,7 @@ impl Error {
|
||||
Error::PropReadOnly => StatusCode::CONFLICT,
|
||||
Error::PreconditionFailed => StatusCode::PRECONDITION_FAILED,
|
||||
Self::IOError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Self::Forbidden => StatusCode::FORBIDDEN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use quick_xml::name::Namespace;
|
||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc3744
|
||||
#[derive(Debug, Clone, XmlSerialize, XmlDeserialize, Eq, Hash, PartialEq)]
|
||||
pub enum UserPrivilege {
|
||||
Read,
|
||||
@@ -47,6 +48,12 @@ pub struct UserPrivilegeSet {
|
||||
|
||||
impl UserPrivilegeSet {
|
||||
pub fn has(&self, privilege: &UserPrivilege) -> bool {
|
||||
if (privilege == &UserPrivilege::WriteProperties
|
||||
|| privilege == &UserPrivilege::WriteContent)
|
||||
&& self.privileges.contains(&UserPrivilege::Write)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
self.privileges.contains(privilege) || self.privileges.contains(&UserPrivilege::All)
|
||||
}
|
||||
|
||||
@@ -72,6 +79,15 @@ impl UserPrivilegeSet {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn owner_write_properties(is_owner: bool) -> Self {
|
||||
// Content is read-only but we can write properties
|
||||
if is_owner {
|
||||
Self::write_properties()
|
||||
} else {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_only() -> Self {
|
||||
Self {
|
||||
privileges: HashSet::from([
|
||||
@@ -81,6 +97,17 @@ impl UserPrivilegeSet {
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_properties() -> Self {
|
||||
Self {
|
||||
privileges: HashSet::from([
|
||||
UserPrivilege::Read,
|
||||
UserPrivilege::WriteProperties,
|
||||
UserPrivilege::ReadAcl,
|
||||
UserPrivilege::ReadCurrentUserPrivilegeSet,
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<[UserPrivilege; N]> for UserPrivilegeSet {
|
||||
|
||||
@@ -1,25 +1,54 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::StatusCode;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{
|
||||
header::{Depth, Overwrite},
|
||||
resource::ResourceService,
|
||||
};
|
||||
use axum::{
|
||||
extract::{MatchedPath, Path, State},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::{HeaderMap, StatusCode, Uri};
|
||||
use matchit_serde::ParamsDeserializer;
|
||||
use serde::Deserialize;
|
||||
use tracing::instrument;
|
||||
|
||||
#[instrument(skip(_path, _resource_service,))]
|
||||
#[instrument(skip(path, resource_service,))]
|
||||
pub(crate) async fn axum_route_copy<R: ResourceService>(
|
||||
Path(_path): Path<R::PathComponents>,
|
||||
State(_resource_service): State<R>,
|
||||
Path(path): Path<R::PathComponents>,
|
||||
State(resource_service): State<R>,
|
||||
depth: Option<Depth>,
|
||||
principal: R::Principal,
|
||||
overwrite: Overwrite,
|
||||
matched_path: MatchedPath,
|
||||
header_map: HeaderMap,
|
||||
) -> Result<Response, R::Error> {
|
||||
// TODO: Actually implement, but to be WebDAV-compliant we must at least support this route but
|
||||
// can return a 403 error
|
||||
let _depth = depth.unwrap_or(Depth::Infinity);
|
||||
Ok(StatusCode::FORBIDDEN.into_response())
|
||||
let destination = header_map
|
||||
.get("Destination")
|
||||
.ok_or(crate::Error::Forbidden)?
|
||||
.to_str()
|
||||
.map_err(|_| crate::Error::Forbidden)?;
|
||||
let destination_uri: Uri = destination.parse().map_err(|_| crate::Error::Forbidden)?;
|
||||
// TODO: Check that host also matches
|
||||
let destination = destination_uri.path();
|
||||
|
||||
let mut router = matchit::Router::new();
|
||||
router.insert(matched_path.as_str(), ()).unwrap();
|
||||
if let Ok(matchit::Match { params, .. }) = router.at(destination) {
|
||||
let params =
|
||||
matchit_serde::Params::try_from(¶ms).map_err(|_| crate::Error::Forbidden)?;
|
||||
let dest_path = R::PathComponents::deserialize(&ParamsDeserializer::new(params))
|
||||
.map_err(|_| crate::Error::Forbidden)?;
|
||||
|
||||
if resource_service
|
||||
.copy_resource(&path, &dest_path, &principal, overwrite.is_true())
|
||||
.await?
|
||||
{
|
||||
// Overwritten
|
||||
Ok(StatusCode::NO_CONTENT.into_response())
|
||||
} else {
|
||||
// Not overwritten
|
||||
Ok(StatusCode::CREATED.into_response())
|
||||
}
|
||||
} else {
|
||||
Ok(StatusCode::FORBIDDEN.into_response())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,10 +45,11 @@ pub async fn route_delete<R: ResourceService>(
|
||||
if_match: Option<IfMatch>,
|
||||
if_none_match: Option<IfNoneMatch>,
|
||||
) -> Result<(), R::Error> {
|
||||
let resource = resource_service.get_resource(path_components).await?;
|
||||
let resource = resource_service.get_resource(path_components, true).await?;
|
||||
|
||||
// Kind of a bodge since we don't get unbind from the parent
|
||||
let privileges = resource.get_user_privileges(principal)?;
|
||||
if !privileges.has(&UserPrivilege::Write) {
|
||||
if !privileges.has(&UserPrivilege::WriteProperties) {
|
||||
return Err(Error::Unauthorized.into());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,54 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::StatusCode;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{
|
||||
header::{Depth, Overwrite},
|
||||
resource::ResourceService,
|
||||
};
|
||||
use axum::{
|
||||
extract::{MatchedPath, Path, State},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::{HeaderMap, StatusCode, Uri};
|
||||
use matchit_serde::ParamsDeserializer;
|
||||
use serde::Deserialize;
|
||||
use tracing::instrument;
|
||||
|
||||
#[instrument(skip(_path, _resource_service,))]
|
||||
#[instrument(skip(path, resource_service,))]
|
||||
pub(crate) async fn axum_route_move<R: ResourceService>(
|
||||
Path(_path): Path<R::PathComponents>,
|
||||
State(_resource_service): State<R>,
|
||||
Path(path): Path<R::PathComponents>,
|
||||
State(resource_service): State<R>,
|
||||
depth: Option<Depth>,
|
||||
principal: R::Principal,
|
||||
overwrite: Overwrite,
|
||||
matched_path: MatchedPath,
|
||||
header_map: HeaderMap,
|
||||
) -> Result<Response, R::Error> {
|
||||
// TODO: Actually implement, but to be WebDAV-compliant we must at least support this route but
|
||||
// can return a 403 error
|
||||
let _depth = depth.unwrap_or(Depth::Infinity);
|
||||
Ok(StatusCode::FORBIDDEN.into_response())
|
||||
let destination = header_map
|
||||
.get("Destination")
|
||||
.ok_or(crate::Error::Forbidden)?
|
||||
.to_str()
|
||||
.map_err(|_| crate::Error::Forbidden)?;
|
||||
let destination_uri: Uri = destination.parse().map_err(|_| crate::Error::Forbidden)?;
|
||||
// TODO: Check that host also matches
|
||||
let destination = destination_uri.path();
|
||||
|
||||
let mut router = matchit::Router::new();
|
||||
router.insert(matched_path.as_str(), ()).unwrap();
|
||||
if let Ok(matchit::Match { params, .. }) = router.at(destination) {
|
||||
let params =
|
||||
matchit_serde::Params::try_from(¶ms).map_err(|_| crate::Error::Forbidden)?;
|
||||
let dest_path = R::PathComponents::deserialize(&ParamsDeserializer::new(params))
|
||||
.map_err(|_| crate::Error::Forbidden)?;
|
||||
|
||||
if resource_service
|
||||
.copy_resource(&path, &dest_path, &principal, overwrite.is_true())
|
||||
.await?
|
||||
{
|
||||
// Overwritten
|
||||
Ok(StatusCode::NO_CONTENT.into_response())
|
||||
} else {
|
||||
// Not overwritten
|
||||
Ok(StatusCode::CREATED.into_response())
|
||||
}
|
||||
} else {
|
||||
Ok(StatusCode::FORBIDDEN.into_response())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,9 @@ pub(crate) async fn route_propfind<R: ResourceService>(
|
||||
resource_service: &R,
|
||||
puri: &impl PrincipalUri,
|
||||
) -> Result<RSMultistatus<R>, R::Error> {
|
||||
let resource = resource_service.get_resource(path_components).await?;
|
||||
let resource = resource_service
|
||||
.get_resource(path_components, false)
|
||||
.await?;
|
||||
let privileges = resource.get_user_privileges(principal)?;
|
||||
if !privileges.has(&UserPrivilege::Read) {
|
||||
return Err(Error::Unauthorized.into());
|
||||
|
||||
@@ -85,7 +85,9 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
|
||||
operations,
|
||||
) = XmlDocument::parse_str(body).map_err(Error::XmlError)?;
|
||||
|
||||
let mut resource = resource_service.get_resource(path_components).await?;
|
||||
let mut resource = resource_service
|
||||
.get_resource(path_components, false)
|
||||
.await?;
|
||||
let privileges = resource.get_user_privileges(principal)?;
|
||||
if !privileges.has(&UserPrivilege::Write) {
|
||||
return Err(Error::Unauthorized.into());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user