File: libbalsa-webdav.h

package info (click to toggle)
balsa 2.6.5-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 23,576 kB
  • sloc: ansic: 99,871; xml: 4,934; makefile: 769; sh: 185; awk: 60; python: 34
file content (330 lines) | stat: -rw-r--r-- 14,220 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/* LibBalsaWebdav - Webdav base class for Balsa
 *
 * Copyright (C) Albrecht Dreß <mailto:albrecht.dress@posteo.de> 2023
 *
 * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this library. If not, see
 * <https://www.gnu.org/licenses/>.
 *
 * Relevant standards used in this module:
 * * RFC 4918: HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)
 * * RFC 5397: WebDAV Current Principal Extension
 * * RFC 6578: Collection Synchronization for Web Distributed Authoring and Versioning (WebDAV)
 * * RFC 6764: Locating Services for Calendaring Extensions to WebDAV (CalDAV) and vCard Extensions to WebDAV (CardDAV)
 * * RFC 8144: Use of the Prefer Header Field in Web Distributed Authoring and Versioning (WebDAV)
 * * https://github.com/apple/ccs-calendarserver/blob/master/doc/Extensions/caldav-ctag.txt: CTag in CalDAV
 */

#ifndef _LIBBALSA_WEBDAV_H
#define _LIBBALSA_WEBDAV_H


#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
# include "config.h"
#endif						/* HAVE_CONFIG_H */


#if defined(HAVE_WEBDAV)


#include <glib-object.h>
#include <libxml/xpath.h>
#include <libsoup/soup.h>


G_BEGIN_DECLS


#define WEBDAV_ERROR_QUARK							(g_quark_from_static_string("libbalsa-webdav"))

/** @brief XPath fragments
 *
 * Some XPath fragments, used to extract information from various PROPFIND (RFC 4918, sect. 9.1) responses.
 * @note Every request always includes the namespace 'a="DAV:"' (RFC 4918).
 */
#define WEBDAV_MULTISTATUS_XP						"/a:multistatus/a:response/a:propstat"
#define WEBDAV_OK_XP								"a:status[contains(text(),' 200 ')]"
#define WEBDAV_XP_SUB_CTAG							"a:propstat/a:prop/c:getctag/text()"
#define WEBDAV_XP_SUB_SYNCTOK						"a:propstat/a:prop/a:sync-token/text()"
#define WEBDAV_XP_SUB_REPSET						"a:propstat/a:prop/a:supported-report-set/a:supported-report/a:report/*"
#define WEBDAV_XP_SUB_HREF							"a:href/text()"
#define WEBDAV_XP_SUB_DISPNAME						"a:propstat/a:prop/a:displayname/text()"

/** Namespace for CalendarServer extensions */
#define WEBDAV_NS_CALSERVER							"http://calendarserver.org/ns/"

#define LIBBALSA_WEBDAV_TYPE						(libbalsa_webdav_get_type())
G_DECLARE_DERIVABLE_TYPE(LibBalsaWebdav, libbalsa_webdav, LIBBALSA, WEBDAV, SoupSession)


struct _LibBalsaWebdavClass {
	SoupSessionClass parent;
};


/** @brief Callback function prototype for evaluating the response of a PROPFIND request (RFC 4918, sect. 9.1)
 *
 * @param[in] nodes set of matching XML nodes
 * @param[in] xpath_ctx current XPath context
 * @param[in] user_data user data pointer
 * @return an application-dependent linked list of items
 */
typedef GList *(*libbalsa_webdav_eval_func)(xmlNodeSetPtr nodes, xmlXPathContextPtr xpath_ctx, gpointer user_data);


/** @brief WebDAV server synchronisation support */
typedef enum {
	LIBBALSA_WEBDAV_SYNC_NONE,			/**< Server does not support synchronisation. */
	LIBBALSA_WEBDAV_SYNC_TOKEN,			/**< Server supports synchronisation token (RFC 6578, sect. 4). */
	LIBBALSA_WEBDAV_SYNC_CTAG			/**< Server supports "getctag" (CTag in CalDAV). */
} libbalsa_webdav_sync_t;


/** @brief WebDAV resource item */
typedef struct {
	gchar *name;						/**< WebDAV resource display name (RFC 4918, sect. 15.2), @em MAY be <c>NULL</c>. */
	gchar *href;						/**< WebDAV resource URI, never <c>NULL</c>. */
	guint flags;						/**< Resource capability flags, type dependent. */
} libbalsa_webdav_resource_t;


/** @brief WebDAV PROPFIND (RFC 4918, sect. 9.1) request data */
typedef struct {
	gchar *item;						/**< The XML items which shall be requested. */
	gchar *depth;						/**< Request DEPTH header value (RFC 4918, sect. 10.2). */
	gchar *xpath;						/**< XPath to extract from the response. */
	gchar *ns_list[5];					/**< <c>NULL</c>-terminated list of (identifier, namespace) pairs containing additional
										 * namespace definitions in the request ("a", "DAV:" is added automatically). */
	libbalsa_webdav_eval_func eval_fn;	/**< Callback function for evaluating the matching XPath items from the response. */
} libbalsa_webdav_propfind_t;


