"Fossies" - the Fresh Open Source Software Archive 
Member "evolution-mapi-3.46.1/src/calendar/e-cal-backend-mapi.c" (2 Dec 2022, 58825 Bytes) of package /linux/misc/evolution-mapi-3.46.1.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "e-cal-backend-mapi.c" see the
Fossies "Dox" file reference documentation.
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4 * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with the program; if not, see <http://www.gnu.org/licenses/>
18 *
19 *
20 * Authors:
21 * Suman Manjunath <msuman@novell.com>
22 */
23
24 #include "evolution-mapi-config.h"
25
26 #include <glib/gi18n-lib.h>
27 #include <gio/gio.h>
28
29 #include <libedata-cal/libedata-cal.h>
30 #include <libedataserver/libedataserver.h>
31
32 #include "e-mapi-connection.h"
33 #include "e-mapi-cal-utils.h"
34 #include "e-mapi-utils.h"
35 #include "e-source-mapi-folder.h"
36
37 #include "e-cal-backend-mapi.h"
38
39 #define d(x)
40
41 #ifdef G_OS_WIN32
42 /* Undef the similar macro from pthread.h, it doesn't check if
43 * gmtime() returns NULL.
44 */
45 #undef gmtime_r
46
47 /* The gmtime() in Microsoft's C library is MT-safe */
48 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
49 #endif
50
51 #define EC_ERROR(_code) e_client_error_create (_code, NULL)
52 #define EC_ERROR_EX(_code, _msg) e_client_error_create (_code, _msg)
53 #define ECC_ERROR(_code) e_cal_client_error_create (_code, NULL)
54
55 /* Current data version */
56 #define EMA_DATA_VERSION 1
57 #define EMA_DATA_VERSION_KEY "ema-data-version"
58
59 struct _ECalBackendMAPIPrivate {
60 GRecMutex conn_lock;
61 EMapiConnection *conn;
62 };
63
64 G_DEFINE_TYPE_WITH_PRIVATE (ECalBackendMAPI, e_cal_backend_mapi, E_TYPE_CAL_META_BACKEND)
65
66 static gchar * ecb_mapi_dup_component_revision_cb (ECalCache *cal_cache,
67 ICalComponent *icomp);
68
69 static void
70 ecb_mapi_error_to_client_error (GError **perror,
71 const GError *mapi_error,
72 GQuark domain,
73 gint code,
74 const gchar *context)
75 {
76 gchar *err_msg = NULL;
77
78 if (!perror)
79 return;
80
81 if (g_error_matches (mapi_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
82 g_propagate_error (perror, g_error_copy (mapi_error));
83 return;
84 }
85
86 if (domain == E_CLIENT_ERROR && code == E_CLIENT_ERROR_OTHER_ERROR &&
87 mapi_error && mapi_error->domain == E_MAPI_ERROR) {
88 /* Change error to more accurate only with OtherError */
89 switch (mapi_error->code) {
90 case MAPI_E_PASSWORD_CHANGE_REQUIRED:
91 case MAPI_E_PASSWORD_EXPIRED:
92 code = E_CLIENT_ERROR_AUTHENTICATION_REQUIRED;
93 break;
94 case ecRpcFailed:
95 code = E_CLIENT_ERROR_REPOSITORY_OFFLINE;
96 break;
97 default:
98 break;
99 }
100 }
101
102 if (context)
103 err_msg = g_strconcat (context, mapi_error ? ": " : NULL, mapi_error ? mapi_error->message : NULL, NULL);
104
105 g_set_error_literal (perror, domain, code, err_msg ? err_msg : mapi_error ? mapi_error->message : _("Unknown error"));
106
107 g_free (err_msg);
108 }
109
110 static void
111 ecb_mapi_lock_connection (ECalBackendMAPI *cbmapi)
112 {
113 g_return_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi));
114
115 g_rec_mutex_lock (&cbmapi->priv->conn_lock);
116 }
117
118 static void
119 ecb_mapi_unlock_connection (ECalBackendMAPI *cbmapi)
120 {
121 g_return_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi));
122
123 g_rec_mutex_unlock (&cbmapi->priv->conn_lock);
124 }
125
126 static CamelMapiSettings *
127 ecb_mapi_get_collection_settings (ECalBackendMAPI *cbmapi)
128 {
129 ESource *source;
130 ESource *collection;
131 ESourceCamel *extension;
132 ESourceRegistry *registry;
133 CamelSettings *settings;
134 const gchar *extension_name;
135
136 source = e_backend_get_source (E_BACKEND (cbmapi));
137 registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbmapi));
138
139 extension_name = e_source_camel_get_extension_name ("mapi");
140 e_source_camel_generate_subtype ("mapi", CAMEL_TYPE_MAPI_SETTINGS);
141
142 /* The collection settings live in our parent data source. */
143 collection = e_source_registry_find_extension (registry, source, extension_name);
144 g_return_val_if_fail (collection != NULL, NULL);
145
146 extension = e_source_get_extension (collection, extension_name);
147 settings = e_source_camel_get_settings (extension);
148
149 g_object_unref (collection);
150
151 return CAMEL_MAPI_SETTINGS (settings);
152 }
153
154 static gboolean
155 ecb_mapi_open_folder (ECalBackendMAPI *cbmapi,
156 mapi_object_t *out_obj_folder,
157 GCancellable *cancellable,
158 GError **error)
159 {
160 ESource *source;
161 ESourceMapiFolder *ext_mapi_folder;
162 mapi_id_t fid;
163 gchar *foreign_username;
164 gboolean success;
165
166 g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), FALSE);
167 g_return_val_if_fail (cbmapi->priv->conn != NULL, FALSE);
168 g_return_val_if_fail (out_obj_folder != NULL, FALSE);
169
170 source = e_backend_get_source (E_BACKEND (cbmapi));
171 ext_mapi_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
172
173 fid = e_source_mapi_folder_get_id (ext_mapi_folder);
174 foreign_username = e_source_mapi_folder_dup_foreign_username (ext_mapi_folder);
175
176 if (foreign_username && *foreign_username)
177 success = e_mapi_connection_open_foreign_folder (cbmapi->priv->conn, foreign_username, fid, out_obj_folder, cancellable, error);
178 else if (e_source_mapi_folder_is_public (ext_mapi_folder))
179 success = e_mapi_connection_open_public_folder (cbmapi->priv->conn, fid, out_obj_folder, cancellable, error);
180 else
181 success = e_mapi_connection_open_personal_folder (cbmapi->priv->conn, fid, out_obj_folder, cancellable, error);
182
183 g_free (foreign_username);
184
185 return success;
186 }
187
188 static void
189 ecb_mapi_maybe_disconnect (ECalBackendMAPI *cbmapi,
190 const GError *mapi_error)
191 {
192 g_return_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi));
193
194 /* no error or already disconnected */
195 if (!mapi_error || !cbmapi->priv->conn)
196 return;
197
198 if (g_error_matches (mapi_error, E_MAPI_ERROR, ecRpcFailed) ||
199 g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_CALL_FAILED)) {
200 e_mapi_connection_disconnect (cbmapi->priv->conn,
201 !g_error_matches (mapi_error, E_MAPI_ERROR, ecRpcFailed),
202 NULL, NULL);
203 g_clear_object (&cbmapi->priv->conn);
204 }
205 }
206
207 static void
208 ecb_mapi_get_comp_mid (ICalComponent *icomp,
209 mapi_id_t *mid)
210 {
211 gchar *x_mid;
212
213 g_return_if_fail (icomp != NULL);
214 g_return_if_fail (mid != NULL);
215
216 x_mid = e_cal_util_component_dup_x_property (icomp, "X-EVOLUTION-MAPI-MID");
217 if (x_mid) {
218 e_mapi_util_mapi_id_from_string (x_mid, mid);
219 g_free (x_mid);
220 } else {
221 e_mapi_util_mapi_id_from_string (i_cal_component_get_uid (icomp), mid);
222 }
223 }
224
225 static gboolean
226 ecb_mapi_capture_req_props (EMapiConnection *conn,
227 TALLOC_CTX *mem_ctx,
228 /* const */ EMapiObject *object,
229 guint32 obj_index,
230 guint32 obj_total,
231 gpointer user_data,
232 GCancellable *cancellable,
233 GError **perror)
234 {
235 struct cal_cbdata *cbdata = user_data;
236 const uint32_t *ui32;
237
238 g_return_val_if_fail (object != NULL, FALSE);
239 g_return_val_if_fail (cbdata != NULL, FALSE);
240
241 ui32 = e_mapi_util_find_array_propval (&object->properties, PidTagOwnerAppointmentId);
242 if (ui32)
243 cbdata->appt_id = *ui32;
244 ui32 = e_mapi_util_find_array_propval (&object->properties, PidLidAppointmentSequence);
245 if (ui32)
246 cbdata->appt_seq = *ui32;
247
248 cbdata->cleanglobalid = e_mapi_util_copy_sbinary_short (e_mapi_util_find_array_propval (&object->properties, PidLidCleanGlobalObjectId));
249 cbdata->globalid = e_mapi_util_copy_sbinary_short (e_mapi_util_find_array_propval (&object->properties, PidLidGlobalObjectId));
250
251 cbdata->username = g_strdup (e_mapi_util_find_array_propval (&object->properties, PidTagSentRepresentingName));
252 cbdata->useridtype = g_strdup (e_mapi_util_find_array_propval (&object->properties, PidTagSentRepresentingAddressType));
253 cbdata->userid = g_strdup (e_mapi_util_find_array_propval (&object->properties, PidTagSentRepresentingEmailAddress));
254
255 cbdata->ownername = g_strdup (e_mapi_util_find_array_propval (&object->properties, PidTagSenderName));
256 cbdata->owneridtype = g_strdup (e_mapi_util_find_array_propval (&object->properties, PidTagSenderAddressType));
257 cbdata->ownerid = g_strdup (e_mapi_util_find_array_propval (&object->properties, PidTagSenderEmailAddress));
258
259 return TRUE;
260 }
261
262 static gboolean
263 ecb_mapi_list_for_one_mid_cb (EMapiConnection *conn,
264 TALLOC_CTX *mem_ctx,
265 const ListObjectsData *object_data,
266 guint32 obj_index,
267 guint32 obj_total,
268 gpointer user_data,
269 GCancellable *cancellable,
270 GError **perror)
271 {
272 mapi_id_t *pmid = user_data;
273
274 g_return_val_if_fail (pmid != NULL, FALSE);
275 g_return_val_if_fail (object_data != NULL, FALSE);
276
277 *pmid = object_data->mid;
278
279 return TRUE;
280 }
281
282 static gboolean
283 ecb_mapi_build_global_id_restriction (EMapiConnection *conn,
284 TALLOC_CTX *mem_ctx,
285 struct mapi_SRestriction **restrictions,
286 gpointer user_data,
287 GCancellable *cancellable,
288 GError **perror)
289 {
290 ECalComponent *comp = user_data;
291 struct SBinary_short sb;
292 struct SPropValue sprop;
293 struct mapi_SRestriction *restriction;
294 gchar *propval;
295
296 g_return_val_if_fail (restrictions != NULL, FALSE);
297 g_return_val_if_fail (comp != NULL, FALSE);
298
299 restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
300 g_return_val_if_fail (restriction != NULL, FALSE);
301
302 restriction->rt = RES_PROPERTY;
303 restriction->res.resProperty.relop = RELOP_EQ;
304 restriction->res.resProperty.ulPropTag = PidLidGlobalObjectId;
305
306 propval = e_cal_util_component_dup_x_property (e_cal_component_get_icalcomponent (comp), "X-EVOLUTION-MAPI-GLOBALID");
307 if (propval && *propval) {
308 gsize len = 0;
309
310 sb.lpb = g_base64_decode (propval, &len);
311 sb.cb = len;
312 } else {
313 ICalTime *dtstamp;
314 struct FILETIME creation_time = { 0 };
315 const gchar *uid;
316
317 uid = e_cal_component_get_uid (comp);
318
319 dtstamp = e_cal_component_get_dtstamp (comp);
320 if (!dtstamp)
321 dtstamp = i_cal_time_new_null_time ();
322
323 e_mapi_util_time_t_to_filetime (i_cal_time_as_timet (dtstamp), &creation_time);
324 e_mapi_cal_util_generate_globalobjectid (FALSE, uid, NULL, (dtstamp && i_cal_time_get_year (dtstamp)) ? &creation_time : NULL, &sb);
325
326 g_clear_object (&dtstamp);
327 }
328 g_free (propval);
329
330 set_SPropValue_proptag (&sprop, PidLidGlobalObjectId, &sb);
331 cast_mapi_SPropValue (mem_ctx, &(restriction->res.resProperty.lpProp), &sprop);
332
333 *restrictions = restriction;
334
335 return TRUE;
336 }
337
338 static gboolean
339 ecb_mapi_build_global_id_or_mid_restriction_from_uid (EMapiConnection *conn,
340 TALLOC_CTX *mem_ctx,
341 struct mapi_SRestriction **restrictions,
342 gpointer user_data,
343 GCancellable *cancellable,
344 GError **perror)
345 {
346 const gchar *uid = user_data;
347 struct SPropValue sprop;
348 struct mapi_SRestriction *restriction;
349 mapi_id_t mid = 0;
350
351 g_return_val_if_fail (restrictions != NULL, FALSE);
352 g_return_val_if_fail (uid != NULL, FALSE);
353
354 restriction = talloc_zero (mem_ctx, struct mapi_SRestriction);
355 g_return_val_if_fail (restriction != NULL, FALSE);
356
357 restriction->rt = RES_PROPERTY;
358 restriction->res.resProperty.relop = RELOP_EQ;
359
360 if (e_mapi_util_mapi_id_from_string (uid, &mid) && mid) {
361 restriction->res.resProperty.ulPropTag = PidTagMid;
362
363 set_SPropValue_proptag (&sprop, PidTagMid, &mid);
364 cast_mapi_SPropValue (mem_ctx, &(restriction->res.resProperty.lpProp), &sprop);
365 } else {
366 struct SBinary_short sb;
367 gsize len = 0;
368
369 sb.lpb = g_base64_decode (uid, &len);
370 sb.cb = len;
371
372 restriction->res.resProperty.ulPropTag = PidLidGlobalObjectId;
373
374 set_SPropValue_proptag (&sprop, PidLidGlobalObjectId, &sb);
375 cast_mapi_SPropValue (mem_ctx, &(restriction->res.resProperty.lpProp), &sprop);
376 }
377
378 *restrictions = restriction;
379
380 return TRUE;
381 }
382
383 /* should call free_server_data() before done with cbdata */
384 static void
385 ecb_mapi_get_server_data (ECalBackendMAPI *cbmapi,
386 ECalComponent *comp,
387 struct cal_cbdata *cbdata,
388 GCancellable *cancellable)
389 {
390 EMapiConnection *conn;
391 ICalComponent *icomp;
392 mapi_id_t mid;
393 mapi_object_t obj_folder;
394 GError *mapi_error = NULL;
395
396 icomp = e_cal_component_get_icalcomponent (comp);
397 ecb_mapi_get_comp_mid (icomp, &mid);
398
399 conn = cbmapi->priv->conn;
400 if (!conn)
401 goto cleanup;
402
403 if (!ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, &mapi_error))
404 goto cleanup;
405
406 if (!e_mapi_connection_transfer_object (conn, &obj_folder, mid, ecb_mapi_capture_req_props, cbdata, cancellable, &mapi_error)) {
407 if (!g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_NOT_FOUND)) {
408 g_clear_error (&mapi_error);
409 e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
410 goto cleanup;
411 }
412
413 /* try to find by global-id, if not found by MID */
414 g_clear_error (&mapi_error);
415 }
416
417 if (e_mapi_connection_list_objects (conn, &obj_folder,
418 ecb_mapi_build_global_id_restriction, comp,
419 ecb_mapi_list_for_one_mid_cb, &mid,
420 cancellable, &mapi_error)) {
421 e_mapi_connection_transfer_object (conn, &obj_folder, mid, ecb_mapi_capture_req_props, cbdata, cancellable, &mapi_error);
422 }
423
424 e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
425
426 cleanup:
427 ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
428 g_clear_error (&mapi_error);
429 }
430
431 /* frees data members allocated in get_server_data(), not the cbdata itself */
432 static void
433 ecb_mapi_free_server_data (struct cal_cbdata *cbdata)
434 {
435 if (!cbdata)
436 return;
437
438 #define do_free(_func, _val) _func (_val); _val = NULL
439
440 do_free (e_mapi_util_free_sbinary_short, cbdata->cleanglobalid);
441 do_free (e_mapi_util_free_sbinary_short, cbdata->globalid);
442 do_free (g_free, cbdata->username);
443 do_free (g_free, cbdata->useridtype);
444 do_free (g_free, cbdata->userid);
445 do_free (g_free, cbdata->ownername);
446 do_free (g_free, cbdata->owneridtype);
447 do_free (g_free, cbdata->ownerid);
448
449 #undef do_free
450 }
451
452 #define free_and_dupe_str(_des, _new) G_STMT_START { \
453 g_free (_des); \
454 _des = g_strdup (_new); \
455 } G_STMT_END
456
457 static ESource *
458 ecb_mapi_find_identity_source (ECalBackendMAPI *cbmapi)
459 {
460 ESourceRegistry *registry;
461 GList *all_sources, *my_sources, *iter;
462 CamelMapiSettings *settings;
463 ESource *res = NULL;
464
465 g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), NULL);
466
467 settings = ecb_mapi_get_collection_settings (cbmapi);
468 g_return_val_if_fail (settings != NULL, NULL);
469
470 registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbmapi));
471 all_sources = e_source_registry_list_sources (registry, NULL);
472 my_sources = e_mapi_utils_filter_sources_for_profile (all_sources,
473 camel_mapi_settings_get_profile (settings));
474 g_list_free_full (all_sources, g_object_unref);
475
476 for (iter = my_sources; iter; iter = iter->next) {
477 ESource *source = iter->data;
478
479 if (!source)
480 continue;
481
482 if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_IDENTITY)) {
483 res = g_object_ref (source);
484 break;
485 }
486 }
487
488 g_list_free_full (my_sources, g_object_unref);
489
490 return res;
491 }
492
493 static const gchar *
494 ecb_mapi_get_owner_name (ECalBackendMAPI *cbmapi)
495 {
496 ESource *identity_source;
497 ESourceMailIdentity *identity_ext;
498 const gchar *res = NULL;
499
500 identity_source = ecb_mapi_find_identity_source (cbmapi);
501 if (!identity_source)
502 return NULL;
503
504 identity_ext = e_source_get_extension (identity_source, E_SOURCE_EXTENSION_MAIL_IDENTITY);
505 if (identity_ext)
506 res = e_source_mail_identity_get_name (identity_ext);
507
508 g_object_unref (identity_source);
509
510 return res;
511 }
512
513 static const gchar *
514 ecb_mapi_get_owner_email (ECalBackendMAPI *cbmapi)
515 {
516 ESource *identity_source;
517 ESourceMailIdentity *identity_ext;
518 const gchar *res = NULL;
519
520 identity_source = ecb_mapi_find_identity_source (cbmapi);
521 if (!identity_source)
522 return NULL;
523
524 identity_ext = e_source_get_extension (identity_source, E_SOURCE_EXTENSION_MAIL_IDENTITY);
525 if (identity_ext)
526 res = e_source_mail_identity_get_address (identity_ext);
527
528 g_object_unref (identity_source);
529
530 return res;
531 }
532
533 static gboolean
534 ecb_mapi_modifier_is_organizer (ECalBackendMAPI *cbmapi,
535 ECalComponent *comp)
536 {
537 ECalComponentOrganizer *org;
538 const gchar *ownerid, *orgid;
539 gboolean res;
540
541 if (!e_cal_component_has_organizer (comp))
542 return TRUE;
543
544 org = e_cal_component_get_organizer (comp);
545 if (!org)
546 return TRUE;
547
548 orgid = e_cal_component_organizer_get_value (org);
549
550 if (orgid && !g_ascii_strncasecmp (orgid, "mailto:", 7))
551 orgid = orgid + 7;
552
553 ownerid = ecb_mapi_get_owner_email (cbmapi);
554
555 res = g_ascii_strcasecmp (orgid, ownerid) == 0;
556
557 e_cal_component_organizer_free (org);
558
559 return res;
560 }
561
562 static OlResponseStatus
563 ecb_mapi_find_my_response (ECalBackendMAPI *cbmapi,
564 ECalComponent *comp)
565 {
566 ICalComponent *icomp = e_cal_component_get_icalcomponent (comp);
567 ICalProperty *attendee;
568 gchar *att = NULL;
569 OlResponseStatus val = olResponseTentative;
570
571 att = g_strdup_printf ("mailto:%s", ecb_mapi_get_owner_email (cbmapi));
572
573 for (attendee = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
574 attendee;
575 g_object_unref (attendee), attendee = i_cal_component_get_next_property (icomp, I_CAL_ATTENDEE_PROPERTY)) {
576 const gchar *value = i_cal_property_get_attendee (attendee);
577 if (!g_ascii_strcasecmp (value, att)) {
578 ICalParameterPartstat partstat = I_CAL_PARTSTAT_NONE;
579 ICalParameter *param;
580
581 param = i_cal_property_get_first_parameter (attendee, I_CAL_PARTSTAT_PARAMETER);
582 if (param) {
583 partstat = i_cal_parameter_get_partstat (param);
584 g_object_unref (param);
585 }
586
587 switch (partstat) {
588 case I_CAL_PARTSTAT_ACCEPTED:
589 val = olResponseAccepted;
590 break;
591 case I_CAL_PARTSTAT_TENTATIVE:
592 val = olResponseTentative;
593 break;
594 case I_CAL_PARTSTAT_DECLINED:
595 val = olResponseDeclined;
596 break;
597 default:
598 val = olResponseTentative;
599 break;
600 }
601
602 g_object_unref (attendee);
603 break;
604 }
605 }
606
607 g_free (att);
608
609 return val;
610 }
611
612 static void
613 ecb_mapi_server_notification_cb (EMapiConnection *conn,
614 guint event_mask,
615 gpointer event_data,
616 gpointer user_data)
617 {
618 ECalBackendMAPI *cbmapi = user_data;
619 mapi_id_t update_folder1 = 0, update_folder2 = 0;
620
621 g_return_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi));
622
623 switch (event_mask) {
624 case fnevNewMail:
625 case fnevNewMail | fnevMbit: {
626 struct NewMailNotification *newmail = event_data;
627
628 if (newmail)
629 update_folder1 = newmail->FID;
630 } break;
631 case fnevObjectCreated:
632 case fnevMbit | fnevObjectCreated: {
633 struct MessageCreatedNotification *msgcreated = event_data;
634
635 if (msgcreated)
636 update_folder1 = msgcreated->FID;
637 } break;
638 case fnevObjectModified:
639 case fnevMbit | fnevObjectModified: {
640 struct MessageModifiedNotification *msgmodified = event_data;
641
642 if (msgmodified)
643 update_folder1 = msgmodified->FID;
644 } break;
645 case fnevObjectDeleted:
646 case fnevMbit | fnevObjectDeleted: {
647 struct MessageDeletedNotification *msgdeleted = event_data;
648
649 if (msgdeleted)
650 update_folder1 = msgdeleted->FID;
651 } break;
652 case fnevObjectMoved:
653 case fnevMbit | fnevObjectMoved: {
654 struct MessageMoveCopyNotification *msgmoved = event_data;
655
656 if (msgmoved) {
657 update_folder1 = msgmoved->OldFID;
658 update_folder2 = msgmoved->FID;
659 }
660 } break;
661 case fnevObjectCopied:
662 case fnevMbit | fnevObjectCopied: {
663 struct MessageMoveCopyNotification *msgcopied = event_data;
664
665 if (msgcopied) {
666 update_folder1 = msgcopied->OldFID;
667 update_folder2 = msgcopied->FID;
668 }
669 } break;
670 default:
671 break;
672 }
673
674 if (update_folder1 || update_folder2) {
675 ESource *source;
676 ESourceMapiFolder *ext_mapi_folder;
677
678 source = e_backend_get_source (E_BACKEND (cbmapi));
679 ext_mapi_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
680
681 if (update_folder1 == e_source_mapi_folder_get_id (ext_mapi_folder) ||
682 update_folder2 == e_source_mapi_folder_get_id (ext_mapi_folder)) {
683 e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbmapi));
684 }
685 }
686 }
687
688 static gboolean
689 ecb_mapi_connect_sync (ECalMetaBackend *meta_backend,
690 const ENamedParameters *credentials,
691 ESourceAuthenticationResult *out_auth_result,
692 gchar **out_certificate_pem,
693 GTlsCertificateFlags *out_certificate_errors,
694 GCancellable *cancellable,
695 GError **error)
696 {
697 ECalBackendMAPI *cbmapi;
698 EMapiConnection *old_conn;
699 CamelMapiSettings *settings;
700 ESource *source;
701 ESourceMapiFolder *ext_mapi_folder;
702 GError *mapi_error = NULL;
703
704 g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
705 g_return_val_if_fail (out_auth_result != NULL, FALSE);
706
707 cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
708
709 ecb_mapi_lock_connection (cbmapi);
710
711 if (cbmapi->priv->conn &&
712 e_mapi_connection_connected (cbmapi->priv->conn)) {
713 ecb_mapi_unlock_connection (cbmapi);
714 return TRUE;
715 }
716
717 settings = ecb_mapi_get_collection_settings (cbmapi);
718 source = e_backend_get_source (E_BACKEND (cbmapi));
719 ext_mapi_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_MAPI_FOLDER);
720
721 old_conn = cbmapi->priv->conn;
722
723 cbmapi->priv->conn = e_mapi_connection_new (
724 e_cal_backend_get_registry (E_CAL_BACKEND (cbmapi)),
725 camel_mapi_settings_get_profile (settings),
726 credentials, cancellable, &mapi_error);
727
728 if (!cbmapi->priv->conn) {
729 cbmapi->priv->conn = e_mapi_connection_find (camel_mapi_settings_get_profile (settings));
730 if (cbmapi->priv->conn && !e_mapi_connection_connected (cbmapi->priv->conn))
731 e_mapi_connection_reconnect (cbmapi->priv->conn, credentials, cancellable, &mapi_error);
732 }
733
734 if (old_conn)
735 g_signal_handlers_disconnect_by_func (old_conn, G_CALLBACK (ecb_mapi_server_notification_cb), cbmapi);
736
737 g_clear_object (&old_conn);
738
739 if (!cbmapi->priv->conn || mapi_error) {
740 gboolean is_network_error = mapi_error && mapi_error->domain != E_MAPI_ERROR;
741
742 g_clear_object (&cbmapi->priv->conn);
743 ecb_mapi_unlock_connection (cbmapi);
744
745 if (is_network_error)
746 ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR, NULL);
747
748 g_clear_error (&mapi_error);
749
750 if (is_network_error) {
751 *out_auth_result = E_SOURCE_AUTHENTICATION_ERROR;
752 } else if ((!credentials || !e_named_parameters_count (credentials)) && !camel_mapi_settings_get_kerberos (settings)) {
753 *out_auth_result = E_SOURCE_AUTHENTICATION_REQUIRED;
754 } else {
755 *out_auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
756 }
757
758 return FALSE;
759 }
760
761 if (e_source_mapi_folder_get_server_notification (ext_mapi_folder)) {
762 mapi_object_t obj_folder;
763 GError *mapi_error = NULL;
764
765 g_signal_connect (cbmapi->priv->conn, "server-notification", G_CALLBACK (ecb_mapi_server_notification_cb), cbmapi);
766
767 if (ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, &mapi_error)) {
768 e_mapi_connection_enable_notifications (cbmapi->priv->conn, &obj_folder,
769 fnevObjectCreated | fnevObjectModified | fnevObjectDeleted | fnevObjectMoved | fnevObjectCopied,
770 cancellable, &mapi_error);
771
772 e_mapi_connection_close_folder (cbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
773 }
774
775 if (mapi_error) {
776 ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
777 g_clear_error (&mapi_error);
778 }
779 }
780
781 ecb_mapi_unlock_connection (cbmapi);
782
783 *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED;
784
785 return TRUE;
786 }
787
788 static gboolean
789 ecb_mapi_disconnect_sync (ECalMetaBackend *meta_backend,
790 GCancellable *cancellable,
791 GError **error)
792 {
793 ECalBackendMAPI *cbmapi;
794 gboolean success = TRUE;
795
796 g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
797
798 cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
799
800 ecb_mapi_lock_connection (cbmapi);
801
802 if (cbmapi->priv->conn) {
803 g_signal_handlers_disconnect_by_func (cbmapi->priv->conn, G_CALLBACK (ecb_mapi_server_notification_cb), cbmapi);
804
805 success = e_mapi_connection_disconnect (cbmapi->priv->conn, FALSE, cancellable, error);
806 g_clear_object (&cbmapi->priv->conn);
807 }
808
809 ecb_mapi_unlock_connection (cbmapi);
810
811 return success;
812 }
813
814 typedef struct _LoadMultipleData {
815 ECalMetaBackend *meta_backend;
816 ICalComponentKind kind;
817 GSList **out_components; /* ICalComponent * */
818 } LoadMultipleData;
819
820 static gboolean
821 transfer_calendar_objects_cb (EMapiConnection *conn,
822 TALLOC_CTX *mem_ctx,
823 /* const */ EMapiObject *object,
824 guint32 obj_index,
825 guint32 obj_total,
826 gpointer user_data,
827 GCancellable *cancellable,
828 GError **perror)
829 {
830 LoadMultipleData *lmd = user_data;
831 ECalComponent *comp;
832 const mapi_id_t *pmid;
833 gchar *use_uid;
834 GSList *instances = NULL;
835
836 g_return_val_if_fail (conn != NULL, FALSE);
837 g_return_val_if_fail (object != NULL, FALSE);
838 g_return_val_if_fail (lmd != NULL, FALSE);
839
840 pmid = e_mapi_util_find_array_propval (&object->properties, PidTagMid);
841 if (pmid)
842 use_uid = e_mapi_util_mapi_id_to_string (*pmid);
843 else
844 use_uid = e_util_generate_uid ();
845
846 comp = e_mapi_cal_util_object_to_comp (conn, object,
847 lmd->kind, FALSE, use_uid, &instances);
848
849 g_free (use_uid);
850
851 if (comp)
852 instances = g_slist_prepend (instances, comp);
853
854 if (instances) {
855 ICalComponent *icomp;
856
857 icomp = e_cal_meta_backend_merge_instances (lmd->meta_backend, instances, FALSE);
858 if (icomp)
859 *lmd->out_components = g_slist_prepend (*lmd->out_components, icomp);
860 }
861
862 g_slist_free_full (instances, g_object_unref);
863
864 return TRUE;
865 }
866
867 static gboolean
868 ecb_mapi_load_multiple_sync (ECalBackendMAPI *cbmapi,
869 const GSList *uids, /* gchar * */
870 GSList **out_components, /* ICalComponent * */
871 GCancellable *cancellable,
872 GError **error)
873 {
874 LoadMultipleData lmd;
875 GSList *mids = NULL, *link;
876 mapi_object_t obj_folder;
877 gboolean success;
878 GError *mapi_error = NULL;
879
880 g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), FALSE);
881 g_return_val_if_fail (uids != NULL, FALSE);
882 g_return_val_if_fail (out_components != NULL, FALSE);
883
884 for (link = (GSList *) uids; link; link = g_slist_next (link)) {
885 mapi_id_t *pmid, mid;
886
887 if (e_mapi_util_mapi_id_from_string (link->data, &mid)) {
888 pmid = g_new0 (mapi_id_t, 1);
889 *pmid = mid;
890
891 mids = g_slist_prepend (mids, pmid);
892 }
893 }
894
895 ecb_mapi_lock_connection (cbmapi);
896
897 lmd.meta_backend = E_CAL_META_BACKEND (cbmapi);
898 lmd.kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
899 lmd.out_components = out_components;
900
901 success = ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, &mapi_error);
902
903 if (success) {
904 success = e_mapi_connection_transfer_objects (cbmapi->priv->conn, &obj_folder, mids,
905 transfer_calendar_objects_cb, &lmd, cancellable, &mapi_error);
906
907 e_mapi_connection_close_folder (cbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
908 }
909
910 if (mapi_error) {
911 ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
912 ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR, _("Failed to transfer objects from a server"));
913 g_error_free (mapi_error);
914
915 success = FALSE;
916 }
917
918 ecb_mapi_unlock_connection (cbmapi);
919
920 g_slist_free_full (mids, g_free);
921
922 return success;
923 }
924
925 static gboolean
926 ecb_mapi_preload_infos_sync (ECalBackendMAPI *cbmapi,
927 GSList *created_objects,
928 GSList *modified_objects,
929 GCancellable *cancellable,
930 GError **error)
931 {
932 GHashTable *infos;
933 GSList *uids = NULL, *link;
934 gboolean success = TRUE;
935
936 g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (cbmapi), FALSE);
937
938 infos = g_hash_table_new (g_str_hash, g_str_equal);
939
940 for (link = created_objects; link; link = g_slist_next (link)) {
941 ECalMetaBackendInfo *nfo = link->data;
942
943 if (nfo && nfo->extra) {
944 uids = g_slist_prepend (uids, nfo->extra);
945 g_hash_table_insert (infos, nfo->extra, nfo);
946 } else if (nfo && nfo->uid) {
947 uids = g_slist_prepend (uids, nfo->uid);
948 g_hash_table_insert (infos, nfo->uid, nfo);
949 }
950 }
951
952 for (link = modified_objects; link; link = g_slist_next (link)) {
953 ECalMetaBackendInfo *nfo = link->data;
954
955 if (nfo && nfo->extra) {
956 uids = g_slist_prepend (uids, nfo->extra);
957 g_hash_table_insert (infos, nfo->extra, nfo);
958 } else if (nfo && nfo->uid) {
959 uids = g_slist_prepend (uids, nfo->uid);
960 g_hash_table_insert (infos, nfo->uid, nfo);
961 }
962 }
963
964 uids = g_slist_reverse (uids);
965 if (uids) {
966 GSList *components = NULL;
967
968 success = ecb_mapi_load_multiple_sync (cbmapi, uids, &components, cancellable, error);
969 if (success) {
970 for (link = components; link; link = g_slist_next (link)) {
971 ICalComponent *icomp = link->data;
972
973 if (icomp) {
974 ECalMetaBackendInfo *nfo;
975 const gchar *uid = NULL;
976 gchar *xmid = NULL;
977
978 if (i_cal_component_isa (icomp) == I_CAL_VCALENDAR_COMPONENT) {
979 ICalComponent *subcomp;
980 ICalComponentKind kind;
981
982 kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
983
984 for (subcomp = i_cal_component_get_first_component (icomp, kind);
985 subcomp && !uid;
986 g_object_unref (subcomp), subcomp = i_cal_component_get_next_component (icomp, kind)) {
987 uid = i_cal_component_get_uid (subcomp);
988 xmid = e_cal_util_component_dup_x_property (subcomp, "X-EVOLUTION-MAPI-MID");
989 }
990
991 g_clear_object (&subcomp);
992 } else {
993 uid = i_cal_component_get_uid (icomp);
994 xmid = e_cal_util_component_dup_x_property (icomp, "X-EVOLUTION-MAPI-MID");
995 }
996
997 nfo = uid ? g_hash_table_lookup (infos, uid) : NULL;
998 if (!nfo && xmid)
999 nfo = g_hash_table_lookup (infos, xmid);
1000
1001 if (nfo && !nfo->object)
1002 nfo->object = i_cal_component_as_ical_string (icomp);
1003
1004 g_free (xmid);
1005 }
1006 }
1007 }
1008
1009 g_slist_free_full (components, g_object_unref);
1010 }
1011
1012 g_hash_table_destroy (infos);
1013 g_slist_free (uids);
1014
1015 return success;
1016 }
1017
1018 static gboolean
1019 ecb_mapi_get_changes_sync (ECalMetaBackend *meta_backend,
1020 const gchar *last_sync_tag,
1021 gboolean is_repeat,
1022 gchar **out_new_sync_tag,
1023 gboolean *out_repeat,
1024 GSList **out_created_objects,
1025 GSList **out_modified_objects,
1026 GSList **out_removed_objects,
1027 GCancellable *cancellable,
1028 GError **error)
1029 {
1030 ECalBackendMAPI *cbmapi;
1031
1032 g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
1033 g_return_val_if_fail (out_created_objects != NULL, FALSE);
1034 g_return_val_if_fail (out_modified_objects != NULL, FALSE);
1035
1036 /* Chain up to parent's method */
1037 if (!E_CAL_META_BACKEND_CLASS (e_cal_backend_mapi_parent_class)->get_changes_sync (meta_backend,
1038 last_sync_tag, is_repeat, out_new_sync_tag, out_repeat, out_created_objects,
1039 out_modified_objects, out_removed_objects, cancellable, error)) {
1040 return FALSE;
1041 }
1042
1043 cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
1044
1045 /* Preload some of the components in chunk, to speed-up things;
1046 ignore errors, to not break whole update process. */
1047 ecb_mapi_preload_infos_sync (cbmapi, *out_created_objects, *out_modified_objects, cancellable, NULL);
1048
1049 return TRUE;
1050 }
1051
1052 static gboolean
1053 ecb_mapi_list_existing_uids_cb (EMapiConnection *conn,
1054 TALLOC_CTX *mem_ctx,
1055 const ListObjectsData *object_data,
1056 guint32 obj_index,
1057 guint32 obj_total,
1058 gpointer user_data,
1059 GCancellable *cancellable,
1060 GError **perror)
1061 {
1062 GSList **out_existing_objects = user_data;
1063 gchar *uid;
1064
1065 g_return_val_if_fail (conn != NULL, FALSE);
1066 g_return_val_if_fail (object_data != NULL, FALSE);
1067 g_return_val_if_fail (out_existing_objects != NULL, FALSE);
1068
1069 uid = e_mapi_util_mapi_id_to_string (object_data->mid);
1070 if (uid) {
1071 ICalTime *itt;
1072 gchar *rev;
1073
1074 itt = i_cal_time_new_from_timet_with_zone (object_data->last_modified, 0, i_cal_timezone_get_utc_timezone ());
1075 rev = i_cal_time_as_ical_string (itt);
1076 g_clear_object (&itt);
1077
1078 *out_existing_objects = g_slist_prepend (*out_existing_objects,
1079 e_cal_meta_backend_info_new (uid, rev, NULL, uid));
1080
1081 g_free (uid);
1082 g_free (rev);
1083 }
1084
1085 return TRUE;
1086 }
1087
1088 static gboolean
1089 ecb_mapi_populate_mid_to_gid_cb (ECalCache *cal_cache,
1090 const gchar *uid,
1091 const gchar *rid,
1092 const gchar *revision,
1093 const gchar *object,
1094 const gchar *extra,
1095 guint32 custom_flags,
1096 EOfflineState offline_state,
1097 gpointer user_data)
1098 {
1099 GHashTable *mid_to_gid = user_data;
1100
1101 g_return_val_if_fail (mid_to_gid != NULL, FALSE);
1102
1103 if (uid && *uid && extra && *extra && g_strcmp0 (uid, extra) != 0)
1104 g_hash_table_insert (mid_to_gid, g_strdup (extra), g_strdup (uid));
1105
1106 return TRUE;
1107 }
1108
1109 static gboolean
1110 ecb_mapi_list_existing_sync (ECalMetaBackend *meta_backend,
1111 gchar **out_new_sync_tag,
1112 GSList **out_existing_objects,
1113 GCancellable *cancellable,
1114 GError **error)
1115 {
1116 ECalBackendMAPI *cbmapi;
1117 mapi_object_t obj_folder;
1118 gboolean success;
1119 GError *mapi_error = NULL;
1120
1121 g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
1122 g_return_val_if_fail (out_existing_objects, FALSE);
1123
1124 *out_existing_objects = NULL;
1125
1126 cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
1127
1128 ecb_mapi_lock_connection (cbmapi);
1129
1130 success = ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, &mapi_error);
1131 if (success) {
1132 success = e_mapi_connection_list_objects (cbmapi->priv->conn, &obj_folder, NULL, NULL,
1133 ecb_mapi_list_existing_uids_cb, out_existing_objects, cancellable, &mapi_error);
1134
1135 e_mapi_connection_close_folder (cbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
1136 }
1137
1138 if (mapi_error) {
1139 ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
1140 ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR, _("Failed to list items from a server"));
1141 g_error_free (mapi_error);
1142
1143 success = FALSE;
1144 }
1145
1146 ecb_mapi_unlock_connection (cbmapi);
1147
1148 /* Components with GlobalId has UID the GlobalId, all other have MessageID,
1149 but here the 'nfo->uid' is MessageID */
1150 if (success) {
1151 ECalCache *cal_cache;
1152
1153 cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
1154 if (cal_cache) {
1155 GHashTable *mid_to_gid;
1156
1157 mid_to_gid = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1158
1159 if (e_cal_cache_search_with_callback (cal_cache, NULL, ecb_mapi_populate_mid_to_gid_cb, mid_to_gid, cancellable, NULL) &&
1160 g_hash_table_size (mid_to_gid) > 0) {
1161 GSList *link;
1162
1163 for (link = *out_existing_objects; link; link = g_slist_next (link)) {
1164 ECalMetaBackendInfo *nfo = link->data;
1165
1166 if (nfo && nfo->uid) {
1167 const gchar *gid = g_hash_table_lookup (mid_to_gid, nfo->uid);
1168
1169 if (gid && *gid) {
1170 g_free (nfo->uid);
1171 nfo->uid = g_strdup (gid);
1172 }
1173 }
1174 }
1175 }
1176
1177 g_hash_table_destroy (mid_to_gid);
1178 g_object_unref (cal_cache);
1179 }
1180 }
1181
1182 return success;
1183 }
1184
1185 static gboolean
1186 ecb_mapi_load_component_sync (ECalMetaBackend *meta_backend,
1187 const gchar *uid,
1188 const gchar *extra,
1189 ICalComponent **out_component,
1190 gchar **out_extra,
1191 GCancellable *cancellable,
1192 GError **error)
1193 {
1194 ECalBackendMAPI *cbmapi;
1195 GSList *uids, *components = NULL;
1196 gboolean success;
1197 GError *local_error = NULL;
1198
1199 g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
1200 g_return_val_if_fail (uid != NULL, FALSE);
1201 g_return_val_if_fail (out_component != NULL, FALSE);
1202
1203 *out_component = NULL;
1204
1205 cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
1206
1207 uids = g_slist_prepend (NULL, (gpointer) uid);
1208
1209 ecb_mapi_lock_connection (cbmapi);
1210
1211 success = ecb_mapi_load_multiple_sync (cbmapi, uids, &components, cancellable, &local_error);
1212 if (!success) {
1213 mapi_object_t obj_folder;
1214 mapi_id_t mid = 0;
1215
1216 /* Not downloaded in the local cache yet, try to find it. */
1217 if (ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, NULL)) {
1218 if (e_mapi_connection_list_objects (cbmapi->priv->conn, &obj_folder,
1219 ecb_mapi_build_global_id_or_mid_restriction_from_uid, (gpointer) uid,
1220 ecb_mapi_list_for_one_mid_cb, &mid, cancellable, NULL) && mid) {
1221 LoadMultipleData lmd;
1222
1223 lmd.meta_backend = E_CAL_META_BACKEND (cbmapi);
1224 lmd.kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbmapi));
1225 lmd.out_components = &components;
1226
1227 success = e_mapi_connection_transfer_object (cbmapi->priv->conn, &obj_folder, mid,
1228 transfer_calendar_objects_cb, &lmd, cancellable, NULL);
1229
1230 if (success)
1231 g_clear_error (&local_error);
1232 }
1233
1234 e_mapi_connection_close_folder (cbmapi->priv->conn, &obj_folder, cancellable, NULL);
1235 }
1236 }
1237
1238 ecb_mapi_unlock_connection (cbmapi);
1239
1240 if (success && components) {
1241 *out_component = components->data;
1242 g_slist_free (components);
1243 } else {
1244 g_slist_free_full (components, g_object_unref);
1245 }
1246
1247 if (local_error)
1248 g_propagate_error (error, local_error);
1249
1250 g_slist_free (uids);
1251
1252 return success;
1253 }
1254
1255 static gboolean
1256 ecb_mapi_save_component_sync (ECalMetaBackend *meta_backend,
1257 gboolean overwrite_existing,
1258 EConflictResolution conflict_resolution,
1259 const GSList *instances,
1260 const gchar *extra,
1261 guint32 opflags, /* bit-or of ECalOperationFlags */
1262 gchar **out_new_uid,
1263 gchar **out_new_extra,
1264 GCancellable *cancellable,
1265 GError **error)
1266 {
1267 ECalBackendMAPI *cbmapi;
1268 ECalComponent *comp;
1269 ICalComponent *icomp;
1270 gboolean no_increment;
1271 mapi_object_t obj_folder;
1272 mapi_id_t mid = 0;
1273 gboolean success;
1274 GError *mapi_error = NULL;
1275
1276 g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
1277 g_return_val_if_fail (instances != NULL, FALSE);
1278 g_return_val_if_fail (out_new_uid != NULL, FALSE);
1279
1280 *out_new_uid = NULL;
1281
1282 if (instances->next ||
1283 e_cal_component_is_instance (instances->data)) {
1284 g_propagate_error (error, EC_ERROR_EX (E_CLIENT_ERROR_OTHER_ERROR,
1285 _("Support for modifying single instances of a recurring appointment is not yet implemented. No change was made to the appointment on the server.")));
1286 return FALSE;
1287 }
1288
1289 cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
1290
1291 icomp = i_cal_component_clone (e_cal_component_get_icalcomponent (instances->data));
1292 no_increment = e_cal_util_component_remove_x_property (icomp, "X-EVOLUTION-IS-REPLY");
1293
1294 comp = e_cal_component_new_from_icalcomponent (icomp);
1295 if (!comp) {
1296 g_propagate_error (error, ECC_ERROR (E_CAL_CLIENT_ERROR_INVALID_OBJECT));
1297 return FALSE;
1298 }
1299
1300 ecb_mapi_lock_connection (cbmapi);
1301
1302 success = ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, &mapi_error);
1303 if (success) {
1304 struct cal_cbdata cbdata = { 0 };
1305 gboolean has_attendees = e_cal_component_has_attendees (comp);
1306
1307 cbdata.kind = e_cal_backend_get_kind (E_CAL_BACKEND (meta_backend));
1308 cbdata.comp = comp;
1309 cbdata.is_modify = overwrite_existing;
1310 cbdata.msgflags = MSGFLAG_READ;
1311 cbdata.get_timezone = e_timezone_cache_get_timezone;
1312 cbdata.get_tz_data = cbmapi;
1313
1314 if (overwrite_existing) {
1315 ecb_mapi_get_comp_mid (icomp, &mid);
1316
1317 ecb_mapi_get_server_data (cbmapi, comp, &cbdata, cancellable);
1318 if (ecb_mapi_modifier_is_organizer (cbmapi, comp)) {
1319 cbdata.meeting_type = has_attendees ? MEETING_OBJECT : NOT_A_MEETING;
1320 cbdata.resp = has_attendees ? olResponseOrganized : olResponseNone;
1321 if (!no_increment)
1322 cbdata.appt_seq += 1;
1323 free_and_dupe_str (cbdata.username, ecb_mapi_get_owner_name (cbmapi));
1324 free_and_dupe_str (cbdata.useridtype, "SMTP");
1325 free_and_dupe_str (cbdata.userid, ecb_mapi_get_owner_email (cbmapi));
1326 free_and_dupe_str (cbdata.ownername, ecb_mapi_get_owner_name (cbmapi));
1327 free_and_dupe_str (cbdata.owneridtype, "SMTP");
1328 free_and_dupe_str (cbdata.ownerid, ecb_mapi_get_owner_email (cbmapi));
1329 } else {
1330 cbdata.resp = has_attendees ? ecb_mapi_find_my_response (cbmapi, comp) : olResponseNone;
1331 cbdata.meeting_type = has_attendees ? MEETING_OBJECT_RCVD : NOT_A_MEETING;
1332 }
1333
1334 success = e_mapi_connection_modify_object (cbmapi->priv->conn, &obj_folder, mid,
1335 e_mapi_cal_utils_comp_to_object, &cbdata, cancellable, &mapi_error);
1336
1337 ecb_mapi_free_server_data (&cbdata);
1338 } else {
1339 cbdata.username = g_strdup (ecb_mapi_get_owner_name (cbmapi));
1340 cbdata.useridtype = (gchar *) "SMTP";
1341 cbdata.userid = g_strdup (ecb_mapi_get_owner_email (cbmapi));
1342 cbdata.ownername = g_strdup (ecb_mapi_get_owner_name (cbmapi));
1343 cbdata.owneridtype = (gchar *) "SMTP";
1344 cbdata.ownerid = g_strdup (ecb_mapi_get_owner_email (cbmapi));
1345
1346 cbdata.meeting_type = has_attendees ? MEETING_OBJECT : NOT_A_MEETING;
1347 cbdata.resp = has_attendees ? olResponseOrganized : olResponseNone;
1348 cbdata.appt_id = e_mapi_cal_util_get_new_appt_id (cbmapi->priv->conn, mapi_object_get_id (&obj_folder));
1349 cbdata.appt_seq = 0;
1350 cbdata.globalid = NULL;
1351 cbdata.cleanglobalid = NULL;
1352
1353 success = e_mapi_connection_create_object (cbmapi->priv->conn, &obj_folder, E_MAPI_CREATE_FLAG_NONE,
1354 e_mapi_cal_utils_comp_to_object, &cbdata, &mid, cancellable, &mapi_error);
1355 }
1356
1357 g_free (cbdata.username);
1358 g_free (cbdata.userid);
1359 g_free (cbdata.ownername);
1360 g_free (cbdata.ownerid);
1361
1362 e_mapi_connection_close_folder (cbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
1363 }
1364
1365 if (mapi_error || !mid) {
1366 ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
1367 ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR,
1368 overwrite_existing ? _("Failed to modify item on a server") : _("Failed to create item on a server"));
1369 g_clear_error (&mapi_error);
1370
1371 success = FALSE;
1372 }
1373
1374 ecb_mapi_unlock_connection (cbmapi);
1375
1376 if (success)
1377 *out_new_uid = e_mapi_util_mapi_id_to_string (mid);
1378
1379 g_object_unref (comp);
1380
1381 return success;
1382 }
1383
1384 static gboolean
1385 ecb_mapi_remove_component_sync (ECalMetaBackend *meta_backend,
1386 EConflictResolution conflict_resolution,
1387 const gchar *uid,
1388 const gchar *extra,
1389 const gchar *object,
1390 guint32 opflags, /* bit-or of ECalOperationFlags */
1391 GCancellable *cancellable,
1392 GError **error)
1393 {
1394 ECalBackendMAPI *cbmapi;
1395 mapi_id_t mid = 0;
1396 gboolean success = TRUE;
1397 GError *mapi_error = NULL;
1398
1399 g_return_val_if_fail (E_IS_CAL_BACKEND_MAPI (meta_backend), FALSE);
1400 g_return_val_if_fail (uid != NULL, FALSE);
1401
1402 cbmapi = E_CAL_BACKEND_MAPI (meta_backend);
1403
1404 if (object) {
1405 ICalComponent *icomp;
1406
1407 icomp = i_cal_component_new_from_string (object);
1408 if (icomp) {
1409 ecb_mapi_get_comp_mid (icomp, &mid);
1410 g_object_unref (icomp);
1411 }
1412 }
1413
1414 if (mid || e_mapi_util_mapi_id_from_string (uid, &mid)) {
1415 mapi_object_t obj_folder;
1416
1417 ecb_mapi_lock_connection (cbmapi);
1418
1419 success = ecb_mapi_open_folder (cbmapi, &obj_folder, cancellable, &mapi_error);
1420 if (success) {
1421 GSList *mids;
1422
1423 mids = g_slist_prepend (NULL, &mid);
1424
1425 success = e_mapi_connection_remove_items (cbmapi->priv->conn, &obj_folder, mids, cancellable, &mapi_error);
1426
1427 e_mapi_connection_close_folder (cbmapi->priv->conn, &obj_folder, cancellable, &mapi_error);
1428
1429 g_slist_free (mids);
1430 }
1431
1432 ecb_mapi_unlock_connection (cbmapi);
1433 }
1434
1435 if (mapi_error || !mid) {
1436 ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
1437 ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR, _("Failed to remove item from a server"));
1438 g_clear_error (&mapi_error);
1439
1440 success = FALSE;
1441 }
1442
1443 return success;
1444 }
1445
1446 static gchar *
1447 ecb_mapi_get_backend_property (ECalBackend *backend,
1448 const gchar *prop_name)
1449 {
1450 ECalBackendMAPI *cbmapi;
1451
1452 g_return_val_if_fail (prop_name != NULL, NULL);
1453
1454 cbmapi = E_CAL_BACKEND_MAPI (backend);
1455
1456 if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
1457 return g_strjoin (
1458 ",",
1459 E_CAL_STATIC_CAPABILITY_NO_ALARM_REPEAT,
1460 E_CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS,
1461 E_CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS,
1462 E_CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS,
1463 E_CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY,
1464 E_CAL_STATIC_CAPABILITY_REMOVE_ALARMS,
1465 E_CAL_STATIC_CAPABILITY_NO_THISANDFUTURE,
1466 E_CAL_STATIC_CAPABILITY_NO_THISANDPRIOR,
1467 E_CAL_STATIC_CAPABILITY_CREATE_MESSAGES,
1468 E_CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK,
1469 E_CAL_STATIC_CAPABILITY_NO_CONV_TO_RECUR,
1470 E_CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING,
1471 E_CAL_STATIC_CAPABILITY_REFRESH_SUPPORTED,
1472 E_CAL_STATIC_CAPABILITY_NO_MEMO_START_DATE,
1473 E_CAL_STATIC_CAPABILITY_TASK_DATE_ONLY,
1474 E_CAL_STATIC_CAPABILITY_TASK_NO_ALARM,
1475 e_cal_meta_backend_get_capabilities (E_CAL_META_BACKEND (backend)),
1476 NULL);
1477 } else if (g_str_equal (prop_name, E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS)) {
1478 return g_strdup (ecb_mapi_get_owner_email (cbmapi));
1479 } else if (g_str_equal (prop_name, E_CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) {
1480 /* We don't support email alarms. This should not have been called. */
1481 return NULL;
1482 }
1483
1484 /* Chain up to parent's method */
1485 return E_CAL_BACKEND_CLASS (e_cal_backend_mapi_parent_class)->impl_get_backend_property (backend, prop_name);
1486 }
1487
1488 static void
1489 ecb_mapi_send_objects_sync (ECalBackendSync *sync_backend,
1490 EDataCal *cal,
1491 GCancellable *cancellable,
1492 const gchar *calobj,
1493 guint32 opflags, /* bit-or of ECalOperationFlags */
1494 GSList **users,
1495 gchar **modified_calobj,
1496 GError **error)
1497 {
1498 ECalBackendMAPI *cbmapi;
1499 EMapiConnection *conn;
1500 ICalComponentKind kind;
1501 ICalComponent *icomp;
1502 GError *mapi_error = NULL;
1503
1504 e_mapi_return_client_error_if_fail (E_IS_CAL_BACKEND_MAPI (sync_backend), E_CLIENT_ERROR_INVALID_ARG);
1505 e_mapi_return_client_error_if_fail (calobj != NULL, E_CLIENT_ERROR_INVALID_ARG);
1506
1507 cbmapi = E_CAL_BACKEND_MAPI (sync_backend);
1508 kind = e_cal_backend_get_kind (E_CAL_BACKEND (sync_backend));
1509
1510 ecb_mapi_lock_connection (cbmapi);
1511
1512 if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbmapi), cancellable, &mapi_error) ||
1513 !cbmapi->priv->conn) {
1514 ecb_mapi_unlock_connection (cbmapi);
1515
1516 if (!mapi_error)
1517 g_propagate_error (error, EC_ERROR (E_CLIENT_ERROR_REPOSITORY_OFFLINE));
1518 else
1519 ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_REPOSITORY_OFFLINE, NULL);
1520 g_clear_error (&mapi_error);
1521 return;
1522 }
1523
1524 conn = cbmapi->priv->conn;
1525
1526 /* check the component for validity */
1527 icomp = i_cal_parser_parse_string (calobj);
1528 if (!icomp) {
1529 ecb_mapi_unlock_connection (cbmapi);
1530 g_propagate_error (error, ECC_ERROR (E_CAL_CLIENT_ERROR_INVALID_OBJECT));
1531 return;
1532 }
1533
1534 *modified_calobj = NULL;
1535 *users = NULL;
1536
1537 if (i_cal_component_isa (icomp) == I_CAL_VCALENDAR_COMPONENT) {
1538 ICalPropertyMethod method = i_cal_component_get_method (icomp);
1539 ICalComponent *subcomp;
1540
1541 for (subcomp = i_cal_component_get_first_component (icomp, kind);
1542 subcomp;
1543 g_object_unref (subcomp), subcomp = i_cal_component_get_next_component (icomp, kind)) {
1544 ECalComponent *comp;
1545 struct cal_cbdata cbdata = { 0 };
1546 mapi_id_t mid = 0;
1547 const gchar *compuid;
1548 gchar *propval;
1549 struct SBinary_short globalid = { 0 }, cleanglobalid = { 0 };
1550 struct timeval *exception_repleace_time = NULL, ex_rep_time = { 0 };
1551 struct FILETIME creation_time = { 0 };
1552 ICalTime *dtstamp;
1553 mapi_object_t obj_folder;
1554
1555 comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (subcomp));
1556 if (!comp)
1557 continue;
1558
1559 cbdata.kind = kind;
1560 cbdata.comp = comp;
1561 cbdata.is_modify = TRUE;
1562 cbdata.msgflags = MSGFLAG_READ | MSGFLAG_SUBMIT | MSGFLAG_UNSENT;
1563
1564 switch (method) {
1565 case I_CAL_METHOD_REQUEST:
1566 cbdata.meeting_type = MEETING_REQUEST;
1567 cbdata.resp = olResponseNotResponded;
1568 break;
1569 case I_CAL_METHOD_CANCEL:
1570 cbdata.meeting_type = MEETING_CANCEL;
1571 cbdata.resp = olResponseNotResponded;
1572 break;
1573 case I_CAL_METHOD_REPLY:
1574 case I_CAL_METHOD_RESPONSE:
1575 cbdata.meeting_type = MEETING_RESPONSE;
1576 cbdata.resp = ecb_mapi_find_my_response (cbmapi, comp);
1577 break;
1578 default:
1579 cbdata.meeting_type = NOT_A_MEETING;
1580 cbdata.resp = olResponseNone;
1581 break;
1582 }
1583
1584 ecb_mapi_get_server_data (cbmapi, comp, &cbdata, cancellable);
1585 free_and_dupe_str (cbdata.username, ecb_mapi_get_owner_name (cbmapi));
1586 free_and_dupe_str (cbdata.useridtype, "SMTP");
1587 free_and_dupe_str (cbdata.userid, ecb_mapi_get_owner_email (cbmapi));
1588 free_and_dupe_str (cbdata.ownername, ecb_mapi_get_owner_name (cbmapi));
1589 free_and_dupe_str (cbdata.owneridtype, "SMTP");
1590 free_and_dupe_str (cbdata.ownerid, ecb_mapi_get_owner_email (cbmapi));
1591 cbdata.get_timezone = e_timezone_cache_get_timezone;
1592 cbdata.get_tz_data = cbmapi;
1593
1594 compuid = e_cal_component_get_uid (comp);
1595
1596 dtstamp = e_cal_component_get_dtstamp (comp);
1597 if (!dtstamp)
1598 dtstamp = i_cal_time_new_null_time ();
1599 e_mapi_util_time_t_to_filetime (i_cal_time_as_timet (dtstamp), &creation_time);
1600
1601 propval = e_cal_util_component_dup_x_property (e_cal_component_get_icalcomponent (comp), "X-EVOLUTION-MAPI-EXREPTIME");
1602 if (propval && *propval) {
1603 mapi_id_t val64 = 0;
1604
1605 if (e_mapi_util_mapi_id_from_string (propval, &val64)) {
1606 memcpy (&ex_rep_time, &val64, 8);
1607 exception_repleace_time = &ex_rep_time;
1608 }
1609 }
1610 g_free (propval);
1611
1612 /* inherit GlobalID from the source object, if available */
1613 if (e_cal_component_get_icalcomponent (comp)) {
1614 propval = e_cal_util_component_dup_x_property (e_cal_component_get_icalcomponent (comp), "X-EVOLUTION-MAPI-GLOBALID");
1615 if (propval && *propval) {
1616 gsize len = 0;
1617
1618 globalid.lpb = g_base64_decode (propval, &len);
1619 globalid.cb = len;
1620
1621 cleanglobalid.lpb = g_memdup (globalid.lpb, globalid.cb);
1622 cleanglobalid.cb = globalid.cb;
1623
1624 /* PidLidCleanGlobalObjectId is same as PidLidGlobalObjectId,
1625 only exception-information are zeros */
1626 if (cleanglobalid.lpb && cleanglobalid.cb > 20) {
1627 for (len = 16; len < 20; len++) {
1628 cleanglobalid.lpb[len] = 0;
1629 }
1630 }
1631
1632 compuid = NULL;
1633 }
1634
1635 g_free (propval);
1636 }
1637
1638 if (compuid) {
1639 e_mapi_cal_util_generate_globalobjectid (FALSE, compuid, exception_repleace_time,
1640 (dtstamp && i_cal_time_get_year (dtstamp)) ? &creation_time : NULL, &globalid);
1641 e_mapi_cal_util_generate_globalobjectid (TRUE, compuid, exception_repleace_time,
1642 (dtstamp && i_cal_time_get_year (dtstamp)) ? &creation_time : NULL, &cleanglobalid);
1643 }
1644
1645 g_clear_object (&dtstamp);
1646
1647 if (cbdata.globalid)
1648 e_mapi_util_free_sbinary_short (cbdata.globalid);
1649 if (cbdata.cleanglobalid)
1650 e_mapi_util_free_sbinary_short (cbdata.cleanglobalid);
1651 cbdata.globalid = &globalid;
1652 cbdata.cleanglobalid = &cleanglobalid;
1653
1654 mid = 0;
1655 if (e_mapi_connection_open_default_folder (conn, olFolderSentMail, &obj_folder, cancellable, &mapi_error)) {
1656 e_mapi_connection_create_object (conn, &obj_folder, E_MAPI_CREATE_FLAG_SUBMIT,
1657 e_mapi_cal_utils_comp_to_object, &cbdata,
1658 &mid, cancellable, &mapi_error);
1659
1660 e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
1661 }
1662
1663 cbdata.globalid = NULL;
1664 cbdata.cleanglobalid = NULL;
1665 ecb_mapi_free_server_data (&cbdata);
1666 g_free (globalid.lpb);
1667 g_free (cleanglobalid.lpb);
1668
1669 if (!mid) {
1670 ecb_mapi_unlock_connection (cbmapi);
1671 g_object_unref (comp);
1672 ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR, _("Failed to create item on a server"));
1673 ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
1674 if (mapi_error)
1675 g_error_free (mapi_error);
1676 return;
1677 }
1678
1679 g_object_unref (comp);
1680 }
1681 }
1682
1683 ecb_mapi_unlock_connection (cbmapi);
1684
1685 *modified_calobj = g_strdup (calobj);
1686
1687 g_object_unref (icomp);
1688 }
1689
1690 static void
1691 ecb_mapi_get_free_busy_sync (ECalBackendSync *sync_backend,
1692 EDataCal *cal,
1693 GCancellable *cancellable,
1694 const GSList *users,
1695 time_t start,
1696 time_t end,
1697 GSList **freebusyobjs,
1698 GError **error)
1699 {
1700 ECalBackendMAPI *cbmapi;
1701 GError *mapi_error = NULL;
1702
1703 g_return_if_fail (E_IS_CAL_BACKEND_MAPI (sync_backend));
1704
1705 cbmapi = E_CAL_BACKEND_MAPI (sync_backend);
1706
1707 ecb_mapi_lock_connection (cbmapi);
1708
1709 if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbmapi), cancellable, &mapi_error) ||
1710 !cbmapi->priv->conn) {
1711 ecb_mapi_unlock_connection (cbmapi);
1712
1713 if (!mapi_error)
1714 g_propagate_error (error, EC_ERROR (E_CLIENT_ERROR_REPOSITORY_OFFLINE));
1715 else
1716 ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_REPOSITORY_OFFLINE, NULL);
1717 g_clear_error (&mapi_error);
1718 return;
1719 }
1720
1721 if (!e_mapi_cal_utils_get_free_busy_data (cbmapi->priv->conn, users, start, end, freebusyobjs, cancellable, &mapi_error)) {
1722 ecb_mapi_error_to_client_error (error, mapi_error, E_CLIENT_ERROR, E_CLIENT_ERROR_OTHER_ERROR, _("Failed to get Free/Busy data"));
1723 ecb_mapi_maybe_disconnect (cbmapi, mapi_error);
1724
1725 if (mapi_error)
1726 g_error_free (mapi_error);
1727 }
1728
1729 ecb_mapi_unlock_connection (cbmapi);
1730 }
1731
1732 static gboolean
1733 ecb_mapi_get_destination_address (EBackend *backend,
1734 gchar **host,
1735 guint16 *port)
1736 {
1737 ESourceRegistry *registry;
1738 ESource *source;
1739 gboolean result = FALSE;
1740
1741 g_return_val_if_fail (host != NULL, FALSE);
1742 g_return_val_if_fail (port != NULL, FALSE);
1743
1744 registry = e_cal_backend_get_registry (E_CAL_BACKEND (backend));
1745 source = e_backend_get_source (backend);
1746
1747 /* Sanity checking */
1748 if (!registry || !source || !e_source_get_parent (source))
1749 return FALSE;
1750
1751 source = e_source_registry_ref_source (registry, e_source_get_parent (source));
1752 if (!source)
1753 return FALSE;
1754
1755 if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
1756 ESourceAuthentication *auth = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
1757
1758 *host = g_strdup (e_source_authentication_get_host (auth));
1759 *port = e_source_authentication_get_port (auth);
1760
1761 if (!*port)
1762 *port = 135;
1763
1764 result = *host && **host;
1765 if (!result) {
1766 g_free (*host);
1767 *host = NULL;
1768 }
1769 }
1770
1771 g_object_unref (source);
1772
1773 return result;
1774 }
1775
1776 static gboolean
1777 ecb_mapi_update_tzid_cb (ECache *cache,
1778 const gchar *uid,
1779 const gchar *revision,
1780 const gchar *object,
1781 EOfflineState offline_state,
1782 gint ncols,
1783 const gchar *column_names[],
1784 const gchar *column_values[],
1785 gchar **out_revision,
1786 gchar **out_object,
1787 EOfflineState *out_offline_state,
1788 ECacheColumnValues **out_other_columns,
1789 gpointer user_data)
1790 {
1791 ICalComponent *icomp;
1792 ICalProperty *prop;
1793 gboolean changed = FALSE;
1794
1795 g_return_val_if_fail (object != NULL, FALSE);
1796 g_return_val_if_fail (out_object != NULL, FALSE);
1797
1798 icomp = i_cal_component_new_from_string (object);
1799 if (!icomp)
1800 return TRUE;
1801
1802 prop = i_cal_component_get_first_property (icomp, I_CAL_DTSTART_PROPERTY);
1803 if (prop && e_cal_util_property_has_parameter (prop, I_CAL_TZID_PARAMETER)) {
1804 ICalTime *itt;
1805
1806 itt = i_cal_property_get_dtstart (prop);
1807 if (itt && i_cal_time_is_valid_time (itt) && i_cal_time_is_utc (itt)) {
1808 i_cal_time_set_timezone (itt, NULL);
1809 i_cal_property_set_dtstart (prop, itt);
1810 changed = TRUE;
1811 }
1812
1813 g_clear_object (&itt);
1814 }
1815 g_clear_object (&prop);
1816
1817 prop = i_cal_component_get_first_property (icomp, I_CAL_DTEND_PROPERTY);
1818 if (prop && e_cal_util_property_has_parameter (prop, I_CAL_TZID_PARAMETER)) {
1819 ICalTime *itt;
1820
1821 itt = i_cal_property_get_dtend (prop);
1822 if (itt && i_cal_time_is_valid_time (itt) && i_cal_time_is_utc (itt)) {
1823 i_cal_time_set_timezone (itt, NULL);
1824 i_cal_property_set_dtend (prop, itt);
1825 changed = TRUE;
1826 }
1827
1828 g_clear_object (&itt);
1829 }
1830 g_clear_object (&prop);
1831
1832 if (changed)
1833 *out_object = i_cal_component_as_ical_string (icomp);
1834
1835 g_object_unref (icomp);
1836
1837 return TRUE;
1838 }
1839
1840 static void
1841 ecb_mapi_migrate (ECalBackendMAPI *cbmapi,
1842 ECalCache *cal_cache,
1843 gint data_version)
1844 {
1845 if (data_version < 1) {
1846 /* DTSTART/DTEND stores with both TZID and 'Z' suffix */
1847 e_cache_foreach_update (E_CACHE (cal_cache), E_CACHE_EXCLUDE_DELETED, NULL,
1848 ecb_mapi_update_tzid_cb, NULL, NULL, NULL);
1849 }
1850 }
1851
1852 static gchar *
1853 ecb_mapi_dup_component_revision_cb (ECalCache *cal_cache,
1854 ICalComponent *icomp)
1855 {
1856 ICalProperty *prop;
1857 ICalTime *itt;
1858 gchar *res;
1859
1860 g_return_val_if_fail (I_CAL_IS_COMPONENT (icomp), NULL);
1861
1862 prop = i_cal_component_get_first_property (icomp, I_CAL_LASTMODIFIED_PROPERTY);
1863 if (!prop)
1864 return NULL;
1865
1866 itt = i_cal_property_get_lastmodified (prop);
1867 res = i_cal_time_as_ical_string (itt);
1868
1869 g_clear_object (&prop);
1870 g_clear_object (&itt);
1871
1872 return res;
1873 }
1874
1875 static void
1876 ecb_mapi_constructed (GObject *object)
1877 {
1878 ECalBackendMAPI *cbmapi = E_CAL_BACKEND_MAPI (object);
1879 ECalCache *cal_cache;
1880 gint data_version;
1881
1882 /* Chaing up to parent's method */
1883 G_OBJECT_CLASS (e_cal_backend_mapi_parent_class)->constructed (object);
1884
1885 /* Reset the connectable, it steals data from Authentication extension,
1886 where is written no address */
1887 e_backend_set_connectable (E_BACKEND (object), NULL);
1888
1889 e_cal_backend_set_writable (E_CAL_BACKEND (cbmapi), TRUE);
1890
1891 cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbmapi));
1892
1893 g_signal_connect (cal_cache, "dup-component-revision",
1894 G_CALLBACK (ecb_mapi_dup_component_revision_cb), NULL);
1895
1896 data_version = e_cache_get_key_int (E_CACHE (cal_cache), EMA_DATA_VERSION_KEY, NULL);
1897
1898 if (EMA_DATA_VERSION != data_version) {
1899 GError *local_error = NULL;
1900
1901 ecb_mapi_migrate (cbmapi, cal_cache, data_version);
1902
1903 if (!e_cache_set_key_int (E_CACHE (cal_cache), EMA_DATA_VERSION_KEY, EMA_DATA_VERSION, &local_error)) {
1904 g_warning ("%s: Failed to store data version: %s\n", G_STRFUNC, local_error ? local_error->message : "Unknown error");
1905 }
1906
1907 g_clear_error (&local_error);
1908 }
1909
1910 g_clear_object (&cal_cache);
1911 }
1912
1913 static void
1914 ecb_mapi_dispose (GObject *object)
1915 {
1916 ECalBackendMAPI *cbmapi = E_CAL_BACKEND_MAPI (object);
1917
1918 g_clear_object (&cbmapi->priv->conn);
1919
1920 /* Chain up to parent's method */
1921 G_OBJECT_CLASS (e_cal_backend_mapi_parent_class)->dispose (object);
1922 }
1923
1924 static void
1925 ecb_mapi_finalize (GObject *object)
1926 {
1927 ECalBackendMAPI *cbmapi = E_CAL_BACKEND_MAPI (object);
1928
1929 g_rec_mutex_clear (&cbmapi->priv->conn_lock);
1930
1931 /* Chain up to parent's method */
1932 G_OBJECT_CLASS (e_cal_backend_mapi_parent_class)->finalize (object);
1933 }
1934
1935 static void
1936 e_cal_backend_mapi_class_init (ECalBackendMAPIClass *klass)
1937 {
1938 GObjectClass *object_class;
1939 EBackendClass *backend_class;
1940 ECalBackendClass *cal_backend_class;
1941 ECalBackendSyncClass *sync_backend_class;
1942 ECalMetaBackendClass *meta_backend_class;
1943
1944 meta_backend_class = E_CAL_META_BACKEND_CLASS (klass);
1945 meta_backend_class->connect_sync = ecb_mapi_connect_sync;
1946 meta_backend_class->disconnect_sync = ecb_mapi_disconnect_sync;
1947 meta_backend_class->get_changes_sync = ecb_mapi_get_changes_sync;
1948 meta_backend_class->list_existing_sync = ecb_mapi_list_existing_sync;
1949 meta_backend_class->load_component_sync = ecb_mapi_load_component_sync;
1950 meta_backend_class->save_component_sync = ecb_mapi_save_component_sync;
1951 meta_backend_class->remove_component_sync = ecb_mapi_remove_component_sync;
1952
1953 cal_backend_class = E_CAL_BACKEND_CLASS (klass);
1954 cal_backend_class->impl_get_backend_property = ecb_mapi_get_backend_property;
1955
1956 sync_backend_class = E_CAL_BACKEND_SYNC_CLASS (klass);
1957 sync_backend_class->send_objects_sync = ecb_mapi_send_objects_sync;
1958 sync_backend_class->get_free_busy_sync = ecb_mapi_get_free_busy_sync;
1959
1960 backend_class = E_BACKEND_CLASS (klass);
1961 backend_class->get_destination_address = ecb_mapi_get_destination_address;
1962
1963 object_class = G_OBJECT_CLASS (klass);
1964 object_class->constructed = ecb_mapi_constructed;
1965 object_class->dispose = ecb_mapi_dispose;
1966 object_class->finalize = ecb_mapi_finalize;
1967 }
1968
1969 static void
1970 e_cal_backend_mapi_init (ECalBackendMAPI *cbmapi)
1971 {
1972 cbmapi->priv = e_cal_backend_mapi_get_instance_private (cbmapi);
1973
1974 g_rec_mutex_init (&cbmapi->priv->conn_lock);
1975 }