1 /*
2 * e-server-side-source.c
3 *
4 * This library is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 /**
19 * SECTION: e-server-side-source
20 * @include: libebackend/libebackend.h
21 * @short_description: A server-side data source
22 *
23 * An #EServerSideSource is an #ESource with some additional capabilities
24 * exclusive to the registry D-Bus service.
25 **/
26
27 #include "evolution-data-server-config.h"
28
29 #include <stdio.h>
30 #include <glib/gi18n-lib.h>
31
32 /* Private D-Bus classes. */
33 #include "e-dbus-source.h"
34
35 #include "e-server-side-source.h"
36
37 #define DBUS_OBJECT_PATH E_SOURCE_REGISTRY_SERVER_OBJECT_PATH "/Source"
38
39 #define PRIMARY_GROUP_NAME "Data Source"
40
41 typedef struct _AsyncContext AsyncContext;
42
43 struct _EServerSideSourcePrivate {
44 gpointer server; /* weak pointer */
45 GWeakRef oauth2_support;
46
47 GNode node;
48 GFile *file;
49
50 /* For comparison. */
51 gchar *file_contents;
52
53 gchar *write_directory;
54
55 GMutex last_values_lock;
56 gchar *last_reason;
57 gchar *last_certificate_pem;
58 gchar *last_certificate_errors;
59 gchar *last_dbus_error_name;
60 gchar *last_dbus_error_message;
61 ENamedParameters *last_credentials;
62
63 GMutex pending_credentials_lookup_lock;
64 GCancellable *pending_credentials_lookup;
65 };
66
67 struct _AsyncContext {
68 EDBusSourceRemoteCreatable *remote_creatable;
69 EDBusSourceRemoteDeletable *remote_deletable;
70 EDBusSourceOAuth2Support *oauth2_support;
71 GDBusMethodInvocation *invocation;
72 };
73
74 enum {
75 PROP_0,
76 PROP_EXPORTED,
77 PROP_FILE,
78 PROP_OAUTH2_SUPPORT,
79 PROP_REMOTE_CREATABLE,
80 PROP_REMOTE_DELETABLE,
81 PROP_REMOVABLE,
82 PROP_SERVER,
83 PROP_WRITABLE,
84 PROP_WRITE_DIRECTORY
85 };
86
87 static GInitableIface *initable_parent_interface;
88
89 /* Forward Declarations */
90 static void e_server_side_source_initable_init
91 (GInitableIface *iface);
92
G_DEFINE_TYPE_WITH_CODE(EServerSideSource,e_server_side_source,E_TYPE_SOURCE,G_ADD_PRIVATE (EServerSideSource)G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,e_server_side_source_initable_init))93 G_DEFINE_TYPE_WITH_CODE (
94 EServerSideSource,
95 e_server_side_source,
96 E_TYPE_SOURCE,
97 G_ADD_PRIVATE (EServerSideSource)
98 G_IMPLEMENT_INTERFACE (
99 G_TYPE_INITABLE,
100 e_server_side_source_initable_init))
101
102 static void
103 async_context_free (AsyncContext *async_context)
104 {
105 if (async_context->remote_creatable != NULL)
106 g_object_unref (async_context->remote_creatable);
107
108 if (async_context->remote_deletable != NULL)
109 g_object_unref (async_context->remote_deletable);
110
111 if (async_context->oauth2_support != NULL)
112 g_object_unref (async_context->oauth2_support);
113
114 if (async_context->invocation != NULL)
115 g_object_unref (async_context->invocation);
116
117 g_slice_free (AsyncContext, async_context);
118 }
119
120 static gboolean
server_side_source_parse_data(GKeyFile * key_file,const gchar * data,gsize length,GError ** error)121 server_side_source_parse_data (GKeyFile *key_file,
122 const gchar *data,
123 gsize length,
124 GError **error)
125 {
126 gboolean success;
127
128 success = g_key_file_load_from_data (
129 key_file, data, length, G_KEY_FILE_NONE, error);
130
131 if (!success)
132 return FALSE;
133
134 /* Make sure the key file has a [Data Source] group. */
135 if (!g_key_file_has_group (key_file, PRIMARY_GROUP_NAME)) {
136 g_set_error (
137 error, G_KEY_FILE_ERROR,
138 G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
139 _("Data source is missing a [%s] group"),
140 PRIMARY_GROUP_NAME);
141 return FALSE;
142 }
143
144 return TRUE;
145 }
146
147 static void
server_side_source_print_diff(ESource * source,const gchar * old_data,const gchar * new_data)148 server_side_source_print_diff (ESource *source,
149 const gchar *old_data,
150 const gchar *new_data)
151 {
152 gchar **old_strv = NULL;
153 gchar **new_strv = NULL;
154 guint old_length = 0;
155 guint new_length = 0;
156 guint ii;
157
158 if (!e_source_registry_debug_enabled ())
159 return;
160
161 e_source_registry_debug_print ("Saving %s\n", e_source_get_uid (source));
162
163 if (old_data != NULL) {
164 old_strv = g_strsplit (old_data, "\n", 0);
165 old_length = g_strv_length (old_strv);
166 }
167
168 if (new_data != NULL) {
169 new_strv = g_strsplit (new_data, "\n", 0);
170 new_length = g_strv_length (new_strv);
171 }
172
173 for (ii = 0; ii < MIN (old_length, new_length); ii++) {
174 if (g_strcmp0 (old_strv[ii], new_strv[ii]) != 0) {
175 e_source_registry_debug_print (" - : %s\n", old_strv[ii]);
176 e_source_registry_debug_print (" + : %s\n", new_strv[ii]);
177 } else {
178 e_source_registry_debug_print (" : %s\n", old_strv[ii]);
179 }
180 }
181
182 for (; ii < old_length; ii++)
183 e_source_registry_debug_print (" - : %s\n", old_strv[ii]);
184
185 for (; ii < new_length; ii++)
186 e_source_registry_debug_print (" + : %s\n", new_strv[ii]);
187
188 g_strfreev (old_strv);
189 g_strfreev (new_strv);
190 }
191
192 static gboolean
server_side_source_traverse_cb(GNode * node,GQueue * queue)193 server_side_source_traverse_cb (GNode *node,
194 GQueue *queue)
195 {
196 g_queue_push_tail (queue, g_object_ref (node->data));
197
198 return FALSE;
199 }
200
201 static ESourceCredentialsReason
server_side_source_credentials_reason_from_text(const gchar * arg_reason)202 server_side_source_credentials_reason_from_text (const gchar *arg_reason)
203 {
204 ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_UNKNOWN;
205
206 if (arg_reason && *arg_reason) {
207 GEnumClass *enum_class;
208 GEnumValue *enum_value;
209
210 enum_class = g_type_class_ref (E_TYPE_SOURCE_CREDENTIALS_REASON);
211 enum_value = g_enum_get_value_by_nick (enum_class, arg_reason);
212
213 if (enum_value) {
214 reason = enum_value->value;
215 } else {
216 g_warning ("%s: Unknown reason enum: '%s'", G_STRFUNC, arg_reason);
217 }
218
219 g_type_class_unref (enum_class);
220 }
221
222 return reason;
223 }
224
225 typedef struct _ReinvokeCredentialsRequiredData {
226 EServerSideSource *source;
227 gchar *arg_reason;
228 gchar *arg_certificate_pem;
229 gchar *arg_certificate_errors;
230 gchar *arg_dbus_error_name;
231 gchar *arg_dbus_error_message;
232 } ReinvokeCredentialsRequiredData;
233
234 static void
reinvoke_credentials_required_data_free(gpointer ptr)235 reinvoke_credentials_required_data_free (gpointer ptr)
236 {
237 ReinvokeCredentialsRequiredData *data = ptr;
238
239 if (data) {
240 g_clear_object (&data->source);
241 g_free (data->arg_reason);
242 g_free (data->arg_certificate_pem);
243 g_free (data->arg_certificate_errors);
244 g_free (data->arg_dbus_error_name);
245 g_free (data->arg_dbus_error_message);
246 g_slice_free (ReinvokeCredentialsRequiredData, data);
247 }
248 }
249
250 static void server_side_source_credentials_lookup_cb (GObject *source_object, GAsyncResult *result, gpointer user_data);
251
252 static gboolean
server_side_source_invoke_credentials_required_cb(EDBusSource * dbus_interface,GDBusMethodInvocation * invocation,const gchar * arg_reason,const gchar * arg_certificate_pem,const gchar * arg_certificate_errors,const gchar * arg_dbus_error_name,const gchar * arg_dbus_error_message,EServerSideSource * source)253 server_side_source_invoke_credentials_required_cb (EDBusSource *dbus_interface,
254 GDBusMethodInvocation *invocation,
255 const gchar *arg_reason,
256 const gchar *arg_certificate_pem,
257 const gchar *arg_certificate_errors,
258 const gchar *arg_dbus_error_name,
259 const gchar *arg_dbus_error_message,
260 EServerSideSource *source)
261 {
262 gboolean skip_emit = FALSE;
263
264 if (invocation)
265 e_dbus_source_complete_invoke_credentials_required (dbus_interface, invocation);
266
267 g_mutex_lock (&source->priv->pending_credentials_lookup_lock);
268 if (source->priv->pending_credentials_lookup) {
269 g_cancellable_cancel (source->priv->pending_credentials_lookup);
270 g_clear_object (&source->priv->pending_credentials_lookup);
271 }
272 g_mutex_unlock (&source->priv->pending_credentials_lookup_lock);
273
274 g_mutex_lock (&source->priv->last_values_lock);
275
276 g_free (source->priv->last_reason);
277 g_free (source->priv->last_certificate_pem);
278 g_free (source->priv->last_certificate_errors);
279 g_free (source->priv->last_dbus_error_name);
280 g_free (source->priv->last_dbus_error_message);
281 source->priv->last_reason = g_strdup (arg_reason);
282 source->priv->last_certificate_pem = g_strdup (arg_certificate_pem);
283 source->priv->last_certificate_errors = g_strdup (arg_certificate_errors);
284 source->priv->last_dbus_error_name = g_strdup (arg_dbus_error_name);
285 source->priv->last_dbus_error_message = g_strdup (arg_dbus_error_message);
286
287 g_mutex_unlock (&source->priv->last_values_lock);
288
289 /* Do not bother clients, when the password is stored. */
290 if (server_side_source_credentials_reason_from_text (arg_reason) == E_SOURCE_CREDENTIALS_REASON_REQUIRED) {
291 ESourceRegistryServer *server;
292 ESourceCredentialsProvider *credentials_provider;
293
294 server = e_server_side_source_get_server (source);
295 credentials_provider = server ? e_source_registry_server_ref_credentials_provider (server) : NULL;
296
297 if (credentials_provider) {
298 ReinvokeCredentialsRequiredData *data;
299 GCancellable *cancellable;
300
301 g_mutex_lock (&source->priv->pending_credentials_lookup_lock);
302 if (source->priv->pending_credentials_lookup) {
303 g_cancellable_cancel (source->priv->pending_credentials_lookup);
304 g_clear_object (&source->priv->pending_credentials_lookup);
305 }
306 cancellable = g_cancellable_new ();
307 source->priv->pending_credentials_lookup = g_object_ref (cancellable);
308 g_mutex_unlock (&source->priv->pending_credentials_lookup_lock);
309
310 data = g_slice_new0 (ReinvokeCredentialsRequiredData);
311 data->source = g_object_ref (source);
312 data->arg_reason = g_strdup (arg_reason);
313 data->arg_certificate_pem = g_strdup (arg_certificate_pem);
314 data->arg_certificate_errors = g_strdup (arg_certificate_errors);
315 data->arg_dbus_error_name = g_strdup (arg_dbus_error_name);
316 data->arg_dbus_error_message = g_strdup (arg_dbus_error_message);
317
318 skip_emit = TRUE;
319
320 e_source_credentials_provider_lookup (credentials_provider, E_SOURCE (source),
321 cancellable, server_side_source_credentials_lookup_cb, data);
322
323 g_object_unref (cancellable);
324 }
325
326 g_clear_object (&credentials_provider);
327 }
328
329 if (!skip_emit) {
330 e_dbus_source_emit_credentials_required (dbus_interface, arg_reason, arg_certificate_pem, arg_certificate_errors, arg_dbus_error_name, arg_dbus_error_message);
331 }
332
333 return TRUE;
334 }
335
336 static gboolean
server_side_source_invoke_authenticate_cb(EDBusSource * dbus_interface,GDBusMethodInvocation * invocation,const gchar * const * arg_credentials,EServerSideSource * source)337 server_side_source_invoke_authenticate_cb (EDBusSource *dbus_interface,
338 GDBusMethodInvocation *invocation,
339 const gchar * const *arg_credentials,
340 EServerSideSource *source)
341 {
342 gchar **last_credentials_strv = NULL;
343
344 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), TRUE);
345
346 g_mutex_lock (&source->priv->pending_credentials_lookup_lock);
347 if (source->priv->pending_credentials_lookup) {
348 g_cancellable_cancel (source->priv->pending_credentials_lookup);
349 g_clear_object (&source->priv->pending_credentials_lookup);
350 }
351 g_mutex_unlock (&source->priv->pending_credentials_lookup_lock);
352
353 g_mutex_lock (&source->priv->last_values_lock);
354
355 /* Empty credentials are used to use the last credentials instead */
356 if (source->priv->last_credentials && arg_credentials && !arg_credentials[0]) {
357 last_credentials_strv = e_named_parameters_to_strv (source->priv->last_credentials);
358 arg_credentials = (const gchar * const *) last_credentials_strv;
359 } else if (arg_credentials && arg_credentials[0]) {
360 ENamedParameters *credentials = e_named_parameters_new_strv (arg_credentials);
361
362 /* If only one credential value is passed in, and it's the SSL Trust,
363 and there was any credentials already tried, then merge the previous
364 credentials with the SSL Trust, to inherit the password, if any. */
365 if (source->priv->last_credentials &&
366 e_named_parameters_count (credentials) == 1 &&
367 e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_SSL_TRUST)) {
368 gint ii, count;
369
370 count = e_named_parameters_count (source->priv->last_credentials);
371 for (ii = 0; ii < count; ii++) {
372 gchar *name;
373
374 name = e_named_parameters_get_name (source->priv->last_credentials, ii);
375 if (!name)
376 continue;
377
378 if (*name && !e_named_parameters_exists (credentials, name)) {
379 e_named_parameters_set (credentials, name,
380 e_named_parameters_get (source->priv->last_credentials, name));
381 }
382
383 g_free (name);
384 }
385
386 last_credentials_strv = e_named_parameters_to_strv (credentials);
387 arg_credentials = (const gchar * const *) last_credentials_strv;
388 }
389
390 e_named_parameters_free (source->priv->last_credentials);
391 source->priv->last_credentials = credentials;
392 }
393
394 g_free (source->priv->last_reason);
395 g_free (source->priv->last_certificate_pem);
396 g_free (source->priv->last_certificate_errors);
397 g_free (source->priv->last_dbus_error_name);
398 g_free (source->priv->last_dbus_error_message);
399 source->priv->last_reason = NULL;
400 source->priv->last_certificate_pem = NULL;
401 source->priv->last_certificate_errors = NULL;
402 source->priv->last_dbus_error_name = NULL;
403 source->priv->last_dbus_error_message = NULL;
404
405 g_mutex_unlock (&source->priv->last_values_lock);
406
407 if (invocation)
408 e_dbus_source_complete_invoke_authenticate (dbus_interface, invocation);
409
410 e_dbus_source_emit_authenticate (dbus_interface, arg_credentials);
411
412 g_strfreev (last_credentials_strv);
413
414 return TRUE;
415 }
416
417 static void
server_side_source_credentials_lookup_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)418 server_side_source_credentials_lookup_cb (GObject *source_object,
419 GAsyncResult *result,
420 gpointer user_data)
421 {
422 GDBusObject *dbus_object;
423 EDBusSource *dbus_source;
424 ReinvokeCredentialsRequiredData *data = user_data;
425 ENamedParameters *credentials = NULL;
426 gboolean success;
427 GError *error = NULL;
428
429 g_return_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (source_object));
430 g_return_if_fail (data != NULL);
431
432 success = e_source_credentials_provider_lookup_finish (E_SOURCE_CREDENTIALS_PROVIDER (source_object), result, &credentials, &error);
433
434 dbus_object = e_source_ref_dbus_object (E_SOURCE (data->source));
435 if (!dbus_object) {
436 e_named_parameters_free (credentials);
437 reinvoke_credentials_required_data_free (data);
438 return;
439 }
440
441 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
442 if (!dbus_source) {
443 e_named_parameters_free (credentials);
444 g_clear_object (&dbus_object);
445 reinvoke_credentials_required_data_free (data);
446 return;
447 }
448
449 if (success && credentials) {
450 gchar **arg_credentials;
451
452 arg_credentials = e_named_parameters_to_strv (credentials);
453
454 server_side_source_invoke_authenticate_cb (dbus_source, NULL,
455 (const gchar * const *) arg_credentials, data->source);
456
457 g_strfreev (arg_credentials);
458 } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
459 if (e_source_registry_debug_enabled ()) {
460 printf ("%s: Failed to lookup password for source %s (%s): %s\n", G_STRFUNC,
461 e_source_get_uid (E_SOURCE (data->source)),
462 e_source_get_display_name (E_SOURCE (data->source)),
463 error ? error->message : "Unknown error");
464 fflush (stdout);
465 }
466
467 g_prefix_error (&error, "%s", _("Failed to lookup credentials: "));
468
469 if (server_side_source_credentials_reason_from_text (data->arg_reason) == E_SOURCE_CREDENTIALS_REASON_REQUIRED &&
470 error && !e_source_credentials_provider_can_prompt (E_SOURCE_CREDENTIALS_PROVIDER (source_object), E_SOURCE (data->source))) {
471 GEnumClass *enum_class;
472 GEnumValue *enum_value;
473 gchar *dbus_error_name;
474
475 enum_class = g_type_class_ref (E_TYPE_SOURCE_CREDENTIALS_REASON);
476 enum_value = g_enum_get_value (enum_class, E_SOURCE_CREDENTIALS_REASON_ERROR);
477
478 g_return_if_fail (enum_value != NULL);
479
480 dbus_error_name = g_dbus_error_encode_gerror (error);
481
482 /* Use error reason when the source cannot be prompted for credentials */
483 e_dbus_source_emit_credentials_required (dbus_source, enum_value->value_nick,
484 data->arg_certificate_pem, data->arg_certificate_errors, dbus_error_name, error->message);
485
486 g_type_class_unref (enum_class);
487 g_free (dbus_error_name);
488 } else {
489 /* Reinvoke for clients only, if not cancelled */
490 const gchar *arg_dbus_error_name, *arg_dbus_error_message;
491 gchar *dbus_error_name = NULL;
492
493 arg_dbus_error_name = data->arg_dbus_error_name;
494 arg_dbus_error_message = data->arg_dbus_error_message;
495
496 if (!arg_dbus_error_name || !*arg_dbus_error_name) {
497 if (!error)
498 error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, _("Unknown error"));
499
500 dbus_error_name = g_dbus_error_encode_gerror (error);
501 arg_dbus_error_name = dbus_error_name;
502 arg_dbus_error_message = error->message;
503 }
504
505 e_dbus_source_emit_credentials_required (dbus_source, data->arg_reason,
506 data->arg_certificate_pem, data->arg_certificate_errors,
507 arg_dbus_error_name, arg_dbus_error_message);
508
509 g_free (dbus_error_name);
510 }
511 }
512
513 e_named_parameters_free (credentials);
514 reinvoke_credentials_required_data_free (data);
515 g_object_unref (dbus_source);
516 g_object_unref (dbus_object);
517 g_clear_error (&error);
518 }
519
520 static gboolean
server_side_source_get_last_credentials_required_arguments_cb(EDBusSource * dbus_interface,GDBusMethodInvocation * invocation,EServerSideSource * source)521 server_side_source_get_last_credentials_required_arguments_cb (EDBusSource *dbus_interface,
522 GDBusMethodInvocation *invocation,
523 EServerSideSource *source)
524 {
525 g_mutex_lock (&source->priv->last_values_lock);
526
527 e_dbus_source_complete_get_last_credentials_required_arguments (dbus_interface, invocation,
528 source->priv->last_reason ? source->priv->last_reason : "",
529 source->priv->last_certificate_pem ? source->priv->last_certificate_pem : "",
530 source->priv->last_certificate_errors ? source->priv->last_certificate_errors : "",
531 source->priv->last_dbus_error_name ? source->priv->last_dbus_error_name : "",
532 source->priv->last_dbus_error_message ? source->priv->last_dbus_error_message : "");
533
534 g_mutex_unlock (&source->priv->last_values_lock);
535
536 return TRUE;
537 }
538
539 static void
server_side_source_unset_last_credentials_required_arguments(EServerSideSource * source)540 server_side_source_unset_last_credentials_required_arguments (EServerSideSource *source)
541 {
542 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
543
544 g_mutex_lock (&source->priv->last_values_lock);
545
546 g_free (source->priv->last_reason);
547 g_free (source->priv->last_certificate_pem);
548 g_free (source->priv->last_certificate_errors);
549 g_free (source->priv->last_dbus_error_name);
550 g_free (source->priv->last_dbus_error_message);
551
552 source->priv->last_reason = NULL;
553 source->priv->last_certificate_pem = NULL;
554 source->priv->last_certificate_errors = NULL;
555 source->priv->last_dbus_error_name = NULL;
556 source->priv->last_dbus_error_message = NULL;
557
558 g_mutex_unlock (&source->priv->last_values_lock);
559 }
560
561 static gboolean
server_side_source_unset_last_credentials_required_arguments_cb(EDBusSource * dbus_interface,GDBusMethodInvocation * invocation,EServerSideSource * source)562 server_side_source_unset_last_credentials_required_arguments_cb (EDBusSource *dbus_interface,
563 GDBusMethodInvocation *invocation,
564 EServerSideSource *source)
565 {
566 server_side_source_unset_last_credentials_required_arguments (source);
567
568 e_dbus_source_complete_unset_last_credentials_required_arguments (dbus_interface, invocation);
569
570 return TRUE;
571 }
572
573 static gboolean
server_side_source_remove_cb(EDBusSourceRemovable * dbus_interface,GDBusMethodInvocation * invocation,EServerSideSource * source)574 server_side_source_remove_cb (EDBusSourceRemovable *dbus_interface,
575 GDBusMethodInvocation *invocation,
576 EServerSideSource *source)
577 {
578 GError *error = NULL;
579
580 /* Note we don't need to verify the source is removable here
581 * since if it isn't, the remove() method won't be available.
582 * Descendants of the source are removed whether they export
583 * a remove() method or not. */
584
585 e_source_remove_sync (E_SOURCE (source), NULL, &error);
586
587 if (error != NULL)
588 g_dbus_method_invocation_take_error (invocation, error);
589 else
590 e_dbus_source_removable_complete_remove (
591 dbus_interface, invocation);
592
593 return TRUE;
594 }
595
596 static gboolean
server_side_source_write_cb(EDBusSourceWritable * dbus_interface,GDBusMethodInvocation * invocation,const gchar * data,ESource * source)597 server_side_source_write_cb (EDBusSourceWritable *dbus_interface,
598 GDBusMethodInvocation *invocation,
599 const gchar *data,
600 ESource *source)
601 {
602 GKeyFile *key_file;
603 GDBusObject *dbus_object;
604 EDBusSource *dbus_source;
605 GError *error = NULL;
606
607 /* Note we don't need to verify the source is writable here
608 * since if it isn't, the write() method won't be available. */
609
610 dbus_object = e_source_ref_dbus_object (source);
611 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
612
613 /* Validate the raw data before making the changes live. */
614 key_file = g_key_file_new ();
615 server_side_source_parse_data (key_file, data, strlen (data), &error);
616 g_key_file_free (key_file);
617
618 /* Q: How does this trigger data being written to disk?
619 *
620 * A: Here's the sequence of events:
621 *
622 * 1) We set the EDBusSource:data property.
623 * 2) ESource picks up the "notify::data" signal and parses
624 * the raw data, which triggers an ESource:changed signal.
625 * 3) Our changed() method schedules an idle callback.
626 * 4) The idle callback calls e_source_write_sync().
627 * 5) e_source_write_sync() calls e_dbus_source_dup_data()
628 * and synchronously writes the resulting string to disk.
629 *
630 * XXX: This should be done more straigtforward, because rely
631 * on two different signals and having actual data file
632 * save in 5 steps is ridiculous, not talking that
633 * the returned GError from this D-Bus call doesn't handle
634 * errors from actual file save, which can also break, thus
635 * the caller doesn't know about any real problem during saving
636 * and thinks that everything went fine.
637 */
638
639 if (error == NULL) {
640 e_dbus_source_set_data (dbus_source, data);
641
642 /* Make sure the ESource::changed signal is called, otherwise
643 * the above Q&A doesn't work and changed data are not saved. */
644 e_source_changed (source);
645 }
646
647 if (error != NULL)
648 g_dbus_method_invocation_take_error (invocation, error);
649 else
650 e_dbus_source_writable_complete_write (
651 dbus_interface, invocation);
652
653 g_object_unref (dbus_source);
654 g_object_unref (dbus_object);
655
656 return TRUE;
657 }
658
659 /* Helper for server_side_source_remote_create_cb() */
660 static void
server_side_source_remote_create_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)661 server_side_source_remote_create_done_cb (GObject *source_object,
662 GAsyncResult *result,
663 gpointer user_data)
664 {
665 ESource *source;
666 AsyncContext *async_context;
667 GError *error = NULL;
668
669 source = E_SOURCE (source_object);
670 async_context = (AsyncContext *) user_data;
671
672 e_source_remote_create_finish (source, result, &error);
673
674 if (error != NULL)
675 g_dbus_method_invocation_take_error (
676 async_context->invocation, error);
677 else
678 e_dbus_source_remote_creatable_complete_create (
679 async_context->remote_creatable,
680 async_context->invocation);
681
682 async_context_free (async_context);
683 }
684
685 static gboolean
server_side_source_remote_create_cb(EDBusSourceRemoteCreatable * dbus_interface,GDBusMethodInvocation * invocation,const gchar * uid,const gchar * data,ESource * source)686 server_side_source_remote_create_cb (EDBusSourceRemoteCreatable *dbus_interface,
687 GDBusMethodInvocation *invocation,
688 const gchar *uid,
689 const gchar *data,
690 ESource *source)
691 {
692 EServerSideSource *server_side_source;
693 ESourceRegistryServer *server;
694 AsyncContext *async_context;
695 ESource *scratch_source;
696 GDBusObject *dbus_object;
697 EDBusSource *dbus_source;
698 GKeyFile *key_file;
699 GFile *file;
700 GError *error = NULL;
701
702 /* Create a new EServerSideSource from 'uid' and 'data' but
703 * DO NOT add it to the ESourceRegistryServer yet. It's up
704 * to the ECollectionBackend whether to use source as given
705 * or create its own equivalent EServerSideSource, possibly
706 * in response to a notification from a remote server. */
707
708 /* Validate the raw data. */
709 key_file = g_key_file_new ();
710 server_side_source_parse_data (key_file, data, strlen (data), &error);
711 g_key_file_free (key_file);
712
713 if (error != NULL) {
714 g_dbus_method_invocation_take_error (invocation, error);
715 return TRUE;
716 }
717
718 server_side_source = E_SERVER_SIDE_SOURCE (source);
719 server = e_server_side_source_get_server (server_side_source);
720
721 file = e_server_side_source_new_user_file (uid);
722 scratch_source = e_server_side_source_new (server, file, &error);
723 g_object_unref (file);
724
725 /* Sanity check. */
726 g_warn_if_fail (
727 ((scratch_source != NULL) && (error == NULL)) ||
728 ((scratch_source == NULL) && (error != NULL)));
729
730 if (error != NULL) {
731 g_dbus_method_invocation_take_error (invocation, error);
732 return TRUE;
733 }
734
735 dbus_object = e_source_ref_dbus_object (scratch_source);
736 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
737
738 e_dbus_source_set_data (dbus_source, data);
739
740 g_object_unref (dbus_object);
741 g_object_unref (dbus_source);
742
743 async_context = g_slice_new0 (AsyncContext);
744 async_context->remote_creatable = g_object_ref (dbus_interface);
745 async_context->invocation = g_object_ref (invocation);
746
747 e_source_remote_create (
748 source, scratch_source, NULL,
749 server_side_source_remote_create_done_cb,
750 async_context);
751
752 g_object_unref (scratch_source);
753
754 return TRUE;
755 }
756
757 /* Helper for server_side_source_remote_delete_cb() */
758 static void
server_side_source_remote_delete_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)759 server_side_source_remote_delete_done_cb (GObject *source_object,
760 GAsyncResult *result,
761 gpointer user_data)
762 {
763 ESource *source;
764 AsyncContext *async_context;
765 GError *error = NULL;
766
767 source = E_SOURCE (source_object);
768 async_context = (AsyncContext *) user_data;
769
770 e_source_remote_delete_finish (source, result, &error);
771
772 if (error != NULL)
773 g_dbus_method_invocation_take_error (
774 async_context->invocation, error);
775 else
776 e_dbus_source_remote_deletable_complete_delete (
777 async_context->remote_deletable,
778 async_context->invocation);
779
780 async_context_free (async_context);
781 }
782
783 static gboolean
server_side_source_remote_delete_cb(EDBusSourceRemoteDeletable * dbus_interface,GDBusMethodInvocation * invocation,ESource * source)784 server_side_source_remote_delete_cb (EDBusSourceRemoteDeletable *dbus_interface,
785 GDBusMethodInvocation *invocation,
786 ESource *source)
787 {
788 AsyncContext *async_context;
789
790 async_context = g_slice_new0 (AsyncContext);
791 async_context->remote_deletable = g_object_ref (dbus_interface);
792 async_context->invocation = g_object_ref (invocation);
793
794 e_source_remote_delete (
795 source, NULL,
796 server_side_source_remote_delete_done_cb,
797 async_context);
798
799 return TRUE;
800 }
801
802 /* Helper for server_side_source_get_access_token_cb() */
803 static void
server_side_source_get_access_token_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)804 server_side_source_get_access_token_done_cb (GObject *source_object,
805 GAsyncResult *result,
806 gpointer user_data)
807 {
808 ESource *source;
809 AsyncContext *async_context;
810 gchar *access_token = NULL;
811 gint expires_in = 0;
812 GError *error = NULL;
813
814 source = E_SOURCE (source_object);
815 async_context = (AsyncContext *) user_data;
816
817 e_source_get_oauth2_access_token_finish (
818 source, result, &access_token, &expires_in, &error);
819
820 /* Sanity check. */
821 g_return_if_fail (
822 ((access_token != NULL) && (error == NULL)) ||
823 ((access_token == NULL) && (error != NULL)));
824
825 if (error != NULL)
826 g_dbus_method_invocation_take_error (
827 async_context->invocation, error);
828 else
829 e_dbus_source_oauth2_support_complete_get_access_token (
830 async_context->oauth2_support,
831 async_context->invocation,
832 access_token, expires_in);
833
834 g_free (access_token);
835
836 async_context_free (async_context);
837 }
838
839 static gboolean
server_side_source_get_access_token_cb(EDBusSourceOAuth2Support * dbus_interface,GDBusMethodInvocation * invocation,ESource * source)840 server_side_source_get_access_token_cb (EDBusSourceOAuth2Support *dbus_interface,
841 GDBusMethodInvocation *invocation,
842 ESource *source)
843 {
844 AsyncContext *async_context;
845
846 async_context = g_slice_new0 (AsyncContext);
847 async_context->oauth2_support = g_object_ref (dbus_interface);
848 async_context->invocation = g_object_ref (invocation);
849
850 e_source_get_oauth2_access_token (
851 source, NULL,
852 server_side_source_get_access_token_done_cb,
853 async_context);
854
855 return TRUE;
856 }
857
858 static void
server_side_source_set_file(EServerSideSource * source,GFile * file)859 server_side_source_set_file (EServerSideSource *source,
860 GFile *file)
861 {
862 g_return_if_fail (file == NULL || G_IS_FILE (file));
863 g_return_if_fail (source->priv->file == NULL);
864
865 if (file != NULL)
866 source->priv->file = g_object_ref (file);
867 }
868
869 static void
server_side_source_set_server(EServerSideSource * source,ESourceRegistryServer * server)870 server_side_source_set_server (EServerSideSource *source,
871 ESourceRegistryServer *server)
872 {
873 g_return_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server));
874 g_return_if_fail (source->priv->server == NULL);
875
876 source->priv->server = server;
877
878 g_object_add_weak_pointer (
879 G_OBJECT (server), &source->priv->server);
880 }
881
882 static void
server_side_source_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)883 server_side_source_set_property (GObject *object,
884 guint property_id,
885 const GValue *value,
886 GParamSpec *pspec)
887 {
888 switch (property_id) {
889 case PROP_FILE:
890 server_side_source_set_file (
891 E_SERVER_SIDE_SOURCE (object),
892 g_value_get_object (value));
893 return;
894
895 case PROP_OAUTH2_SUPPORT:
896 e_server_side_source_set_oauth2_support (
897 E_SERVER_SIDE_SOURCE (object),
898 g_value_get_object (value));
899 return;
900
901 case PROP_REMOTE_CREATABLE:
902 e_server_side_source_set_remote_creatable (
903 E_SERVER_SIDE_SOURCE (object),
904 g_value_get_boolean (value));
905 return;
906
907 case PROP_REMOTE_DELETABLE:
908 e_server_side_source_set_remote_deletable (
909 E_SERVER_SIDE_SOURCE (object),
910 g_value_get_boolean (value));
911 return;
912
913 case PROP_REMOVABLE:
914 e_server_side_source_set_removable (
915 E_SERVER_SIDE_SOURCE (object),
916 g_value_get_boolean (value));
917 return;
918
919 case PROP_SERVER:
920 server_side_source_set_server (
921 E_SERVER_SIDE_SOURCE (object),
922 g_value_get_object (value));
923 return;
924
925 case PROP_WRITABLE:
926 e_server_side_source_set_writable (
927 E_SERVER_SIDE_SOURCE (object),
928 g_value_get_boolean (value));
929 return;
930
931 case PROP_WRITE_DIRECTORY:
932 e_server_side_source_set_write_directory (
933 E_SERVER_SIDE_SOURCE (object),
934 g_value_get_string (value));
935 return;
936 }
937
938 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
939 }
940
941 static void
server_side_source_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)942 server_side_source_get_property (GObject *object,
943 guint property_id,
944 GValue *value,
945 GParamSpec *pspec)
946 {
947 switch (property_id) {
948 case PROP_EXPORTED:
949 g_value_set_boolean (
950 value,
951 e_server_side_source_get_exported (
952 E_SERVER_SIDE_SOURCE (object)));
953 return;
954
955 case PROP_FILE:
956 g_value_set_object (
957 value,
958 e_server_side_source_get_file (
959 E_SERVER_SIDE_SOURCE (object)));
960 return;
961
962 case PROP_OAUTH2_SUPPORT:
963 g_value_take_object (
964 value,
965 e_server_side_source_ref_oauth2_support (
966 E_SERVER_SIDE_SOURCE (object)));
967 return;
968
969 case PROP_REMOTE_CREATABLE:
970 g_value_set_boolean (
971 value,
972 e_source_get_remote_creatable (
973 E_SOURCE (object)));
974 return;
975
976 case PROP_REMOTE_DELETABLE:
977 g_value_set_boolean (
978 value,
979 e_source_get_remote_deletable (
980 E_SOURCE (object)));
981 return;
982
983 case PROP_REMOVABLE:
984 g_value_set_boolean (
985 value,
986 e_source_get_removable (
987 E_SOURCE (object)));
988 return;
989
990 case PROP_SERVER:
991 g_value_set_object (
992 value,
993 e_server_side_source_get_server (
994 E_SERVER_SIDE_SOURCE (object)));
995 return;
996
997 case PROP_WRITABLE:
998 g_value_set_boolean (
999 value,
1000 e_source_get_writable (
1001 E_SOURCE (object)));
1002 return;
1003
1004 case PROP_WRITE_DIRECTORY:
1005 g_value_set_string (
1006 value,
1007 e_server_side_source_get_write_directory (
1008 E_SERVER_SIDE_SOURCE (object)));
1009 return;
1010 }
1011
1012 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1013 }
1014
1015 static void
server_side_source_dispose(GObject * object)1016 server_side_source_dispose (GObject *object)
1017 {
1018 EServerSideSourcePrivate *priv;
1019
1020 priv = E_SERVER_SIDE_SOURCE (object)->priv;
1021
1022 g_mutex_lock (&priv->last_values_lock);
1023
1024 g_free (priv->last_reason);
1025 g_free (priv->last_certificate_pem);
1026 g_free (priv->last_certificate_errors);
1027 g_free (priv->last_dbus_error_name);
1028 g_free (priv->last_dbus_error_message);
1029 priv->last_reason = NULL;
1030 priv->last_certificate_pem = NULL;
1031 priv->last_certificate_errors = NULL;
1032 priv->last_dbus_error_name = NULL;
1033 priv->last_dbus_error_message = NULL;
1034
1035 e_named_parameters_free (priv->last_credentials);
1036 priv->last_credentials = NULL;
1037
1038 g_mutex_unlock (&priv->last_values_lock);
1039
1040 g_mutex_lock (&priv->pending_credentials_lookup_lock);
1041 if (priv->pending_credentials_lookup) {
1042 g_cancellable_cancel (priv->pending_credentials_lookup);
1043 g_clear_object (&priv->pending_credentials_lookup);
1044 }
1045 g_mutex_unlock (&priv->pending_credentials_lookup_lock);
1046
1047 if (priv->server != NULL) {
1048 g_object_remove_weak_pointer (
1049 G_OBJECT (priv->server), &priv->server);
1050 priv->server = NULL;
1051 }
1052
1053 g_weak_ref_set (&priv->oauth2_support, NULL);
1054
1055 g_clear_object (&priv->file);
1056
1057 /* Chain up to parent's dispose() method. */
1058 G_OBJECT_CLASS (e_server_side_source_parent_class)->dispose (object);
1059 }
1060
1061 static void
server_side_source_finalize(GObject * object)1062 server_side_source_finalize (GObject *object)
1063 {
1064 EServerSideSourcePrivate *priv;
1065
1066 priv = E_SERVER_SIDE_SOURCE (object)->priv;
1067
1068 g_node_unlink (&priv->node);
1069
1070 g_free (priv->file_contents);
1071 g_free (priv->write_directory);
1072
1073 g_weak_ref_clear (&priv->oauth2_support);
1074 g_mutex_clear (&priv->last_values_lock);
1075 g_mutex_clear (&priv->pending_credentials_lookup_lock);
1076
1077 /* Chain up to parent's finalize() method. */
1078 G_OBJECT_CLASS (e_server_side_source_parent_class)->finalize (object);
1079 }
1080
1081 static void
server_side_source_changed(ESource * source)1082 server_side_source_changed (ESource *source)
1083 {
1084 GDBusObject *dbus_object;
1085 EDBusSource *dbus_source;
1086 gchar *old_data;
1087 gchar *new_data;
1088 GError *error = NULL;
1089
1090 /* Do not write changes to disk until the source has been exported. */
1091 if (!e_server_side_source_get_exported (E_SERVER_SIDE_SOURCE (source)))
1092 return;
1093
1094 dbus_object = e_source_ref_dbus_object (source);
1095 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
1096
1097 old_data = e_dbus_source_dup_data (dbus_source);
1098 new_data = e_source_to_string (source, NULL);
1099
1100 /* Setting the "data" property triggers the ESource::changed,
1101 * signal, which invokes this callback, which sets the "data"
1102 * property, etc. This breaks an otherwise infinite loop. */
1103 if (g_strcmp0 (old_data, new_data) != 0)
1104 e_dbus_source_set_data (dbus_source, new_data);
1105
1106 g_free (old_data);
1107 g_free (new_data);
1108
1109 g_object_unref (dbus_source);
1110 g_object_unref (dbus_object);
1111
1112 /* This writes the "data" property to disk. */
1113 e_source_write_sync (source, NULL, &error);
1114
1115 if (error != NULL) {
1116 g_warning ("%s: %s", G_STRFUNC, error->message);
1117 g_error_free (error);
1118 }
1119 }
1120
1121 static gboolean
server_side_source_remove_sync(ESource * source,GCancellable * cancellable,GError ** error)1122 server_side_source_remove_sync (ESource *source,
1123 GCancellable *cancellable,
1124 GError **error)
1125 {
1126 EAsyncClosure *closure;
1127 GAsyncResult *result;
1128 gboolean success;
1129
1130 closure = e_async_closure_new ();
1131
1132 e_source_remove (
1133 source, cancellable, e_async_closure_callback, closure);
1134
1135 result = e_async_closure_wait (closure);
1136
1137 success = e_source_remove_finish (source, result, error);
1138
1139 e_async_closure_free (closure);
1140
1141 return success;
1142 }
1143
1144 static void
server_side_source_remove(ESource * source,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1145 server_side_source_remove (ESource *source,
1146 GCancellable *cancellable,
1147 GAsyncReadyCallback callback,
1148 gpointer user_data)
1149 {
1150 EServerSideSourcePrivate *priv;
1151 GSimpleAsyncResult *simple;
1152 ESourceRegistryServer *server;
1153 GQueue queue = G_QUEUE_INIT;
1154 GList *list, *link;
1155 GError *error = NULL;
1156
1157 /* XXX Yes we block here. We do this operation
1158 * synchronously to keep the server code simple. */
1159
1160 priv = E_SERVER_SIDE_SOURCE (source)->priv;
1161
1162 simple = g_simple_async_result_new (
1163 G_OBJECT (source), callback, user_data,
1164 server_side_source_remove);
1165
1166 g_simple_async_result_set_check_cancellable (simple, cancellable);
1167
1168 /* Collect the source and its descendants into a queue.
1169 * Do this before unexporting so we hold references to
1170 * all the removed sources. */
1171 g_node_traverse (
1172 &priv->node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
1173 (GNodeTraverseFunc) server_side_source_traverse_cb, &queue);
1174
1175 /* Unexport the object and its descendants. */
1176 server = E_SOURCE_REGISTRY_SERVER (priv->server);
1177 e_source_registry_server_remove_source (server, source);
1178
1179 list = g_queue_peek_head_link (&queue);
1180
1181 /* Delete the key file for each source in the queue. */
1182 for (link = list; link != NULL; link = g_list_next (link)) {
1183 EServerSideSource *child;
1184 GFile *file;
1185
1186 child = E_SERVER_SIDE_SOURCE (link->data);
1187 file = e_server_side_source_get_file (child);
1188
1189 if (file != NULL)
1190 g_file_delete (file, cancellable, &error);
1191
1192 /* XXX Even though e_source_registry_server_remove_source()
1193 * is called first, the object path is unexported from
1194 * an idle callback some time after we have deleted the
1195 * key file. That creates a small window of time where
1196 * the file is deleted but the object is still exported.
1197 *
1198 * If a client calls e_source_remove() during that small
1199 * window of time, we still want to report a successful
1200 * removal, so disregard G_IO_ERROR_NOT_FOUND. */
1201 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
1202 g_clear_error (&error);
1203
1204 if (error != NULL)
1205 goto exit;
1206 }
1207
1208 exit:
1209 while (!g_queue_is_empty (&queue))
1210 g_object_unref (g_queue_pop_head (&queue));
1211
1212 if (error != NULL)
1213 g_simple_async_result_take_error (simple, error);
1214
1215 g_simple_async_result_complete_in_idle (simple);
1216 g_object_unref (simple);
1217 }
1218
1219 static gboolean
server_side_source_remove_finish(ESource * source,GAsyncResult * result,GError ** error)1220 server_side_source_remove_finish (ESource *source,
1221 GAsyncResult *result,
1222 GError **error)
1223 {
1224 GSimpleAsyncResult *simple;
1225
1226 g_return_val_if_fail (
1227 g_simple_async_result_is_valid (
1228 result, G_OBJECT (source),
1229 server_side_source_remove), FALSE);
1230
1231 simple = G_SIMPLE_ASYNC_RESULT (result);
1232
1233 /* Assume success unless a GError is set. */
1234 return !g_simple_async_result_propagate_error (simple, error);
1235 }
1236
1237 static gboolean
server_side_source_write_sync(ESource * source,GCancellable * cancellable,GError ** error)1238 server_side_source_write_sync (ESource *source,
1239 GCancellable *cancellable,
1240 GError **error)
1241 {
1242 EAsyncClosure *closure;
1243 GAsyncResult *result;
1244 gboolean success;
1245
1246 closure = e_async_closure_new ();
1247
1248 e_source_write (
1249 source, cancellable, e_async_closure_callback, closure);
1250
1251 result = e_async_closure_wait (closure);
1252
1253 success = e_source_write_finish (source, result, error);
1254
1255 e_async_closure_free (closure);
1256
1257 return success;
1258 }
1259
1260 static void
server_side_source_write(ESource * source,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1261 server_side_source_write (ESource *source,
1262 GCancellable *cancellable,
1263 GAsyncReadyCallback callback,
1264 gpointer user_data)
1265 {
1266 EServerSideSourcePrivate *priv;
1267 GSimpleAsyncResult *simple;
1268 GDBusObject *dbus_object;
1269 EDBusSource *dbus_source;
1270 gboolean replace_file;
1271 const gchar *old_data;
1272 gchar *new_data;
1273 GError *error = NULL;
1274
1275 /* XXX Yes we block here. We do this operation
1276 * synchronously to keep the server code simple. */
1277
1278 priv = E_SERVER_SIDE_SOURCE (source)->priv;
1279
1280 simple = g_simple_async_result_new (
1281 G_OBJECT (source), callback, user_data,
1282 server_side_source_write);
1283
1284 g_simple_async_result_set_check_cancellable (simple, cancellable);
1285
1286 dbus_object = e_source_ref_dbus_object (source);
1287 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
1288
1289 old_data = priv->file_contents;
1290 new_data = e_source_to_string (source, NULL);
1291
1292 /* When writing source data to disk, we always write to the
1293 * source's specified "write-directory" even if the key file
1294 * was originally read from a different directory. To avoid
1295 * polluting the write directory with key files identical to
1296 * the original, we check that the data has actually changed
1297 * before writing a copy to disk. */
1298
1299 replace_file =
1300 G_IS_FILE (priv->file) &&
1301 (g_strcmp0 (old_data, new_data) != 0);
1302
1303 if (replace_file) {
1304 GFile *file;
1305 GFile *write_directory;
1306 gchar *basename;
1307
1308 g_warn_if_fail (priv->write_directory != NULL);
1309
1310 basename = g_file_get_basename (priv->file);
1311 write_directory = g_file_new_for_path (priv->write_directory);
1312 file = g_file_get_child (write_directory, basename);
1313 g_free (basename);
1314
1315 if (!g_file_equal (file, priv->file)) {
1316 g_object_unref (priv->file);
1317 priv->file = g_object_ref (file);
1318 }
1319
1320 server_side_source_print_diff (source, old_data, new_data);
1321
1322 g_file_make_directory_with_parents (
1323 write_directory, cancellable, &error);
1324
1325 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
1326 g_clear_error (&error);
1327
1328 if (error == NULL)
1329 g_file_replace_contents (
1330 file, new_data, strlen (new_data),
1331 NULL, FALSE, G_FILE_CREATE_NONE,
1332 NULL, cancellable, &error);
1333
1334 if (error == NULL) {
1335 g_free (priv->file_contents);
1336 priv->file_contents = new_data;
1337 new_data = NULL;
1338 }
1339
1340 g_object_unref (write_directory);
1341 g_object_unref (file);
1342 }
1343
1344 g_free (new_data);
1345
1346 g_object_unref (dbus_source);
1347 g_object_unref (dbus_object);
1348
1349 if (error != NULL)
1350 g_simple_async_result_take_error (simple, error);
1351
1352 g_simple_async_result_complete_in_idle (simple);
1353 g_object_unref (simple);
1354 }
1355
1356 static gboolean
server_side_source_write_finish(ESource * source,GAsyncResult * result,GError ** error)1357 server_side_source_write_finish (ESource *source,
1358 GAsyncResult *result,
1359 GError **error)
1360 {
1361 GSimpleAsyncResult *simple;
1362
1363 g_return_val_if_fail (
1364 g_simple_async_result_is_valid (
1365 result, G_OBJECT (source),
1366 server_side_source_write), FALSE);
1367
1368 simple = G_SIMPLE_ASYNC_RESULT (result);
1369
1370 /* Assume success unless a GError is set. */
1371 return !g_simple_async_result_propagate_error (simple, error);
1372 }
1373
1374 static gboolean
server_side_source_remote_create_sync(ESource * source,ESource * scratch_source,GCancellable * cancellable,GError ** error)1375 server_side_source_remote_create_sync (ESource *source,
1376 ESource *scratch_source,
1377 GCancellable *cancellable,
1378 GError **error)
1379 {
1380 ECollectionBackend *backend;
1381 ESourceRegistryServer *server;
1382 EServerSideSource *server_side_source;
1383 gboolean success;
1384
1385 if (!e_source_get_remote_creatable (source)) {
1386 g_set_error (
1387 error, G_IO_ERROR,
1388 G_IO_ERROR_NOT_SUPPORTED,
1389 _("Data source “%s” does not "
1390 "support creating remote resources"),
1391 e_source_get_display_name (source));
1392 return FALSE;
1393 }
1394
1395 server_side_source = E_SERVER_SIDE_SOURCE (source);
1396 server = e_server_side_source_get_server (server_side_source);
1397 backend = e_source_registry_server_ref_backend (server, source);
1398
1399 if (backend == NULL) {
1400 g_set_error (
1401 error, G_IO_ERROR,
1402 G_IO_ERROR_NOT_SUPPORTED,
1403 _("Data source “%s” has no collection "
1404 "backend to create the remote resource"),
1405 e_source_get_display_name (source));
1406 return FALSE;
1407 }
1408
1409 success = e_collection_backend_create_resource_sync (
1410 backend, scratch_source, cancellable, error);
1411
1412 g_object_unref (backend);
1413
1414 return success;
1415 }
1416
1417 static gboolean
server_side_source_remote_delete_sync(ESource * source,GCancellable * cancellable,GError ** error)1418 server_side_source_remote_delete_sync (ESource *source,
1419 GCancellable *cancellable,
1420 GError **error)
1421 {
1422 ECollectionBackend *backend;
1423 ESourceRegistryServer *server;
1424 EServerSideSource *server_side_source;
1425 gboolean success;
1426
1427 if (!e_source_get_remote_deletable (source)) {
1428 g_set_error (
1429 error, G_IO_ERROR,
1430 G_IO_ERROR_NOT_SUPPORTED,
1431 _("Data source “%s” does not "
1432 "support deleting remote resources"),
1433 e_source_get_display_name (source));
1434 return FALSE;
1435 }
1436
1437 server_side_source = E_SERVER_SIDE_SOURCE (source);
1438 server = e_server_side_source_get_server (server_side_source);
1439 backend = e_source_registry_server_ref_backend (server, source);
1440
1441 if (backend == NULL) {
1442 g_set_error (
1443 error, G_IO_ERROR,
1444 G_IO_ERROR_NOT_SUPPORTED,
1445 _("Data source “%s” has no collection "
1446 "backend to delete the remote resource"),
1447 e_source_get_display_name (source));
1448 return FALSE;
1449 }
1450
1451 success = e_collection_backend_delete_resource_sync (
1452 backend, source, cancellable, error);
1453
1454 g_object_unref (backend);
1455
1456 return success;
1457 }
1458
1459 static gboolean
server_side_source_get_oauth2_access_token_sync(ESource * source,GCancellable * cancellable,gchar ** out_access_token,gint * out_expires_in,GError ** error)1460 server_side_source_get_oauth2_access_token_sync (ESource *source,
1461 GCancellable *cancellable,
1462 gchar **out_access_token,
1463 gint *out_expires_in,
1464 GError **error)
1465 {
1466 EOAuth2Support *oauth2_support;
1467 gboolean success;
1468
1469 oauth2_support = e_server_side_source_ref_oauth2_support (
1470 E_SERVER_SIDE_SOURCE (source));
1471
1472 if (oauth2_support == NULL) {
1473 ESourceRegistryServer *server;
1474
1475 server = e_server_side_source_get_server (E_SERVER_SIDE_SOURCE (source));
1476
1477 if (server)
1478 oauth2_support = e_source_registry_server_ref_oauth2_support (server);
1479
1480 if (!oauth2_support) {
1481 g_set_error (
1482 error, G_IO_ERROR,
1483 G_IO_ERROR_NOT_SUPPORTED,
1484 _("Data source “%s” does not "
1485 "support OAuth 2.0 authentication"),
1486 e_source_get_display_name (source));
1487 return FALSE;
1488 }
1489 }
1490
1491 success = e_oauth2_support_get_access_token_sync (
1492 oauth2_support, source, cancellable,
1493 out_access_token, out_expires_in, error);
1494
1495 g_object_unref (oauth2_support);
1496
1497 return success;
1498 }
1499
1500 static gboolean
server_side_source_invoke_credentials_required_impl(ESource * source,gpointer dbus_source,const gchar * arg_reason,const gchar * arg_certificate_pem,const gchar * arg_certificate_errors,const gchar * arg_dbus_error_name,const gchar * arg_dbus_error_message,GCancellable * cancellable,GError ** error)1501 server_side_source_invoke_credentials_required_impl (ESource *source,
1502 gpointer dbus_source, /* EDBusSource * */
1503 const gchar *arg_reason,
1504 const gchar *arg_certificate_pem,
1505 const gchar *arg_certificate_errors,
1506 const gchar *arg_dbus_error_name,
1507 const gchar *arg_dbus_error_message,
1508 GCancellable *cancellable,
1509 GError **error)
1510 {
1511 g_return_val_if_fail (E_DBUS_IS_SOURCE (dbus_source), FALSE);
1512
1513 return server_side_source_invoke_credentials_required_cb (dbus_source, NULL,
1514 arg_reason ? arg_reason : "",
1515 arg_certificate_pem ? arg_certificate_pem : "",
1516 arg_certificate_errors ? arg_certificate_errors : "",
1517 arg_dbus_error_name ? arg_dbus_error_name : "",
1518 arg_dbus_error_message ? arg_dbus_error_message : "",
1519 E_SERVER_SIDE_SOURCE (source));
1520 }
1521
1522 static gboolean
server_side_source_invoke_authenticate_impl(ESource * source,gpointer dbus_source,const gchar * const * arg_credentials,GCancellable * cancellable,GError ** error)1523 server_side_source_invoke_authenticate_impl (ESource *source,
1524 gpointer dbus_source, /* EDBusSource * */
1525 const gchar * const *arg_credentials,
1526 GCancellable *cancellable,
1527 GError **error)
1528 {
1529 g_return_val_if_fail (E_DBUS_IS_SOURCE (dbus_source), FALSE);
1530
1531 return server_side_source_invoke_authenticate_cb (dbus_source, NULL,
1532 arg_credentials, E_SERVER_SIDE_SOURCE (source));
1533 }
1534
1535 static gboolean
server_side_source_unset_last_credentials_required_arguments_impl(ESource * source,GCancellable * cancellable,GError ** error)1536 server_side_source_unset_last_credentials_required_arguments_impl (ESource *source,
1537 GCancellable *cancellable,
1538 GError **error)
1539 {
1540 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
1541
1542 server_side_source_unset_last_credentials_required_arguments (E_SERVER_SIDE_SOURCE (source));
1543
1544 return TRUE;
1545 }
1546
1547 static gboolean
server_side_source_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)1548 server_side_source_initable_init (GInitable *initable,
1549 GCancellable *cancellable,
1550 GError **error)
1551 {
1552 EServerSideSource *source;
1553 GDBusObject *dbus_object;
1554 EDBusSource *dbus_source;
1555 gchar *uid;
1556
1557 source = E_SERVER_SIDE_SOURCE (initable);
1558
1559 dbus_source = e_dbus_source_skeleton_new ();
1560
1561 uid = e_source_dup_uid (E_SOURCE (source));
1562 if (uid == NULL)
1563 uid = e_util_generate_uid ();
1564 e_dbus_source_set_uid (dbus_source, uid);
1565 g_free (uid);
1566
1567 dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
1568 e_dbus_object_skeleton_set_source (
1569 E_DBUS_OBJECT_SKELETON (dbus_object), dbus_source);
1570 g_object_unref (dbus_object);
1571
1572 g_signal_connect (
1573 dbus_source, "handle-invoke-credentials-required",
1574 G_CALLBACK (server_side_source_invoke_credentials_required_cb), source);
1575 g_signal_connect (
1576 dbus_source, "handle-invoke-authenticate",
1577 G_CALLBACK (server_side_source_invoke_authenticate_cb), source);
1578 g_signal_connect (
1579 dbus_source, "handle-get-last-credentials-required-arguments",
1580 G_CALLBACK (server_side_source_get_last_credentials_required_arguments_cb), source);
1581 g_signal_connect (
1582 dbus_source, "handle-unset-last-credentials-required-arguments",
1583 G_CALLBACK (server_side_source_unset_last_credentials_required_arguments_cb), source);
1584
1585 g_object_unref (dbus_source);
1586
1587 if (!e_server_side_source_load (source, cancellable, error))
1588 return FALSE;
1589
1590 /* Chain up to parent interface's init() method. */
1591 return initable_parent_interface->init (initable, cancellable, error);
1592 }
1593
1594 static void
e_server_side_source_class_init(EServerSideSourceClass * class)1595 e_server_side_source_class_init (EServerSideSourceClass *class)
1596 {
1597 GObjectClass *object_class;
1598 ESourceClass *source_class;
1599
1600 object_class = G_OBJECT_CLASS (class);
1601 object_class->set_property = server_side_source_set_property;
1602 object_class->get_property = server_side_source_get_property;
1603 object_class->dispose = server_side_source_dispose;
1604 object_class->finalize = server_side_source_finalize;
1605
1606 source_class = E_SOURCE_CLASS (class);
1607 source_class->changed = server_side_source_changed;
1608 source_class->remove_sync = server_side_source_remove_sync;
1609 source_class->remove = server_side_source_remove;
1610 source_class->remove_finish = server_side_source_remove_finish;
1611 source_class->write_sync = server_side_source_write_sync;
1612 source_class->write = server_side_source_write;
1613 source_class->write_finish = server_side_source_write_finish;
1614 source_class->remote_create_sync = server_side_source_remote_create_sync;
1615 source_class->remote_delete_sync = server_side_source_remote_delete_sync;
1616 source_class->get_oauth2_access_token_sync = server_side_source_get_oauth2_access_token_sync;
1617 source_class->invoke_credentials_required_impl = server_side_source_invoke_credentials_required_impl;
1618 source_class->invoke_authenticate_impl = server_side_source_invoke_authenticate_impl;
1619 source_class->unset_last_credentials_required_arguments_impl = server_side_source_unset_last_credentials_required_arguments_impl;
1620
1621 g_object_class_install_property (
1622 object_class,
1623 PROP_EXPORTED,
1624 g_param_spec_boolean (
1625 "exported",
1626 "Exported",
1627 "Whether the source has been exported over D-Bus",
1628 FALSE,
1629 G_PARAM_READABLE |
1630 G_PARAM_STATIC_STRINGS));
1631
1632 g_object_class_install_property (
1633 object_class,
1634 PROP_FILE,
1635 g_param_spec_object (
1636 "file",
1637 "File",
1638 "The key file for the data source",
1639 G_TYPE_FILE,
1640 G_PARAM_READWRITE |
1641 G_PARAM_CONSTRUCT_ONLY |
1642 G_PARAM_STATIC_STRINGS));
1643
1644 g_object_class_install_property (
1645 object_class,
1646 PROP_OAUTH2_SUPPORT,
1647 g_param_spec_object (
1648 "oauth2-support",
1649 "OAuth2 Support",
1650 "The object providing OAuth 2.0 support",
1651 E_TYPE_OAUTH2_SUPPORT,
1652 G_PARAM_READWRITE |
1653 G_PARAM_EXPLICIT_NOTIFY |
1654 G_PARAM_STATIC_STRINGS));
1655
1656 /* This overrides the "remote-creatable" property
1657 * in ESourceClass with a writable version. */
1658 g_object_class_install_property (
1659 object_class,
1660 PROP_REMOTE_CREATABLE,
1661 g_param_spec_boolean (
1662 "remote-creatable",
1663 "Remote Creatable",
1664 "Whether the data source "
1665 "can create remote resources",
1666 FALSE,
1667 G_PARAM_READWRITE |
1668 G_PARAM_EXPLICIT_NOTIFY |
1669 G_PARAM_STATIC_STRINGS));
1670
1671 /* This overrides the "remote-deletable" property
1672 * in ESourceClass with a writable version. */
1673 g_object_class_install_property (
1674 object_class,
1675 PROP_REMOTE_DELETABLE,
1676 g_param_spec_boolean (
1677 "remote-deletable",
1678 "Remote Deletable",
1679 "Whether the data source "
1680 "can delete remote resources",
1681 FALSE,
1682 G_PARAM_READWRITE |
1683 G_PARAM_EXPLICIT_NOTIFY |
1684 G_PARAM_STATIC_STRINGS));
1685
1686 /* This overrides the "removable" property
1687 * in ESourceClass with a writable version. */
1688 g_object_class_install_property (
1689 object_class,
1690 PROP_REMOVABLE,
1691 g_param_spec_boolean (
1692 "removable",
1693 "Removable",
1694 "Whether the data source is removable",
1695 FALSE,
1696 G_PARAM_READWRITE |
1697 G_PARAM_EXPLICIT_NOTIFY |
1698 G_PARAM_STATIC_STRINGS));
1699
1700 g_object_class_install_property (
1701 object_class,
1702 PROP_SERVER,
1703 g_param_spec_object (
1704 "server",
1705 "Server",
1706 "The server to which the data source belongs",
1707 E_TYPE_SOURCE_REGISTRY_SERVER,
1708 G_PARAM_READWRITE |
1709 G_PARAM_CONSTRUCT_ONLY |
1710 G_PARAM_STATIC_STRINGS));
1711
1712 /* This overrides the "writable" property
1713 * in ESourceClass with a writable version. */
1714 g_object_class_install_property (
1715 object_class,
1716 PROP_WRITABLE,
1717 g_param_spec_boolean (
1718 "writable",
1719 "Writable",
1720 "Whether the data source is writable",
1721 FALSE,
1722 G_PARAM_READWRITE |
1723 G_PARAM_EXPLICIT_NOTIFY |
1724 G_PARAM_STATIC_STRINGS));
1725
1726 /* Do not use G_PARAM_CONSTRUCT. We initialize the
1727 * property ourselves in e_server_side_source_init(). */
1728 g_object_class_install_property (
1729 object_class,
1730 PROP_WRITE_DIRECTORY,
1731 g_param_spec_string (
1732 "write-directory",
1733 "Write Directory",
1734 "Directory in which to write changes to disk",
1735 NULL,
1736 G_PARAM_READWRITE |
1737 G_PARAM_EXPLICIT_NOTIFY |
1738 G_PARAM_STATIC_STRINGS));
1739 }
1740
1741 static void
e_server_side_source_initable_init(GInitableIface * iface)1742 e_server_side_source_initable_init (GInitableIface *iface)
1743 {
1744 initable_parent_interface = g_type_interface_peek_parent (iface);
1745
1746 iface->init = server_side_source_initable_init;
1747 }
1748
1749 static void
e_server_side_source_init(EServerSideSource * source)1750 e_server_side_source_init (EServerSideSource *source)
1751 {
1752 const gchar *user_dir;
1753
1754 source->priv = e_server_side_source_get_instance_private (source);
1755
1756 source->priv->node.data = source;
1757
1758 user_dir = e_server_side_source_get_user_dir ();
1759 source->priv->write_directory = g_strdup (user_dir);
1760
1761 g_weak_ref_init (&source->priv->oauth2_support, NULL);
1762 g_mutex_init (&source->priv->last_values_lock);
1763 g_mutex_init (&source->priv->pending_credentials_lookup_lock);
1764 }
1765
1766 /**
1767 * e_server_side_source_get_user_dir:
1768 *
1769 * Returns the directory where user-specific data source files are stored.
1770 *
1771 * Returns: the user-specific data source directory
1772 *
1773 * Since: 3.6
1774 **/
1775 const gchar *
e_server_side_source_get_user_dir(void)1776 e_server_side_source_get_user_dir (void)
1777 {
1778 static gchar *dirname = NULL;
1779
1780 if (G_UNLIKELY (dirname == NULL)) {
1781 const gchar *config_dir = e_get_user_config_dir ();
1782 dirname = g_build_filename (config_dir, "sources", NULL);
1783 g_mkdir_with_parents (dirname, 0700);
1784 }
1785
1786 return dirname;
1787 }
1788
1789 /**
1790 * e_server_side_source_new_user_file:
1791 * @uid: unique identifier for a data source, or %NULL
1792 *
1793 * Generates a unique file name for a new user-specific data source.
1794 * If @uid is non-%NULL it will be used in the basename of the file,
1795 * otherwise a unique basename will be generated using e_util_generate_uid().
1796 *
1797 * The returned #GFile can then be passed to e_server_side_source_new().
1798 * Unreference the #GFile with g_object_unref() when finished with it.
1799 *
1800 * Note the data source file itself is not created here, only its name.
1801 *
1802 * Returns: (transfer full): the #GFile for a new data source
1803 *
1804 * Since: 3.6
1805 **/
1806 GFile *
e_server_side_source_new_user_file(const gchar * uid)1807 e_server_side_source_new_user_file (const gchar *uid)
1808 {
1809 GFile *file;
1810 gchar *safe_uid;
1811 gchar *basename;
1812 gchar *filename;
1813 const gchar *user_dir;
1814
1815 if (uid == NULL)
1816 safe_uid = e_util_generate_uid ();
1817 else
1818 safe_uid = g_strdup (uid);
1819 e_filename_make_safe (safe_uid);
1820
1821 user_dir = e_server_side_source_get_user_dir ();
1822 basename = g_strconcat (safe_uid, ".source", NULL);
1823 filename = g_build_filename (user_dir, basename, NULL);
1824
1825 file = g_file_new_for_path (filename);
1826
1827 g_free (basename);
1828 g_free (filename);
1829 g_free (safe_uid);
1830
1831 return file;
1832 }
1833
1834 /**
1835 * e_server_side_source_uid_from_file:
1836 * @file: a #GFile for a data source
1837 * @error: return location for a #GError, or %NULL
1838 *
1839 * Extracts a unique identity string from the base name of @file.
1840 * If the base name of @file is missing a '.source' extension, the
1841 * function sets @error and returns %NULL.
1842 *
1843 * Returns: the unique identity string for @file, or %NULL
1844 *
1845 * Since: 3.6
1846 **/
1847 gchar *
e_server_side_source_uid_from_file(GFile * file,GError ** error)1848 e_server_side_source_uid_from_file (GFile *file,
1849 GError **error)
1850 {
1851 gchar *basename;
1852 gchar *uid = NULL;
1853
1854 g_return_val_if_fail (G_IS_FILE (file), FALSE);
1855
1856 basename = g_file_get_basename (file);
1857
1858 if (*basename == '.') {
1859 /* ignore hidden files */
1860 } else if (g_str_has_suffix (basename, ".source")) {
1861 /* strlen(".source") --> 7 */
1862 uid = g_strndup (basename, strlen (basename) - 7);
1863 } else {
1864 g_set_error (
1865 error, G_IO_ERROR,
1866 G_IO_ERROR_INVALID_FILENAME,
1867 _("File must have a “.source” extension"));
1868 }
1869
1870 g_free (basename);
1871
1872 return uid;
1873 }
1874
1875 /**
1876 * e_server_side_source_new:
1877 * @server: an #ESourceRegistryServer
1878 * @file: a #GFile, or %NULL
1879 * @error: return location for a #GError, or %NULL
1880 *
1881 * Creates a new #EServerSideSource which belongs to @server. If @file
1882 * is non-%NULL and points to an existing file, the #EServerSideSource is
1883 * initialized from the file content. If a read error occurs or the file
1884 * contains syntax errors, the function sets @error and returns %NULL.
1885 *
1886 * Returns: a new #EServerSideSource, or %NULL
1887 *
1888 * Since: 3.6
1889 **/
1890 ESource *
e_server_side_source_new(ESourceRegistryServer * server,GFile * file,GError ** error)1891 e_server_side_source_new (ESourceRegistryServer *server,
1892 GFile *file,
1893 GError **error)
1894 {
1895 EDBusObjectSkeleton *dbus_object;
1896 ESource *source;
1897 gchar *uid = NULL;
1898
1899 g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server), NULL);
1900 g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
1901
1902 /* Extract a UID from the GFile, if we were given one. */
1903 if (file != NULL) {
1904 uid = e_server_side_source_uid_from_file (file, error);
1905 if (uid == NULL)
1906 return NULL;
1907 }
1908
1909 /* XXX This is an awkward way of initializing the "dbus-object"
1910 * property, but e_source_ref_dbus_object() needs to work. */
1911 dbus_object = e_dbus_object_skeleton_new (DBUS_OBJECT_PATH);
1912
1913 source = g_initable_new (
1914 E_TYPE_SERVER_SIDE_SOURCE, NULL, error,
1915 "dbus-object", dbus_object,
1916 "file", file, "server", server,
1917 "uid", uid, NULL);
1918
1919 g_object_unref (dbus_object);
1920 g_free (uid);
1921
1922 return source;
1923 }
1924
1925 /**
1926 * e_server_side_source_new_memory_only:
1927 * @server: an #ESourceRegistryServer
1928 * @uid: a unique identifier, or %NULL
1929 * @error: return location for a #GError, or %NULL
1930 *
1931 * Creates a memory-only #EServerSideSource which belongs to @server.
1932 * No on-disk key file is created for this data source, so it will not
1933 * be remembered across sessions.
1934 *
1935 * Data source collections are often populated with memory-only data
1936 * sources to serve as proxies for resources discovered on a remote server.
1937 * These data sources are usually neither #EServerSideSource:writable nor
1938 * #EServerSideSource:removable by clients, at least not directly.
1939 *
1940 * If an error occurs while instantiating the #EServerSideSource, the
1941 * function sets @error and returns %NULL. Although at this time there
1942 * are no known error conditions for memory-only data sources.
1943 *
1944 * Returns: a new memory-only #EServerSideSource, or %NULL
1945 *
1946 * Since: 3.6
1947 **/
1948 ESource *
e_server_side_source_new_memory_only(ESourceRegistryServer * server,const gchar * uid,GError ** error)1949 e_server_side_source_new_memory_only (ESourceRegistryServer *server,
1950 const gchar *uid,
1951 GError **error)
1952 {
1953 EDBusObjectSkeleton *dbus_object;
1954 ESource *source;
1955
1956 g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server), NULL);
1957
1958 /* XXX This is an awkward way of initializing the "dbus-object"
1959 * property, but e_source_ref_dbus_object() needs to work. */
1960 dbus_object = e_dbus_object_skeleton_new (DBUS_OBJECT_PATH);
1961
1962 source = g_initable_new (
1963 E_TYPE_SERVER_SIDE_SOURCE, NULL, error,
1964 "dbus-object", dbus_object,
1965 "server", server, "uid", uid, NULL);
1966
1967 g_object_unref (dbus_object);
1968
1969 return source;
1970 }
1971
1972 /**
1973 * e_server_side_source_load:
1974 * @source: an #EServerSideSource
1975 * @cancellable: optional #GCancellable object, or %NULL
1976 * @error: return location for a #GError, or %NULL
1977 *
1978 * Reloads data source content from the file pointed to by the
1979 * #EServerSideSource:file property.
1980 *
1981 * If the #EServerSideSource:file property is %NULL or the file it points
1982 * to does not exist, the function does nothing and returns %TRUE.
1983 *
1984 * If a read error occurs or the file contains syntax errors, the function
1985 * sets @error and returns %FALSE.
1986 *
1987 * Returns: %TRUE on success, %FALSE on failure
1988 *
1989 * Since: 3.6
1990 **/
1991 gboolean
e_server_side_source_load(EServerSideSource * source,GCancellable * cancellable,GError ** error)1992 e_server_side_source_load (EServerSideSource *source,
1993 GCancellable *cancellable,
1994 GError **error)
1995 {
1996 GDBusObject *dbus_object;
1997 EDBusSource *dbus_source;
1998 GKeyFile *key_file;
1999 GFile *file;
2000 gboolean success = TRUE;
2001 gchar *data = NULL;
2002 gsize length;
2003 GError *local_error = NULL;
2004
2005 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
2006
2007 file = e_server_side_source_get_file (source);
2008
2009 if (file != NULL && !g_file_load_contents (file, cancellable, &data, &length, NULL, &local_error)) {
2010 data = NULL;
2011 length = 0;
2012 }
2013
2014 /* Disregard G_IO_ERROR_NOT_FOUND and treat it as a successful load. */
2015 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
2016 g_error_free (local_error);
2017
2018 } else if (local_error != NULL) {
2019 g_propagate_error (error, local_error);
2020 return FALSE;
2021
2022 } else {
2023 g_free (source->priv->file_contents);
2024 source->priv->file_contents = g_strdup (data);
2025 }
2026
2027 if (data == NULL) {
2028 /* Create the bare minimum to pass parse_data(). */
2029 data = g_strdup_printf ("[%s]", PRIMARY_GROUP_NAME);
2030 length = strlen (data);
2031 }
2032
2033 key_file = g_key_file_new ();
2034
2035 success = server_side_source_parse_data (
2036 key_file, data, length, error);
2037
2038 g_key_file_free (key_file);
2039
2040 if (!success) {
2041 g_free (data);
2042 return FALSE;
2043 }
2044
2045 /* Update the D-Bus interface properties. */
2046
2047 dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
2048 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
2049
2050 e_dbus_source_set_data (dbus_source, data);
2051
2052 g_object_unref (dbus_source);
2053 g_object_unref (dbus_object);
2054
2055 g_free (data);
2056
2057 return TRUE;
2058 }
2059
2060 /**
2061 * e_server_side_source_get_file:
2062 * @source: an #EServerSideSource
2063 *
2064 * Returns the #GFile from which data source content is loaded and to
2065 * which changes are saved. Note the @source may not have a #GFile.
2066 *
2067 * Returns: (transfer none) (nullable): the #GFile for @source, or %NULL
2068 *
2069 * Since: 3.6
2070 **/
2071 GFile *
e_server_side_source_get_file(EServerSideSource * source)2072 e_server_side_source_get_file (EServerSideSource *source)
2073 {
2074 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
2075
2076 return source->priv->file;
2077 }
2078
2079 /**
2080 * e_server_side_source_get_node:
2081 * @source: an #EServerSideSource
2082 *
2083 * Returns the #GNode representing the @source's hierarchical placement,
2084 * or %NULL if @source has not been placed in the data source hierarchy.
2085 * The data member of the #GNode points back to @source. This is an easy
2086 * way to traverse ancestor and descendant data sources.
2087 *
2088 * Note that accessing other data sources this way is not thread-safe,
2089 * and this therefore function may be replaced at some later date.
2090 *
2091 * Returns: (transfer none) (nullable): a #GNode, or %NULL
2092 *
2093 * Since: 3.6
2094 **/
2095 GNode *
e_server_side_source_get_node(EServerSideSource * source)2096 e_server_side_source_get_node (EServerSideSource *source)
2097 {
2098 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
2099
2100 return &source->priv->node;
2101 }
2102
2103 /**
2104 * e_server_side_source_get_server:
2105 * @source: an #EServerSideSource
2106 *
2107 * Returns the #ESourceRegistryServer to which @source belongs.
2108 *
2109 * Returns: (transfer none): the #ESourceRegistryServer for @source
2110 *
2111 * Since: 3.6
2112 **/
2113 ESourceRegistryServer *
e_server_side_source_get_server(EServerSideSource * source)2114 e_server_side_source_get_server (EServerSideSource *source)
2115 {
2116 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
2117
2118 return source->priv->server;
2119 }
2120
2121 /**
2122 * e_server_side_source_get_exported:
2123 * @source: an #EServerSideSource
2124 *
2125 * Returns whether @source has been exported over D-Bus.
2126 *
2127 * The function returns %FALSE after @source is initially created, %TRUE
2128 * after passing @source uid to e_source_registry_server_ref_source() (provided
2129 * that @source's #ESource:parent is also exported).
2130 *
2131 * Returns: whether @source has been exported
2132 *
2133 * Since: 3.6
2134 **/
2135 gboolean
e_server_side_source_get_exported(EServerSideSource * source)2136 e_server_side_source_get_exported (EServerSideSource *source)
2137 {
2138 ESourceRegistryServer *server;
2139 ESource *exported_source;
2140 gboolean exported = FALSE;
2141 const gchar *uid;
2142
2143 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
2144
2145 uid = e_source_get_uid (E_SOURCE (source));
2146 server = e_server_side_source_get_server (source);
2147
2148 /* We're exported if we can look ourselves up in the registry. */
2149
2150 exported_source = e_source_registry_server_ref_source (server, uid);
2151 if (exported_source != NULL) {
2152 exported = TRUE;
2153 g_object_unref (exported_source);
2154 }
2155
2156 return exported;
2157 }
2158
2159 /**
2160 * e_server_side_source_get_write_directory:
2161 * @source: an #EServerSideSource
2162 *
2163 * Returns the local directory path where changes to @source are written.
2164 *
2165 * By default, changes are written to the local directory path returned by
2166 * e_server_side_source_get_user_dir(), but an #ECollectionBackend may wish
2167 * to override this to use its own private cache directory for data sources
2168 * it creates automatically.
2169 *
2170 * Returns: the directory where changes are written
2171 *
2172 * Since: 3.6
2173 **/
2174 const gchar *
e_server_side_source_get_write_directory(EServerSideSource * source)2175 e_server_side_source_get_write_directory (EServerSideSource *source)
2176 {
2177 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
2178
2179 return source->priv->write_directory;
2180 }
2181
2182 /**
2183 * e_server_side_source_set_write_directory:
2184 * @source: an #EServerSideSource
2185 * @write_directory: the directory where changes are to be written
2186 *
2187 * Sets the local directory path where changes to @source are to be written.
2188 *
2189 * By default, changes are written to the local directory path returned by
2190 * e_server_side_source_get_user_dir(), but an #ECollectionBackend may wish
2191 * to override this to use its own private cache directory for data sources
2192 * it creates automatically.
2193 *
2194 * Since: 3.6
2195 **/
2196 void
e_server_side_source_set_write_directory(EServerSideSource * source,const gchar * write_directory)2197 e_server_side_source_set_write_directory (EServerSideSource *source,
2198 const gchar *write_directory)
2199 {
2200 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
2201 g_return_if_fail (write_directory != NULL);
2202
2203 if (g_strcmp0 (source->priv->write_directory, write_directory) == 0)
2204 return;
2205
2206 g_free (source->priv->write_directory);
2207 source->priv->write_directory = g_strdup (write_directory);
2208
2209 g_object_notify (G_OBJECT (source), "write-directory");
2210 }
2211
2212 /**
2213 * e_server_side_source_set_removable:
2214 * @source: an #EServerSideSource
2215 * @removable: whether to export the Removable interface
2216 *
2217 * Sets whether to allow registry clients to remove @source and its
2218 * descendants. If %TRUE, the Removable D-Bus interface is exported at
2219 * the object path for @source. If %FALSE, the Removable D-Bus interface
2220 * is unexported at the object path for @source, and any attempt by clients
2221 * to call e_source_remove() will fail.
2222 *
2223 * Note this is only enforced for clients of the registry D-Bus service.
2224 * The service itself may remove any data source at any time.
2225 *
2226 * Since: 3.6
2227 **/
2228 void
e_server_side_source_set_removable(EServerSideSource * source,gboolean removable)2229 e_server_side_source_set_removable (EServerSideSource *source,
2230 gboolean removable)
2231 {
2232 EDBusSourceRemovable *dbus_interface = NULL;
2233 GDBusObject *dbus_object;
2234 gboolean currently_removable;
2235
2236 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
2237
2238 currently_removable = e_source_get_removable (E_SOURCE (source));
2239
2240 if (removable == currently_removable)
2241 return;
2242
2243 if (removable) {
2244 dbus_interface =
2245 e_dbus_source_removable_skeleton_new ();
2246
2247 g_signal_connect (
2248 dbus_interface, "handle-remove",
2249 G_CALLBACK (server_side_source_remove_cb), source);
2250 }
2251
2252 dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
2253 e_dbus_object_skeleton_set_source_removable (
2254 E_DBUS_OBJECT_SKELETON (dbus_object), dbus_interface);
2255 g_object_unref (dbus_object);
2256
2257 if (dbus_interface != NULL)
2258 g_object_unref (dbus_interface);
2259
2260 g_object_notify (G_OBJECT (source), "removable");
2261 }
2262
2263 /**
2264 * e_server_side_source_set_writable:
2265 * @source: an #EServerSideSource
2266 * @writable: whether to export the Writable interface
2267 *
2268 * Sets whether to allow registry clients to alter the content of @source.
2269 * If %TRUE, the Writable D-Bus interface is exported at the object path
2270 * for @source. If %FALSE, the Writable D-Bus interface is unexported at
2271 * the object path for @source, and any attempt by clients to call
2272 * e_source_write() will fail.
2273 *
2274 * Note this is only enforced for clients of the registry D-Bus service.
2275 * The service itself can write to any data source at any time.
2276 *
2277 * Since: 3.6
2278 **/
2279 void
e_server_side_source_set_writable(EServerSideSource * source,gboolean writable)2280 e_server_side_source_set_writable (EServerSideSource *source,
2281 gboolean writable)
2282 {
2283 EDBusSourceWritable *dbus_interface = NULL;
2284 GDBusObject *dbus_object;
2285 gboolean currently_writable;
2286
2287 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
2288
2289 currently_writable = e_source_get_writable (E_SOURCE (source));
2290
2291 if (writable == currently_writable)
2292 return;
2293
2294 if (writable) {
2295 dbus_interface =
2296 e_dbus_source_writable_skeleton_new ();
2297
2298 g_signal_connect (
2299 dbus_interface, "handle-write",
2300 G_CALLBACK (server_side_source_write_cb), source);
2301 }
2302
2303 dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
2304 e_dbus_object_skeleton_set_source_writable (
2305 E_DBUS_OBJECT_SKELETON (dbus_object), dbus_interface);
2306 g_object_unref (dbus_object);
2307
2308 if (dbus_interface != NULL)
2309 g_object_unref (dbus_interface);
2310
2311 g_object_notify (G_OBJECT (source), "writable");
2312 }
2313
2314 /**
2315 * e_server_side_source_set_remote_creatable:
2316 * @source: an #EServerSideSource
2317 * @remote_creatable: whether to export the RemoteCreatable interface
2318 *
2319 * Indicates whether @source can be used to create resources on a remote
2320 * server. Typically this is only set to %TRUE for collection sources.
2321 *
2322 * If %TRUE, the RemoteCreatable D-Bus interface is exported at the object
2323 * path for @source. If %FALSE, the RemoteCreatable D-Bus interface is
2324 * unexported at the object path for @source, and any attempt by clients
2325 * to call e_source_remote_create() will fail.
2326 *
2327 * Unlike the #ESource:removable and #ESource:writable properties, this
2328 * is enforced for both clients of the registry D-Bus service and within
2329 * the registry D-Bus service itself.
2330 *
2331 * Since: 3.6
2332 **/
2333 void
e_server_side_source_set_remote_creatable(EServerSideSource * source,gboolean remote_creatable)2334 e_server_side_source_set_remote_creatable (EServerSideSource *source,
2335 gboolean remote_creatable)
2336 {
2337 EDBusSourceRemoteCreatable *dbus_interface = NULL;
2338 GDBusObject *dbus_object;
2339 gboolean currently_remote_creatable;
2340
2341 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
2342
2343 currently_remote_creatable =
2344 e_source_get_remote_creatable (E_SOURCE (source));
2345
2346 if (remote_creatable == currently_remote_creatable)
2347 return;
2348
2349 if (remote_creatable) {
2350 dbus_interface =
2351 e_dbus_source_remote_creatable_skeleton_new ();
2352
2353 g_signal_connect (
2354 dbus_interface, "handle-create",
2355 G_CALLBACK (server_side_source_remote_create_cb),
2356 source);
2357 }
2358
2359 dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
2360 e_dbus_object_skeleton_set_source_remote_creatable (
2361 E_DBUS_OBJECT_SKELETON (dbus_object), dbus_interface);
2362 g_object_unref (dbus_object);
2363
2364 if (dbus_interface != NULL)
2365 g_object_unref (dbus_interface);
2366
2367 g_object_notify (G_OBJECT (source), "remote-creatable");
2368 }
2369
2370 /**
2371 * e_server_side_source_set_remote_deletable:
2372 * @source: an #EServerSideSource
2373 * @remote_deletable: whether to export the RemoteDeletable interface
2374 *
2375 * Indicates whether @source can be used to delete resources on a remote
2376 * server. Typically this is only set to %TRUE for sources created by an
2377 * #ECollectionBackend to represent a remote resource.
2378 *
2379 * If %TRUE, the RemoteDeletable D-Bus interface is exported at the object
2380 * path for @source. If %FALSE, the RemoteDeletable D-Bus interface is
2381 * unexported at the object path for @source, and any attempt by clients
2382 * to call e_source_remote_delete() will fail.
2383 *
2384 * Unlike the #ESource:removable and #ESource:writable properties, this
2385 * is enforced for both clients of the registry D-Bus server and within
2386 * the registry D-Bus service itself.
2387 *
2388 * Since: 3.6
2389 **/
2390 void
e_server_side_source_set_remote_deletable(EServerSideSource * source,gboolean remote_deletable)2391 e_server_side_source_set_remote_deletable (EServerSideSource *source,
2392 gboolean remote_deletable)
2393 {
2394 EDBusSourceRemoteDeletable *dbus_interface = NULL;
2395 GDBusObject *dbus_object;
2396 gboolean currently_remote_deletable;
2397
2398 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
2399
2400 currently_remote_deletable =
2401 e_source_get_remote_deletable (E_SOURCE (source));
2402
2403 if (remote_deletable == currently_remote_deletable)
2404 return;
2405
2406 if (remote_deletable) {
2407 dbus_interface =
2408 e_dbus_source_remote_deletable_skeleton_new ();
2409
2410 g_signal_connect (
2411 dbus_interface, "handle-delete",
2412 G_CALLBACK (server_side_source_remote_delete_cb),
2413 source);
2414 }
2415
2416 dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
2417 e_dbus_object_skeleton_set_source_remote_deletable (
2418 E_DBUS_OBJECT_SKELETON (dbus_object), dbus_interface);
2419 g_object_unref (dbus_object);
2420
2421 if (dbus_interface != NULL)
2422 g_object_unref (dbus_interface);
2423
2424 g_object_notify (G_OBJECT (source), "remote-deletable");
2425 }
2426
2427 /**
2428 * e_server_side_source_ref_oauth2_support:
2429 * @source: an #EServerSideSource
2430 *
2431 * Returns the object implementing the #EOAuth2SupportInterface,
2432 * or %NULL if @source does not support OAuth 2.0 authentication.
2433 *
2434 * The returned #EOAuth2Support object is referenced for thread-safety.
2435 * Unreference the object with g_object_unref() when finished with it.
2436 *
2437 * Returns: (transfer full) (nullable): an #EOAuth2Support object, or %NULL
2438 *
2439 * Since: 3.8
2440 **/
2441 EOAuth2Support *
e_server_side_source_ref_oauth2_support(EServerSideSource * source)2442 e_server_side_source_ref_oauth2_support (EServerSideSource *source)
2443 {
2444 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
2445
2446 return g_weak_ref_get (&source->priv->oauth2_support);
2447 }
2448
2449 /**
2450 * e_server_side_source_set_oauth2_support:
2451 * @source: an #EServerSideSource
2452 * @oauth2_support: an #EOAuth2Support object, or %NULL
2453 *
2454 * Indicates whether @source supports OAuth 2.0 authentication.
2455 *
2456 * If @oauth2_support is non-%NULL, the OAuth2Support D-Bus interface is
2457 * exported at the object path for @source. If @oauth2_support is %NULL,
2458 * the OAuth2Support D-Bus interface is unexported at the object path for
2459 * @source, and any attempt by clients to call
2460 * e_source_get_oauth2_access_token() will fail.
2461 *
2462 * Requests for OAuth 2.0 access tokens are forwarded to @oauth2_support,
2463 * which implements the #EOAuth2SupportInterface.
2464 *
2465 * Since: 3.8
2466 **/
2467 void
e_server_side_source_set_oauth2_support(EServerSideSource * source,EOAuth2Support * oauth2_support)2468 e_server_side_source_set_oauth2_support (EServerSideSource *source,
2469 EOAuth2Support *oauth2_support)
2470 {
2471 EDBusSourceOAuth2Support *dbus_interface = NULL;
2472 GDBusObject *dbus_object;
2473
2474 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
2475
2476 if (oauth2_support != NULL) {
2477 g_return_if_fail (E_IS_OAUTH2_SUPPORT (oauth2_support));
2478
2479 dbus_interface =
2480 e_dbus_source_oauth2_support_skeleton_new ();
2481
2482 g_signal_connect (
2483 dbus_interface, "handle-get-access-token",
2484 G_CALLBACK (server_side_source_get_access_token_cb),
2485 source);
2486 }
2487
2488 g_weak_ref_set (&source->priv->oauth2_support, oauth2_support);
2489
2490 dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
2491 e_dbus_object_skeleton_set_source_oauth2_support (
2492 E_DBUS_OBJECT_SKELETON (dbus_object), dbus_interface);
2493 g_object_unref (dbus_object);
2494
2495 if (dbus_interface != NULL)
2496 g_object_unref (dbus_interface);
2497
2498 g_object_notify (G_OBJECT (source), "oauth2-support");
2499 }
2500