/** @brief WebDAV REPORT request data */
typedef struct {
	gchar *name;						/**< The name of the report including the namespace (note: not an XML item!). */
	gchar *body;						/**< XML body of the report. */
	gchar *depth;						/**< Request DEPTH header value (RFC 4918, sect. 10.2). */
	gchar *xpath;						/**< XPath to extract from the response. */
	gchar *ns_list[5];					/**< <c>NULL</c>-terminated list of (identifier, namespace) pairs containing additional
										 * namespace definitions in the request ("a", "DAV:" is added automatically). */
	libbalsa_webdav_eval_func eval_fn;	/**< Callback function for evaluating the matching XPath items from the response. */
} libbalsa_webdav_report_t;


/** @brief Create a new LibBalsaWebdav object
 *
 * @return LibBalsaWebdav object, not yet linked to a remote URI
 */
LibBalsaWebdav *libbalsa_webdav_new(void);

/** @brief Configure a LibBalsaWebdav object
 *
 * @param[in] webdav LibBalsaWebdav object
 * @param[in] uri full URI of the WebDAV resource
 * @param[in] username user name used for authentication
 * @param[in] password password used for authentication
 */
void libbalsa_webdav_setup(LibBalsaWebdav *webdav,
						   const gchar    *uri,
						   const gchar    *username,
						   const gchar    *password);

/** @brief Get WebDAV resources
 *
 * @param[in] webdav LibBalsaWebdav object
 * @param[in] path request path (see below)
 * @param[in] homeset homeset which shall be requested (e.g. "addressbook-home-set")
 * @param[in] resource relevant resource name returned by the server (e.g. "addressbook")
 * @param[in] namespace namespace for @em item and @em resource
 * @param[in,out] error filled with error information on error, may be @c NULL
 * @return a list of @ref libbalsa_webdav_resource_t items on success, @c NULL on error or if not matching resource has been found
 *
 * The request path is either @c NULL (use the URI configured by calling libbalsa_webdav_setup()), a relative path, or a full URI.
 *
 * The function performs a request for the @c current-user-principal (RFC 5397), the home set specified by @em item and for all
 * @em resource resources in all home sets.  The returned list contains the absolute URI and the display name (RFC 4918, sect. 15.2)
 * of all matching resources.
 *
 * @note The caller @em SHALL free the returned list by calling libbalsa_webdav_resource_free() on every element.
 */
GList *libbalsa_webdav_get_resource(LibBalsaWebdav *webdav,
									const gchar    *path,
									const gchar    *homeset,
									const gchar    *resource,
									const gchar    *namespace,
									GError        **error)
	G_GNUC_WARN_UNUSED_RESULT;

/** @brief Run a WebDAV PROPFIND request
 *
 * @param[in] webdav LibBalsaWebdav object
 * @param[in] path request path (see below)
 * @param[in] task PROPFIND request data
 * @param[in] user_data user data, passed to the evaluation callback @ref libbalsa_webdav_propfind_t::eval_fn
 * @param[in,out] error filled with error information on error, may be @c NULL
 * @return a list of items allocated by the evaluation callback on success, @c NULL on error or if not data has been found
 *
 * The request path is either @c NULL (use the URI configured by calling libbalsa_webdav_setup()), a relative path, or a full URI.
 */
GList *libbalsa_webdav_propfind(LibBalsaWebdav                   *webdav,
								const gchar                      *path,
								const libbalsa_webdav_propfind_t *task,
								gpointer                          user_data,
								GError                          **error)
	G_GNUC_WARN_UNUSED_RESULT;

/** @brief Run a WebDAV REPORT request
 *
 * @param[in] webdav LibBalsaWebdav object
 * @param[in] path request path (see below)
 * @param[in] task REPORT request data
 * @param[in] user_data user data, passed to the evaluation callback @ref libbalsa_webdav_propfind_t::eval_fn
 * @param[in,out] error filled with error information on error, may be @c NULL
 * @return a list of items allocated by the evaluation callback on success, @c NULL on error or if not data has been found
 *
 * The request path is either @c NULL (use the URI configured by calling libbalsa_webdav_setup()), a relative path, or a full URI.
 */
GList *libbalsa_webdav_report(LibBalsaWebdav                 *webdav,
							  const gchar                    *path,
							  const libbalsa_webdav_report_t *task,
							  gpointer                        user_data,
							  GError                        **error)
	G_GNUC_WARN_UNUSED_RESULT;

/** @brief Run a WebDAV PUT request
 *
 * @param[in] webdav LibBalsaWebdav object
 * @param[in] data the data which shall be written, excluding the terminating NUL character
 * @param[in] mime_type the MIME type of the data
 * @param[in] name the name on the remote server, appended to the URI configured by calling libbalsa_webdav_setup()
 * @param[in] etag an ETag value to modify an existing resource, or @c NULL to create a new one
 * @param[in,out] error filled with error information on error, may be @c NULL
 * @return the ETag value returned by the remote server on success, or @c NULL on error
 *
 * Create or modify a remote resource, returning the ETag value of it.  Note that some broken servers do not return an ETag on
 * success.  In this case, the function returns an empty string.
 */
gchar *libbalsa_webdav_put(LibBalsaWebdav *webdav,
						   const gchar    *data,
						   const gchar    *mime_type,
						   const gchar    *name,
						   const gchar    *etag,
						   GError        **error)
	G_GNUC_WARN_UNUSED_RESULT;

/** @brief Read the current CTag
 *
 * @param[in] webdav LibBalsaWebdav object
 * @param[in,out] error filled with error information on error, may be @c NULL
 * @return a newly allocated string containing the current CTag, @c NULL on error or if the remote server does not support it
 */
gchar *libbalsa_webdav_get_ctag(LibBalsaWebdav *webdav,
								GError        **error)
	G_GNUC_WARN_UNUSED_RESULT;

/** @brief Read the current RFC 6578 synchronisation token
 *
 * @param[in] webdav LibBalsaWebdav object
 * @param[in,out] error filled with error information on error, may be @c NULL
 * @return a newly allocated string containing the current token, @c NULL on error or if the remote server does not support it
 */
gchar *libbalsa_webdav_get_sync_token(LibBalsaWebdav *webdav,
									  GError        **error)
	G_GNUC_WARN_UNUSED_RESULT;

/** @brief Get the URI of the WebDAV resource
 *
 * @param[in] webdav LibBalsaWebdav object
 * @return the full URI of the WebDAV resource
 */
const gchar *libbalsa_webdav_get_uri(LibBalsaWebdav *webdav);

/** @brief Force a disconnect of the underlying HTTPS session
 *
 * @param[in] webdav LibBalsaWebdav object
 */
void libbalsa_webdav_disconnect(LibBalsaWebdav *webdav);

/** @brief Free a WebDAV resource item
 *
 * @param[in] resource item which shall be freed, may be @c NULL
 */
void libbalsa_webdav_resource_free(libbalsa_webdav_resource_t *resource);

/** @brief Run a DNS query to identify a WebDAV server
 *
 * @param[in] domain the domain to check
 * @param[in] service the service the domain shall offer
 * @param[out] path filled with the initial "context path" on success
 * @param[in,out] error filled with error information on error, may be @c NULL
 * @return a newly allocated https URI supporting the requested service on success, or @c NULL on error or if no DNS data is
 *         available
 *
 * Run a DNS query for a @c SRV record _service._tcp.domain (RFC 6764) and return it as https URI.  If a @c TXT query returns an
 * initial "context path" it is also returned.  Note that RFC 6764 sect. 4 states that if no such @c TXT record is returned, the
 * ".well-known" URI approach shall be used.  However, at least some broken services (e.g. icloud.com) fail in this case, so the
 * function does not return a path if no appropriate @c TXT record exists.
 */
gchar *libbalsa_webdav_lookup_srv(const gchar *domain,
								  const gchar *service,
								  gchar      **path,
								  GError     **error)
	G_GNUC_WARN_UNUSED_RESULT;


/* Helper functions for evaluating XML node sets */

/** @brief Extract XML text nodes
 *
 * @param[in] nodes XML node set
 * @param[in] xpath_ctx current XPath context, unused
 * @param[in] user_data user data, unused
 * @return a distinct list of newly allocated strings, containing the content of all XML text nodes in @em nodes
 */
GList *libbalsa_webdav_xp_get_strlist(xmlNodeSetPtr      nodes,
									  xmlXPathContextPtr xpath_ctx,
									  gpointer           user_data)
	G_GNUC_WARN_UNUSED_RESULT;

/** @brief Extract a string using XPath from a XML node
 *
 * @param[in] node XML node
 * @param[in] sub_expr XPath expression relative to the XML node
 * @param[in] xpath_ctx current XPath context
 * @return the extracted string on success, @c NULL if the passed XPath did not match
 *
 * Extract a string from a XML text node, selected using a XPath expression applied to a XML node.
 */
gchar *libbalsa_webdav_xp_string_from_sub(xmlNodePtr         node,
										  const xmlChar     *sub_expr,
										  xmlXPathContextPtr xpath_ctx)
	G_GNUC_WARN_UNUSED_RESULT;

/** @brief Peek the text content of an XML attribute node
 *
 * @param[in] node XML attribute node
 * @return the text content of the attribute node on success, @c NULL on error
 */
static inline const gchar *
libbalsa_webdav_peek_xml_attr(xmlNodePtr node)
{
	if ((node->type == XML_ATTRIBUTE_NODE) && (node->children != NULL) && (node->children->type == XML_TEXT_NODE)) {
		return (const gchar *) node->children->content;
	} else {
		return NULL;
	}
}


G_END_DECLS


#endif		/* defined(HAVE_WEBDAV) */


#endif  /* _LIBBALSA_WEBDAV_H */