1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /* e-book-backend-file.c - File contact backend.
4  *
5  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6  * Copyright (C) 2012 Intel Corporation
7  *
8  * This library is free software: you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library. If not, see <http://www.gnu.org/licenses/>.
19  *
20  * Authors: Nat Friedman <nat@novell.com>
21  *          Chris Toshok <toshok@ximian.com>
22  *          Hans Petter Jansson <hpj@novell.com>
23  *          Tristan Van Berkom <tristanvb@openismus.com>
24  */
25 
26 #include "evolution-data-server-config.h"
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <time.h>
34 #include <errno.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 
38 #include <glib/gstdio.h>
39 #include <glib/gi18n-lib.h>
40 
41 #include "e-book-backend-file.h"
42 
43 #ifdef HAVE_LIBDB
44 #include "e-book-backend-file-migrate-bdb.h"
45 #endif
46 
47 #define d(x)
48 
49 #define E_BOOK_BACKEND_FILE_VERSION_NAME "PAS-DB-VERSION"
50 #define E_BOOK_BACKEND_FILE_VERSION "0.2"
51 
52 #define E_BOOK_BACKEND_FILE_REVISION_NAME "PAS-DB-REVISION"
53 
54 #define PAS_ID_PREFIX "pas-id-"
55 
56 /* We need to specify the folderid in order to properly
57  * migrate data from the old EBookSqliteDB API.
58  */
59 #define SQLITEDB_FOLDER_ID   "folder_id"
60 #define SQLITE_REVISION_KEY  "revision"
61 
62 /* Forward Declarations */
63 static void	e_book_backend_file_initable_init
64 						(GInitableIface *iface);
65 
66 struct _EBookBackendFilePrivate {
67 	gchar     *base_directory;
68 	gchar     *photo_dirname;
69 	gchar     *revision;
70 	gchar     *locale;
71 	volatile gint rev_counter;
72 	gboolean   revision_guards;
73 	GRWLock    lock;
74 	GList     *cursors;
75 
76 	EBookSqlite *sqlitedb;
77 };
78 
79 G_DEFINE_TYPE_WITH_CODE (
80 	EBookBackendFile,
81 	e_book_backend_file,
82 	E_TYPE_BOOK_BACKEND_SYNC,
83 	G_ADD_PRIVATE (EBookBackendFile)
84 	G_IMPLEMENT_INTERFACE (
85 		G_TYPE_INITABLE,
86 		e_book_backend_file_initable_init))
87 
88 /****************************************************************
89  *                   File Management helper APIs                *
90  ****************************************************************/
91 typedef enum {
92 	GET_PATH_DB_DIR,
93 	GET_PATH_PHOTO_DIR
94 } GetPathType;
95 
96 typedef enum {
97 	STATUS_NORMAL = 0,
98 	STATUS_MODIFIED,
99 	STATUS_ERROR
100 } PhotoModifiedStatus;
101 
102 static gboolean
remove_file(const gchar * filename,GError ** error)103 remove_file (const gchar *filename,
104              GError **error)
105 {
106 	if (-1 == g_unlink (filename)) {
107 		if (errno == EACCES || errno == EPERM) {
108 			g_set_error_literal (
109 				error, E_CLIENT_ERROR,
110 				E_CLIENT_ERROR_PERMISSION_DENIED,
111 				e_client_error_to_string (
112 				E_CLIENT_ERROR_PERMISSION_DENIED));
113 		} else {
114 			g_set_error (
115 				error, E_CLIENT_ERROR,
116 				E_CLIENT_ERROR_OTHER_ERROR,
117 				_("Failed to remove file “%s”: %s"),
118 				filename, g_strerror (errno));
119 		}
120 		return FALSE;
121 	}
122 
123 	return TRUE;
124 }
125 
126 static gboolean
create_directory(const gchar * dirname,GError ** error)127 create_directory (const gchar *dirname,
128                   GError **error)
129 {
130 	gint rv;
131 
132 	rv = g_mkdir_with_parents (dirname, 0700);
133 	if (rv == -1 && errno != EEXIST) {
134 		g_warning ("failed to make directory %s: %s", dirname, g_strerror (errno));
135 		if (errno == EACCES || errno == EPERM)
136 			g_set_error_literal (
137 				error, E_CLIENT_ERROR,
138 				E_CLIENT_ERROR_PERMISSION_DENIED,
139 				e_client_error_to_string (
140 				E_CLIENT_ERROR_PERMISSION_DENIED));
141 		else
142 			g_set_error (
143 				error, E_CLIENT_ERROR,
144 				E_CLIENT_ERROR_OTHER_ERROR,
145 				_("Failed to make directory %s: %s"),
146 				dirname, g_strerror (errno));
147 		return FALSE;
148 	}
149 	return TRUE;
150 }
151 
152 static gchar *
check_remove_uri_for_field(EContact * old_contact,EContact * new_contact,EContactField field)153 check_remove_uri_for_field (EContact *old_contact,
154                             EContact *new_contact,
155                             EContactField field)
156 {
157 	EContactPhoto *old_photo = NULL, *new_photo = NULL;
158 	gchar         *uri = NULL;
159 
160 	old_photo = e_contact_get (old_contact, field);
161 	if (!old_photo)
162 		return NULL;
163 
164 	if (new_contact) {
165 
166 		new_photo = e_contact_get (new_contact, field);
167 
168 		if (new_photo == NULL ||
169 		    g_ascii_strcasecmp (old_photo->data.uri, new_photo->data.uri))
170 			uri = g_strdup (old_photo->data.uri);
171 	} else {
172 		uri = g_strdup (old_photo->data.uri);
173 	}
174 
175 	e_contact_photo_free (old_photo);
176 	e_contact_photo_free (new_photo);
177 
178 	return uri;
179 }
180 
181 static void
maybe_delete_uri(EBookBackendFile * bf,const gchar * uri)182 maybe_delete_uri (EBookBackendFile *bf,
183                   const gchar *uri)
184 {
185 	GError *error = NULL;
186 	gchar  *filename;
187 
188 	/* A uri that does not give us a filename is certainly not
189 	 * a uri that we created for a local file, just skip it */
190 	if ((filename = g_filename_from_uri (uri, NULL, NULL)) == NULL)
191 		return;
192 
193 	/* If the file is in our path it belongs to us and we need to delete it.
194 	 */
195 	if (bf->priv->photo_dirname &&
196 	    !strncmp (bf->priv->photo_dirname, filename, strlen (bf->priv->photo_dirname))) {
197 
198 		d (g_print ("Deleting uri file: %s\n", filename));
199 
200 		/* Deleting uris should not cause the backend to fail to update
201 		 * a contact so the best we can do from here is log warnings
202 		 * when we fail to unlink a file from the disk.
203 		 */
204 		if (!remove_file (filename, &error)) {
205 			g_warning ("Unable to cleanup photo uri: %s", error->message);
206 			g_error_free (error);
207 		}
208 	}
209 
210 	g_free (filename);
211 }
212 
213 static void
maybe_delete_unused_uris(EBookBackendFile * bf,EContact * old_contact,EContact * new_contact)214 maybe_delete_unused_uris (EBookBackendFile *bf,
215                           EContact *old_contact,
216                           EContact *new_contact)
217 {
218 	gchar             *uri_photo, *uri_logo;
219 
220 	g_return_if_fail (old_contact != NULL);
221 
222 	/* If there is no new contact, collect all the uris to delete from old_contact
223 	 *
224 	 * Otherwise, if any of the photo uri fields have changed in new_contact, then collect the
225 	 * old uris for those fields from old_contact to delete
226 	 */
227 	uri_photo = check_remove_uri_for_field (old_contact, new_contact, E_CONTACT_PHOTO);
228 	uri_logo = check_remove_uri_for_field (old_contact, new_contact, E_CONTACT_LOGO);
229 
230 	if (uri_photo) {
231 		maybe_delete_uri (bf, uri_photo);
232 		g_free (uri_photo);
233 	}
234 
235 	if (uri_logo) {
236 		maybe_delete_uri (bf, uri_logo);
237 		g_free (uri_logo);
238 	}
239 }
240 
241 static gchar *
e_book_backend_file_extract_path_from_source(ESourceRegistry * registry,ESource * source,GetPathType path_type)242 e_book_backend_file_extract_path_from_source (ESourceRegistry *registry,
243                                               ESource *source,
244                                               GetPathType path_type)
245 {
246 	ESource *builtin_source;
247 	const gchar *user_data_dir;
248 	const gchar *uid;
249 	gchar *filename = NULL;
250 
251 	uid = e_source_get_uid (source);
252 	g_return_val_if_fail (uid != NULL, NULL);
253 
254 	user_data_dir = e_get_user_data_dir ();
255 
256 	builtin_source = e_source_registry_ref_builtin_address_book (registry);
257 
258 	/* XXX Backward-compatibility hack:
259 	 *
260 	 * The special built-in "Personal" data source UIDs are now named
261 	 * "system-$COMPONENT" but since the data directories are already
262 	 * split out by component, we'll continue to use the old "system"
263 	 * directories for these particular data sources. */
264 	if (e_source_equal (source, builtin_source))
265 		uid = "system";
266 
267 	switch (path_type) {
268 		case GET_PATH_DB_DIR:
269 			filename = g_build_filename (
270 				user_data_dir, "addressbook", uid, NULL);
271 			break;
272 		case GET_PATH_PHOTO_DIR:
273 			filename = g_build_filename (
274 				user_data_dir, "addressbook", uid, "photos", NULL);
275 			break;
276 		default:
277 			g_warn_if_reached ();
278 	}
279 
280 	g_object_unref (builtin_source);
281 
282 	return filename;
283 }
284 
285 static gchar *
safe_name_for_photo(EBookBackendFile * bf,EContact * contact,EContactPhoto * photo,EContactField field)286 safe_name_for_photo (EBookBackendFile *bf,
287                      EContact *contact,
288                      EContactPhoto *photo,
289                      EContactField field)
290 {
291 	gchar *fullname = NULL, *name, *str;
292 	gchar *suffix = NULL;
293 	gint i = 0;
294 
295 	g_return_val_if_fail (photo->type == E_CONTACT_PHOTO_TYPE_INLINED, NULL);
296 
297 	/* Get a suitable filename extension */
298 	if (photo->data.inlined.mime_type != NULL &&
299 	    photo->data.inlined.mime_type[0] != '\0' &&
300 	    g_ascii_strcasecmp (photo->data.inlined.mime_type, "image/X-EVOLUTION-UNKNOWN") != 0) {
301 		suffix = g_uri_escape_string (
302 			photo->data.inlined.mime_type,
303 			NULL, TRUE);
304 	} else {
305 		gchar *mime_type = NULL;
306 		gchar *content_type = NULL;
307 
308 		content_type = g_content_type_guess (
309 			NULL,
310 			photo->data.inlined.data,
311 			photo->data.inlined.length,
312 			NULL);
313 
314 		if (content_type)
315 			mime_type = g_content_type_get_mime_type (content_type);
316 
317 		if (mime_type)
318 			suffix = g_uri_escape_string (mime_type, NULL, TRUE);
319 		else
320 			suffix = g_strdup ("data");
321 
322 		g_free (mime_type);
323 		g_free (content_type);
324 	}
325 
326 	/* Replace any percent in the text with a dash, to avoid escaping issues in URI-s */
327 	str = suffix;
328 	while (str = strchr (str, '%'), str) {
329 		*str = '-';
330 	}
331 
332 	/* Create a filename based on the uid/field */
333 	name = g_strconcat (
334 		e_contact_get_const (contact, E_CONTACT_UID), "_",
335 		e_contact_field_name (field), NULL);
336 	name = g_strdelimit (name, NULL, '_');
337 
338 	/* Replace any percent in the text with a dash, to avoid escaping issues in URI-s */
339 	str = name;
340 	while (str = strchr (str, '%'), str) {
341 		*str = '-';
342 	}
343 
344 	do {
345 		g_free (fullname);
346 
347 		str = e_filename_mkdir_encoded (bf->priv->photo_dirname, name, NULL, i);
348 		fullname = g_strdup_printf ("%s.%s", str, suffix);
349 		g_free (str);
350 
351 		i++;
352 	} while (g_file_test (fullname, G_FILE_TEST_EXISTS));
353 
354 	g_free (name);
355 	g_free (suffix);
356 
357 	return fullname;
358 }
359 
360 static gchar *
hard_link_photo(EBookBackendFile * bf,EContact * contact,EContactField field,const gchar * src_filename,GError ** error)361 hard_link_photo (EBookBackendFile *bf,
362                  EContact *contact,
363                  EContactField field,
364                  const gchar *src_filename,
365                  GError **error)
366 {
367 	gchar *fullname = NULL, *name, *str;
368 	gint   i = 0, ret;
369 	const gchar *suffix;
370 
371 	/* Copy over the file suffix */
372 	suffix = strrchr (src_filename, '.');
373 	if (suffix)
374 		suffix++;
375 
376 	if (!suffix)
377 		suffix = "data";
378 
379 	/* Create a filename based on uid/field */
380 	name = g_strconcat (
381 		e_contact_get_const (contact, E_CONTACT_UID), "_",
382 		e_contact_field_name (field), NULL);
383 	name = g_strdelimit (name, NULL, '_');
384 
385 	do {
386 		g_free (fullname);
387 
388 		str = e_filename_mkdir_encoded (bf->priv->photo_dirname, name, NULL, i);
389 		fullname = g_strdup_printf ("%s.%s", str, suffix);
390 		g_free (str);
391 
392 		i++;
393 
394 		#ifdef G_OS_WIN32
395 		{
396 		GFile *src_file = g_file_new_for_path (src_filename);
397 		GFile *dst_file = g_file_new_for_path (fullname);
398 		GError *copy_error = NULL;
399 
400 		g_file_copy (src_file, dst_file, G_FILE_COPY_NONE,
401 		             NULL, NULL, NULL, &copy_error);
402 
403 		if (copy_error != NULL) {
404 			g_error_free (copy_error);
405 			ret = -1;
406 		} else
407 			ret = 0;
408 
409 		g_object_unref (src_file);
410 		g_object_unref (dst_file);
411 		}
412 		#else
413 		ret = link (src_filename, fullname);
414 		#endif
415 
416 	} while (ret < 0 && errno == EEXIST);
417 
418 	if (ret < 0) {
419 		if (errno == EACCES || errno == EPERM) {
420 			g_set_error_literal (
421 				error, E_CLIENT_ERROR,
422 				E_CLIENT_ERROR_PERMISSION_DENIED,
423 				e_client_error_to_string (
424 				E_CLIENT_ERROR_PERMISSION_DENIED));
425 		} else {
426 			g_set_error (
427 				error, E_CLIENT_ERROR,
428 				E_CLIENT_ERROR_OTHER_ERROR,
429 				_("Failed to create hardlink for resource “%s”: %s"),
430 				src_filename, g_strerror (errno));
431 		}
432 		g_free (fullname);
433 		fullname = NULL;
434 	}
435 
436 	g_free (name);
437 
438 	return fullname;
439 }
440 
441 static gboolean
is_backend_owned_uri(EBookBackendFile * bf,const gchar * uri)442 is_backend_owned_uri (EBookBackendFile *bf,
443                       const gchar *uri)
444 {
445 	gchar     *filename;
446 	gchar     *dirname;
447 	gboolean   owned_uri;
448 
449 	/* Errors converting from uri definitily indicate it was
450 	 * not our uri to begin with, so just disregard this error. */
451 	filename = g_filename_from_uri (uri, NULL, NULL);
452 	if (!filename)
453 		return FALSE;
454 
455 	dirname = g_path_get_dirname (filename);
456 
457 	owned_uri = bf->priv->photo_dirname && (strcmp (dirname, bf->priv->photo_dirname) == 0);
458 
459 	g_free (filename);
460 	g_free (dirname);
461 
462 	return owned_uri;
463 }
464 
465 static PhotoModifiedStatus
maybe_transform_vcard_field_for_photo(EBookBackendFile * bf,EContact * old_contact,EContact * contact,EContactField field,GError ** error)466 maybe_transform_vcard_field_for_photo (EBookBackendFile *bf,
467                                        EContact *old_contact,
468                                        EContact *contact,
469                                        EContactField field,
470                                        GError **error)
471 {
472 	PhotoModifiedStatus  status = STATUS_NORMAL;
473 	EContactPhoto       *photo;
474 
475 	if (field != E_CONTACT_PHOTO && field != E_CONTACT_LOGO)
476 		return status;
477 
478 	photo = e_contact_get (contact, field);
479 	if (!photo)
480 		return status;
481 
482 	if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
483 		EContactPhoto *new_photo;
484 		gchar         *new_photo_path;
485 		gchar         *uri;
486 
487 		/* Create a unique filename with an extension (hopefully) based on the mime type */
488 		new_photo_path = safe_name_for_photo (bf, contact, photo, field);
489 
490 		if ((uri =
491 		     g_filename_to_uri (new_photo_path, NULL, error)) == NULL) {
492 
493 			status = STATUS_ERROR;
494 		} else if (!g_file_set_contents (new_photo_path,
495 						 (const gchar *) photo->data.inlined.data,
496 						 photo->data.inlined.length,
497 						 error)) {
498 
499 			status = STATUS_ERROR;
500 		} else {
501 			new_photo = e_contact_photo_new ();
502 			new_photo->type = E_CONTACT_PHOTO_TYPE_URI;
503 			new_photo->data.uri = g_strdup (uri);
504 
505 			e_contact_set (contact, field, new_photo);
506 
507 			d (g_print ("Backend modified incomming binary blob to be %s:\n", uri));
508 
509 			status = STATUS_MODIFIED;
510 
511 			e_contact_photo_free (new_photo);
512 		}
513 
514 		g_free (uri);
515 		g_free (new_photo_path);
516 
517 	} else { /* E_CONTACT_PHOTO_TYPE_URI */
518 		const gchar       *uid;
519 		EContactPhoto     *old_photo = NULL, *new_photo;
520 
521 		/* First determine that the new contact uri points to our 'photos' directory,
522 		 * if not then we do nothing
523 		 */
524 		if (!is_backend_owned_uri (bf, photo->data.uri))
525 			goto done;
526 
527 		/* Now check if the uri is changed from the BDB copy
528 		 */
529 		uid = e_contact_get_const (contact, E_CONTACT_UID);
530 		if (uid == NULL) {
531 			g_set_error_literal (
532 				error, E_CLIENT_ERROR,
533 				E_CLIENT_ERROR_OTHER_ERROR,
534 				_("No UID in the contact"));
535 			status = STATUS_ERROR;
536 			goto done;
537 		}
538 
539 		if (old_contact)
540 			old_photo = e_contact_get (old_contact, field);
541 
542 		/* Unless we are receiving the same uri that we already have
543 		 * stored in the BDB... */
544 		if (!old_photo || old_photo->type == E_CONTACT_PHOTO_TYPE_INLINED ||
545 		    g_ascii_strcasecmp (old_photo->data.uri, photo->data.uri) != 0) {
546 			gchar *filename;
547 			gchar *new_filename;
548 			gchar *new_uri = NULL;
549 
550 			/* ... Assume that the incomming uri belongs to another contact
551 			 * still in the BDB. Lets go ahead and create a hard link to the
552 			 * photo file and create a new name for the incomming uri, and
553 			 * use that in the incomming contact to save in place.
554 			 *
555 			 * This piece of code is here to ensure there are no problems if
556 			 * the libebook user decides to cross-reference and start "sharing"
557 			 * uris that we've previously stored in the photo directory.
558 			 *
559 			 * We use the hard-link here to off-load the necessary ref-counting
560 			 * logic to the file-system.
561 			 */
562 			filename = g_filename_from_uri (photo->data.uri, NULL, NULL);
563 			g_return_val_if_fail (filename, STATUS_NORMAL); /* we already checked this with 'is_backend_owned_uri ()' */
564 
565 			new_filename = hard_link_photo (bf, contact, field, filename, error);
566 
567 			if (!new_filename)
568 				status = STATUS_ERROR;
569 			else if ((new_uri = g_filename_to_uri (new_filename, NULL, error)) == NULL) {
570 				/* If we fail here... we need to clean up the hardlink we just created */
571 				GError *local_err = NULL;
572 				if (!remove_file (new_filename, &local_err)) {
573 					g_warning ("Unable to cleanup photo uri: %s", local_err->message);
574 					g_error_free (local_err);
575 				}
576 				status = STATUS_ERROR;
577 			} else {
578 
579 				new_photo = e_contact_photo_new ();
580 				new_photo->type = E_CONTACT_PHOTO_TYPE_URI;
581 				new_photo->data.uri = new_uri;
582 
583 				e_contact_set (contact, field, new_photo);
584 
585 				d (g_print ("Backend modified incomming shared uri to be %s:\n", new_uri));
586 
587 				e_contact_photo_free (new_photo);
588 				status = STATUS_MODIFIED;
589 			}
590 			g_free (new_filename);
591 			g_free (filename);
592 		}
593 
594 		if (old_photo)
595 			e_contact_photo_free (old_photo);
596 
597 	}
598 
599  done:
600 	e_contact_photo_free (photo);
601 
602 	return status;
603 }
604 
605 /*
606  * When a contact is added or modified we receive a vCard,
607  * this function checks if we've received inline data
608  * and replaces it with a uri notation.
609  *
610  * If this function modifies 'contact' then it will
611  * return the 'modified' status and 'vcard_ret' (if specified)
612  * will be set to a newly allocated vcard string.
613  */
614 static PhotoModifiedStatus
maybe_transform_vcard_for_photo(EBookBackendFile * bf,EContact * old_contact,EContact * contact,GError ** error)615 maybe_transform_vcard_for_photo (EBookBackendFile *bf,
616                                  EContact *old_contact,
617                                  EContact *contact,
618                                  GError **error)
619 {
620 	PhotoModifiedStatus status;
621 	gboolean            modified = FALSE;
622 
623 	status = maybe_transform_vcard_field_for_photo (
624 		bf, old_contact, contact,
625 		E_CONTACT_PHOTO, error);
626 	modified = (status == STATUS_MODIFIED);
627 
628 	if (status != STATUS_ERROR) {
629 		status = maybe_transform_vcard_field_for_photo (
630 			bf, old_contact, contact,
631 			E_CONTACT_LOGO, error);
632 		modified = modified || (status == STATUS_MODIFIED);
633 	}
634 
635 	if (status != STATUS_ERROR && modified)
636 		status = STATUS_MODIFIED;
637 
638 	return status;
639 }
640 
641 /****************************************************************
642  *                     Global Revisioning Tools                 *
643  ****************************************************************/
644 static gchar *
e_book_backend_file_create_unique_id(void)645 e_book_backend_file_create_unique_id (void)
646 {
647 	gchar *uid = e_util_generate_uid (), *prefixed;
648 
649 	prefixed = g_strconcat (PAS_ID_PREFIX, uid, NULL);
650 	g_free (uid);
651 
652 	return prefixed;
653 }
654 
655 static gchar *
e_book_backend_file_new_revision(EBookBackendFile * bf,gboolean with_counter)656 e_book_backend_file_new_revision (EBookBackendFile *bf,
657 				  gboolean with_counter)
658 {
659 	gchar time_string[100] = {0};
660 	const struct tm *tm = NULL;
661 	time_t t;
662 
663 	t = time (NULL);
664 	tm = gmtime (&t);
665 	if (tm) {
666 		struct tm ttm = *tm;
667 
668 		if (!with_counter && bf->priv->revision_guards) {
669 			gint value = g_atomic_int_add (&bf->priv->rev_counter, 1);
670 
671 			/* Artificial time, which encodes the revision counter */
672 			ttm.tm_sec = value % 60;
673 			ttm.tm_min = (value / 60 ) % 60;
674 			ttm.tm_hour = (value / 3600) % 24;
675 		}
676 
677 		strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", &ttm);
678 	}
679 
680 	if (with_counter)
681 		return g_strdup_printf ("%s(%d)", time_string, g_atomic_int_add (&bf->priv->rev_counter, 1));
682 
683 	return g_strdup (time_string);
684 }
685 
686 /* For now just bump the revision and set it in the DB every
687  * time the revision bumps, this is the safest approach and
688  * its unclear so far if bumping the revision string for
689  * every DB modification is going to really be an overhead.
690  */
691 static gboolean
e_book_backend_file_bump_revision(EBookBackendFile * bf,GError ** error)692 e_book_backend_file_bump_revision (EBookBackendFile *bf,
693                                    GError **error)
694 {
695 	GError *local_error = NULL;
696 	gchar *new_revision;
697 	gboolean success;
698 
699 	new_revision = e_book_backend_file_new_revision (bf, TRUE);
700 	success = e_book_sqlite_set_key_value (
701 		bf->priv->sqlitedb,
702 		SQLITE_REVISION_KEY,
703 		new_revision,
704 		&local_error);
705 
706 	if (success) {
707 		g_free (bf->priv->revision);
708 		bf->priv->revision = new_revision;
709 
710 		e_book_backend_notify_property_changed (E_BOOK_BACKEND (bf),
711 							E_BOOK_BACKEND_PROPERTY_REVISION,
712 							bf->priv->revision);
713 	} else {
714 		g_free (new_revision);
715 		g_warning (
716 			G_STRLOC ": Error setting database revision: %s",
717 			local_error->message);
718 		g_propagate_error (error, local_error);
719 	}
720 
721 	return success;
722 }
723 
724 static void
e_book_backend_file_load_revision(EBookBackendFile * bf)725 e_book_backend_file_load_revision (EBookBackendFile *bf)
726 {
727 	GError *error = NULL;
728 
729 	if (!e_book_sqlite_get_key_value (bf->priv->sqlitedb,
730 					  SQLITE_REVISION_KEY,
731 					  &bf->priv->revision,
732 					  &error)) {
733 		g_warning (
734 			G_STRLOC ": Error loading database revision: %s",
735 			error ? error->message : "Unknown error");
736 		g_clear_error (&error);
737 	} else if (bf->priv->revision == NULL) {
738 		e_book_backend_file_bump_revision (bf, NULL);
739 	}
740 }
741 
742 static void
e_book_backend_file_load_locale(EBookBackendFile * bf)743 e_book_backend_file_load_locale (EBookBackendFile *bf)
744 {
745 	GError *error = NULL;
746 
747 	if (!e_book_sqlite_get_locale (bf->priv->sqlitedb,
748 				       &bf->priv->locale,
749 				       &error)) {
750 		g_warning (
751 			G_STRLOC ": Error loading database locale setting: %s",
752 			error ? error->message : "Unknown error");
753 		g_clear_error (&error);
754 	}
755 }
756 
757 static void
set_revision(EBookBackendFile * bf,EContact * contact)758 set_revision (EBookBackendFile *bf,
759               EContact *contact)
760 {
761 	gchar *rev;
762 
763 	rev = e_book_backend_file_new_revision (bf, FALSE);
764 	e_contact_set (contact, E_CONTACT_REV, rev);
765 	g_free (rev);
766 }
767 
768 /****************************************************************
769  *                   Dealing with cursor updates                *
770  ****************************************************************/
771 static void
cursors_contact_added(EBookBackendFile * bf,EContact * contact)772 cursors_contact_added (EBookBackendFile *bf,
773                        EContact *contact)
774 {
775 	GList *l;
776 
777 	for (l = bf->priv->cursors; l; l = l->next) {
778 		EDataBookCursor *cursor = l->data;
779 
780 		e_data_book_cursor_contact_added (cursor, contact);
781 	}
782 }
783 
784 static void
cursors_contact_removed(EBookBackendFile * bf,EContact * contact)785 cursors_contact_removed (EBookBackendFile *bf,
786                          EContact *contact)
787 {
788 	GList *l;
789 
790 	for (l = bf->priv->cursors; l; l = l->next) {
791 		EDataBookCursor *cursor = l->data;
792 
793 		e_data_book_cursor_contact_removed (cursor, contact);
794 	}
795 }
796 
797 /****************************************************************
798  *                   Main Backend Implementation                *
799  ****************************************************************/
800 
801 /**
802  * This method will return TRUE if all the contacts were properly created.
803  * If at least one contact fails, the method will return FALSE, all
804  * changes will be reverted (the @contacts list will stay empty) and
805  * @perror will be set.
806  */
807 static gboolean
do_create(EBookBackendFile * bf,const gchar * const * vcards,GSList ** out_contacts,GCancellable * cancellable,GError ** error)808 do_create (EBookBackendFile *bf,
809            const gchar * const *vcards,
810            GSList **out_contacts,
811            GCancellable *cancellable,
812            GError **error)
813 {
814 	PhotoModifiedStatus status = STATUS_NORMAL;
815 	guint ii, length;
816 	GError *local_error = NULL;
817 
818 	length = g_strv_length ((gchar **) vcards);
819 
820 	for (ii = 0; ii < length; ii++) {
821 		gchar           *id;
822 		const gchar     *rev;
823 		EContact        *contact;
824 
825 		contact = e_contact_new_from_vcard (vcards[ii]);
826 
827 		/* Preserve original UID, create a unique UID if needed */
828 		if (e_contact_get_const (contact, E_CONTACT_UID) == NULL) {
829 			id = e_book_backend_file_create_unique_id ();
830 			e_contact_set (contact, E_CONTACT_UID, id);
831 			g_free (id);
832 		}
833 
834 		rev = e_contact_get_const (contact, E_CONTACT_REV);
835 		if (!(rev && *rev))
836 			set_revision (bf, contact);
837 
838 		status = maybe_transform_vcard_for_photo (bf, NULL, contact, error);
839 
840 		if (status != STATUS_ERROR) {
841 
842 			/* Contact was added successfully. */
843 			*out_contacts = g_slist_prepend (*out_contacts, contact);
844 		} else {
845 			/* Contact could not be transformed */
846 			g_warning (
847 				G_STRLOC ": Error transforming vcard with image data %s",
848 				(error && *error) ? (*error)->message :
849 				"Unknown error transforming vcard");
850 			g_object_unref (contact);
851 
852 			/* Abort as soon as an error occurs */
853 			break;
854 		}
855 	}
856 
857 	if (status != STATUS_ERROR) {
858 		GSList *link;
859 
860 		if (!e_book_sqlite_add_contacts (bf->priv->sqlitedb,
861 						 *out_contacts, NULL, FALSE,
862 						 cancellable,
863 						 &local_error)) {
864 
865 			if (g_error_matches (local_error,
866 					     E_BOOK_SQLITE_ERROR,
867 					     E_BOOK_SQLITE_ERROR_CONSTRAINT)) {
868 				g_set_error (
869 					error, E_BOOK_CLIENT_ERROR,
870 					E_BOOK_CLIENT_ERROR_CONTACT_ID_ALREADY_EXISTS,
871 					_("Conflicting UIDs found in added contacts"));
872 				g_clear_error (&local_error);
873 			} else {
874 				g_warning ("Failed to add contacts: %s", local_error->message);
875 				g_propagate_error (error, local_error);
876 			}
877 
878 			status = STATUS_ERROR;
879 		}
880 
881 		/* After adding any contacts, notify any cursors that the new contacts are added */
882 		for (link = *out_contacts; link; link = g_slist_next (link)) {
883 			cursors_contact_added (bf, link->data);
884 		}
885 	}
886 
887 	return (status != STATUS_ERROR);
888 }
889 
890 typedef struct {
891 	EBookBackendFile *bf;
892 	GThread *thread;
893 	EFlag *running;
894 } FileBackendSearchClosure;
895 
896 static void
closure_destroy(FileBackendSearchClosure * closure)897 closure_destroy (FileBackendSearchClosure *closure)
898 {
899 	d (printf ("destroying search closure\n"));
900 	e_flag_free (closure->running);
901 	if (closure->thread)
902 		g_thread_unref (closure->thread);
903 	g_free (closure);
904 }
905 
906 static FileBackendSearchClosure *
init_closure(EDataBookView * book_view,EBookBackendFile * bf)907 init_closure (EDataBookView *book_view,
908               EBookBackendFile *bf)
909 {
910 	FileBackendSearchClosure *closure = g_new (FileBackendSearchClosure, 1);
911 
912 	closure->bf = bf;
913 	closure->thread = NULL;
914 	closure->running = e_flag_new ();
915 
916 	g_object_set_data_full (
917 		G_OBJECT (book_view),
918 		"EBookBackendFile.BookView::closure",
919 		closure, (GDestroyNotify) closure_destroy);
920 
921 	return closure;
922 }
923 
924 static FileBackendSearchClosure *
get_closure(EDataBookView * book_view)925 get_closure (EDataBookView *book_view)
926 {
927 	return g_object_get_data (
928 		G_OBJECT (book_view),
929 		"EBookBackendFile.BookView::closure");
930 }
931 
932 static void
notify_update_vcard(EDataBookView * book_view,gboolean prefiltered,const gchar * id,const gchar * vcard)933 notify_update_vcard (EDataBookView *book_view,
934                      gboolean prefiltered,
935                      const gchar *id,
936                      const gchar *vcard)
937 {
938 	if (prefiltered)
939 		e_data_book_view_notify_update_prefiltered_vcard (book_view, id, vcard);
940 	else
941 		e_data_book_view_notify_update_vcard (book_view, id, vcard);
942 }
943 
944 static gboolean
uid_rev_fields(GHashTable * fields_of_interest)945 uid_rev_fields (GHashTable *fields_of_interest)
946 {
947 	GHashTableIter iter;
948 	gpointer key, value;
949 
950 	if (!fields_of_interest || g_hash_table_size (fields_of_interest) > 2)
951 		return FALSE;
952 
953 	g_hash_table_iter_init (&iter, fields_of_interest);
954 	while (g_hash_table_iter_next (&iter, &key, &value)) {
955 		const gchar *field_name = key;
956 		EContactField field = e_contact_field_id (field_name);
957 
958 		if (field != E_CONTACT_UID &&
959 		    field != E_CONTACT_REV)
960 			return FALSE;
961 	}
962 
963 	return TRUE;
964 }
965 
966 static gpointer
book_view_thread(gpointer user_data)967 book_view_thread (gpointer user_data)
968 {
969 	EDataBookView *book_view = user_data;
970 	FileBackendSearchClosure *closure;
971 	EBookBackendFile *bf;
972 	EBookBackendSExp *sexp;
973 	const gchar *query;
974 	GSList *summary_list = NULL, *l;
975 	GHashTable *fields_of_interest;
976 	GError *local_error = NULL;
977 	gboolean meta_contact, success;
978 
979 	g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (book_view), NULL);
980 
981 	closure = get_closure (book_view);
982 	if (!closure) {
983 		g_warning (G_STRLOC ": NULL closure in book view thread");
984 		return NULL;
985 	}
986 	bf = closure->bf;
987 
988 	d (printf ("starting initial population of book view\n"));
989 
990 	/* ref the book view because it'll be removed and unrefed
991 	 * when/if it's stopped */
992 	g_object_ref (book_view);
993 
994 	sexp = e_data_book_view_get_sexp (book_view);
995 	query = e_book_backend_sexp_text (sexp);
996 
997 	fields_of_interest = e_data_book_view_get_fields_of_interest (book_view);
998 	meta_contact = uid_rev_fields (fields_of_interest);
999 
1000 	if (query && !strcmp (query, "(contains \"x-evolution-any-field\" \"\")")) {
1001 		e_data_book_view_notify_progress (book_view, -1, _("Loading..."));
1002 	} else {
1003 		e_data_book_view_notify_progress (book_view, -1, _("Searching..."));
1004 	}
1005 
1006 	d (printf ("signalling parent thread\n"));
1007 	e_flag_set (closure->running);
1008 
1009 	g_rw_lock_reader_lock (&(bf->priv->lock));
1010 	success = e_book_sqlite_search (
1011 		bf->priv->sqlitedb,
1012 		query,
1013 		meta_contact,
1014 		&summary_list,
1015 		NULL, /* GCancellable */
1016 		&local_error);
1017 	g_rw_lock_reader_unlock (&(bf->priv->lock));
1018 
1019 	if (!success) {
1020 		g_warning (G_STRLOC ": Failed to query initial contacts: %s", local_error->message);
1021 		g_error_free (local_error);
1022 		e_data_book_view_notify_complete (
1023 			book_view,
1024 			g_error_new_literal (
1025 				E_CLIENT_ERROR,
1026 				E_CLIENT_ERROR_NOT_OPENED,
1027 				e_client_error_to_string (
1028 				E_CLIENT_ERROR_NOT_OPENED)));
1029 		g_object_unref (book_view);
1030 		return NULL;
1031 	}
1032 
1033 	for (l = summary_list; l; l = l->next) {
1034 		EbSqlSearchData *data = l->data;
1035 		gchar *vcard = NULL;
1036 
1037 		vcard = data->vcard;
1038 		data->vcard = NULL;
1039 
1040 		notify_update_vcard (book_view, TRUE, data->uid, vcard);
1041 		g_free (vcard);
1042 	}
1043 
1044 	g_slist_foreach (summary_list, (GFunc) e_book_sqlite_search_data_free, NULL);
1045 	g_slist_free (summary_list);
1046 
1047 	if (e_flag_is_set (closure->running))
1048 		e_data_book_view_notify_complete (book_view, NULL /* Success */);
1049 
1050 	g_object_unref (book_view);
1051 
1052 	d (printf ("finished population of book view\n"));
1053 
1054 	return NULL;
1055 }
1056 
1057 static void
book_backend_file_dispose(GObject * object)1058 book_backend_file_dispose (GObject *object)
1059 {
1060 	EBookBackendFile *bf;
1061 
1062 	bf = E_BOOK_BACKEND_FILE (object);
1063 
1064 	g_rw_lock_writer_lock (&(bf->priv->lock));
1065 
1066 	if (bf->priv->cursors) {
1067 		g_list_free_full (bf->priv->cursors, g_object_unref);
1068 		bf->priv->cursors = NULL;
1069 	}
1070 
1071 	g_clear_object (&bf->priv->sqlitedb);
1072 
1073 	g_rw_lock_writer_unlock (&(bf->priv->lock));
1074 
1075 	G_OBJECT_CLASS (e_book_backend_file_parent_class)->dispose (object);
1076 }
1077 
1078 static void
book_backend_file_finalize(GObject * object)1079 book_backend_file_finalize (GObject *object)
1080 {
1081 	EBookBackendFilePrivate *priv;
1082 
1083 	priv = E_BOOK_BACKEND_FILE (object)->priv;
1084 
1085 	g_free (priv->photo_dirname);
1086 	g_free (priv->revision);
1087 	g_free (priv->locale);
1088 	g_free (priv->base_directory);
1089 	g_rw_lock_clear (&(priv->lock));
1090 
1091 	/* Chain up to parent's finalize() method. */
1092 	G_OBJECT_CLASS (e_book_backend_file_parent_class)->finalize (object);
1093 }
1094 
1095 static gchar *
book_backend_file_get_backend_property(EBookBackend * backend,const gchar * prop_name)1096 book_backend_file_get_backend_property (EBookBackend *backend,
1097                                         const gchar *prop_name)
1098 {
1099 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1100 
1101 	g_return_val_if_fail (prop_name != NULL, NULL);
1102 
1103 	if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
1104 		return g_strdup ("local,do-initial-query,bulk-adds,bulk-modifies,bulk-removes,contact-lists");
1105 
1106 	} else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
1107 		return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
1108 
1109 	} else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
1110 		GString *fields;
1111 		gint ii;
1112 
1113 		fields = g_string_sized_new (1024);
1114 
1115 		/* XXX we need a way to say "we support everything", since the
1116 		 * file backend does */
1117 		for (ii = 1; ii < E_CONTACT_FIELD_LAST; ii++) {
1118 			if (fields->len > 0)
1119 				g_string_append_c (fields, ',');
1120 			g_string_append (fields, e_contact_field_name (ii));
1121 		}
1122 
1123 		return g_string_free (fields, FALSE);
1124 
1125 	} else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_REVISION)) {
1126 		gchar *prop_value;
1127 
1128 		g_rw_lock_reader_lock (&(bf->priv->lock));
1129 		prop_value = g_strdup (bf->priv->revision);
1130 		g_rw_lock_reader_unlock (&(bf->priv->lock));
1131 
1132 		return prop_value;
1133 	}
1134 
1135 	/* Chain up to parent's method. */
1136 	return E_BOOK_BACKEND_CLASS (e_book_backend_file_parent_class)->impl_get_backend_property (backend, prop_name);
1137 }
1138 
1139 static gboolean
book_backend_file_open_sync(EBookBackendSync * backend,GCancellable * cancellable,GError ** error)1140 book_backend_file_open_sync (EBookBackendSync *backend,
1141                              GCancellable *cancellable,
1142                              GError **error)
1143 {
1144 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1145 	ESource          *source;
1146 	ESourceRevisionGuards *guards;
1147 
1148 	source = e_backend_get_source (E_BACKEND (backend));
1149 
1150 	/* Local source is always connected. */
1151 	e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
1152 
1153 	g_type_ensure (E_TYPE_SOURCE_REVISION_GUARDS);
1154 	guards = e_source_get_extension (source, E_SOURCE_EXTENSION_REVISION_GUARDS);
1155 
1156 	bf->priv->revision_guards = e_source_revision_guards_get_enabled (guards);
1157 
1158 	g_rw_lock_writer_lock (&(bf->priv->lock));
1159 	if (!bf->priv->revision) {
1160 		e_book_backend_file_load_revision (bf);
1161 		e_book_backend_notify_property_changed (
1162 			E_BOOK_BACKEND (backend),
1163 			E_BOOK_BACKEND_PROPERTY_REVISION,
1164 			bf->priv->revision);
1165 	}
1166 	g_rw_lock_writer_unlock (&(bf->priv->lock));
1167 
1168 	e_backend_set_online (E_BACKEND (backend), TRUE);
1169 	e_book_backend_set_writable (E_BOOK_BACKEND (backend), TRUE);
1170 
1171 	return TRUE;
1172 }
1173 
1174 static gboolean
book_backend_file_create_contacts_sync(EBookBackendSync * backend,const gchar * const * vcards,guint32 opflags,GSList ** out_contacts,GCancellable * cancellable,GError ** error)1175 book_backend_file_create_contacts_sync (EBookBackendSync *backend,
1176 					const gchar * const *vcards,
1177 					guint32 opflags,
1178 					GSList **out_contacts,
1179 					GCancellable *cancellable,
1180 					GError **error)
1181 {
1182 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1183 	gboolean success = FALSE;
1184 
1185 	g_return_val_if_fail (out_contacts != NULL, FALSE);
1186 
1187 	*out_contacts = NULL;
1188 
1189 	g_rw_lock_writer_lock (&(bf->priv->lock));
1190 	if (!e_book_sqlite_lock (bf->priv->sqlitedb,
1191 				 EBSQL_LOCK_WRITE,
1192 				 cancellable, error)) {
1193 		g_rw_lock_writer_unlock (&(bf->priv->lock));
1194 		return FALSE;
1195 	}
1196 
1197 	success = do_create (bf, vcards, out_contacts, cancellable, error);
1198 
1199 	if (success) {
1200 		*out_contacts = g_slist_reverse (*out_contacts);
1201 	} else {
1202 		g_slist_free_full (*out_contacts, g_object_unref);
1203 		*out_contacts = NULL;
1204 	}
1205 
1206 	if (success)
1207 		success = e_book_backend_file_bump_revision (bf, error);
1208 
1209 	if (success)
1210 		success = e_book_sqlite_unlock (
1211 			bf->priv->sqlitedb,
1212 			EBSQL_UNLOCK_COMMIT,
1213 			error);
1214 	else {
1215 		GError *local_error = NULL;
1216 
1217 		/* Rollback transaction */
1218 		e_book_sqlite_unlock (
1219 			bf->priv->sqlitedb,
1220 			EBSQL_UNLOCK_ROLLBACK,
1221 			&local_error);
1222 
1223 		if (local_error != NULL) {
1224 			g_warning (
1225 				"Failed to rollback transaction "
1226 				"after failing to add contacts: %s",
1227 				local_error->message);
1228 			g_clear_error (&local_error);
1229 		}
1230 	}
1231 
1232 	g_rw_lock_writer_unlock (&(bf->priv->lock));
1233 
1234 	return success;
1235 }
1236 
1237 static gboolean
book_backend_file_modify_contacts_sync(EBookBackendSync * backend,const gchar * const * vcards,guint32 opflags,GSList ** out_contacts,GCancellable * cancellable,GError ** error)1238 book_backend_file_modify_contacts_sync (EBookBackendSync *backend,
1239 					const gchar * const *vcards,
1240 					guint32 opflags,
1241 					GSList **out_contacts,
1242 					GCancellable *cancellable,
1243 					GError **error)
1244 {
1245 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1246 	GSList           *ids = NULL;
1247 	GError           *local_error = NULL;
1248 	PhotoModifiedStatus status = STATUS_NORMAL;
1249 	GSList *old_contacts = NULL;
1250 	guint ii, length;
1251 
1252 	length = g_strv_length ((gchar **) vcards);
1253 
1254 	g_rw_lock_writer_lock (&(bf->priv->lock));
1255 
1256 	if (!e_book_sqlite_lock (bf->priv->sqlitedb, EBSQL_LOCK_WRITE, cancellable, error)) {
1257 		g_rw_lock_writer_unlock (&(bf->priv->lock));
1258 		return FALSE;
1259 	}
1260 
1261 	for (ii = 0; ii < length && status != STATUS_ERROR; ii++) {
1262 		gchar *id;
1263 		EContact *mod_contact, *old_contact = NULL;
1264 		const gchar *mod_contact_rev, *old_contact_rev;
1265 
1266 		mod_contact = e_contact_new_from_vcard (vcards[ii]);
1267 		id = e_contact_get (mod_contact, E_CONTACT_UID);
1268 
1269 		if (id == NULL) {
1270 			status = STATUS_ERROR;
1271 
1272 			g_set_error_literal (
1273 				error, E_CLIENT_ERROR,
1274 				E_CLIENT_ERROR_OTHER_ERROR,
1275 				_("No UID in the contact"));
1276 			g_object_unref (mod_contact);
1277 			break;
1278 		}
1279 
1280 		if (!e_book_sqlite_get_contact (bf->priv->sqlitedb,
1281 						id, FALSE, &old_contact,
1282 						&local_error)) {
1283 			g_warning (G_STRLOC ": Failed to load contact %s: %s", id, local_error->message);
1284 			g_propagate_error (error, local_error);
1285 			local_error = NULL;
1286 
1287 			status = STATUS_ERROR;
1288 
1289 			g_free (id);
1290 			g_object_unref (mod_contact);
1291 			break;
1292 		}
1293 
1294 		if (bf->priv->revision_guards) {
1295 			mod_contact_rev = e_contact_get_const (mod_contact, E_CONTACT_REV);
1296 			old_contact_rev = e_contact_get_const (old_contact, E_CONTACT_REV);
1297 
1298 			if (!mod_contact_rev || !old_contact_rev ||
1299 			    strcmp (mod_contact_rev, old_contact_rev) != 0) {
1300 				g_set_error (
1301 					error, E_CLIENT_ERROR,
1302 					E_CLIENT_ERROR_OUT_OF_SYNC,
1303 					_("Tried to modify contact “%s” with out of sync revision"),
1304 					(gchar *) e_contact_get_const (mod_contact, E_CONTACT_UID));
1305 
1306 				status = STATUS_ERROR;
1307 
1308 				g_free (id);
1309 				g_object_unref (mod_contact);
1310 				g_object_unref (old_contact);
1311 				break;
1312 			}
1313 		}
1314 
1315 		/* Transform incomming photo blobs to uris before storing this to the DB */
1316 		status = maybe_transform_vcard_for_photo (bf, old_contact, mod_contact, &local_error);
1317 		if (status == STATUS_ERROR) {
1318 			g_warning (G_STRLOC ": Error transforming contact %s: %s", id, local_error->message);
1319 			g_propagate_error (error, local_error);
1320 			local_error = NULL;
1321 
1322 			g_free (id);
1323 			g_object_unref (old_contact);
1324 			g_object_unref (mod_contact);
1325 			break;
1326 		}
1327 
1328 		/* update the revision (modified time of contact) */
1329 		set_revision (bf, mod_contact);
1330 
1331 		old_contacts = g_slist_prepend (old_contacts, old_contact);
1332 		*out_contacts = g_slist_prepend (*out_contacts, mod_contact);
1333 
1334 		ids = g_slist_prepend (ids, id);
1335 	}
1336 
1337 	if (status != STATUS_ERROR) {
1338 		GSList *old_link, *mod_link;
1339 
1340 		/* Delete old photo file uris if need be (this will compare the new contact
1341 		 * with the current copy in the BDB to extract the uris to delete) */
1342 		old_link = old_contacts;
1343 		mod_link = *out_contacts;
1344 
1345 		while (old_link != NULL && mod_link != NULL) {
1346 			maybe_delete_unused_uris (
1347 				bf,
1348 				E_CONTACT (old_link->data),
1349 				E_CONTACT (mod_link->data));
1350 			old_link = g_slist_next (old_link);
1351 			mod_link = g_slist_next (mod_link);
1352 		}
1353 
1354 		/* Update summary as well */
1355 		e_book_sqlite_add_contacts (
1356 			bf->priv->sqlitedb,
1357 			*out_contacts, NULL, TRUE,
1358 			cancellable, &local_error);
1359 
1360 		if (local_error != NULL) {
1361 			g_warning (
1362 				"Failed to modify contacts: %s",
1363 				local_error->message);
1364 			g_propagate_error (error, local_error);
1365 			local_error = NULL;
1366 
1367 			status = STATUS_ERROR;
1368 		}
1369 	}
1370 
1371 	/* Bump the revision atomically in the same transaction */
1372 	if (status != STATUS_ERROR) {
1373 		if (!e_book_backend_file_bump_revision (bf, error))
1374 			status = STATUS_ERROR;
1375 	}
1376 
1377 	/* Commit or rollback transaction */
1378 	if (status != STATUS_ERROR) {
1379 		gboolean success;
1380 
1381 		success = e_book_sqlite_unlock (
1382 			bf->priv->sqlitedb,
1383 			EBSQL_UNLOCK_COMMIT,
1384 			error);
1385 		if (!success)
1386 			status = STATUS_ERROR;
1387 
1388 	} else {
1389 		/* Rollback transaction */
1390 		e_book_sqlite_unlock (
1391 			bf->priv->sqlitedb,
1392 			EBSQL_UNLOCK_ROLLBACK,
1393 			&local_error);
1394 
1395 		if (local_error != NULL) {
1396 			g_warning (
1397 				"Failed to rollback transaction after "
1398 				"failing to modify contacts: %s",
1399 				local_error->message);
1400 			g_clear_error (&local_error);
1401 		}
1402 	}
1403 
1404 	if (status != STATUS_ERROR) {
1405 		*out_contacts = g_slist_reverse (*out_contacts);
1406 	} else {
1407 		g_slist_free_full (*out_contacts, g_object_unref);
1408 		*out_contacts = NULL;
1409 	}
1410 
1411 	/* Now that we've modified the contact(s),
1412 	 * notify cursors of the changes. */
1413 	if (status != STATUS_ERROR) {
1414 		GSList *link;
1415 
1416 		for (link = old_contacts; link; link = g_slist_next (link)) {
1417 			cursors_contact_removed (bf, E_CONTACT (link->data));
1418 		}
1419 
1420 		for (link = *out_contacts; link; link = g_slist_next (link)) {
1421 			cursors_contact_added (bf, E_CONTACT (link->data));
1422 		}
1423 	}
1424 
1425 	g_rw_lock_writer_unlock (&(bf->priv->lock));
1426 
1427 	g_slist_free_full (old_contacts, g_object_unref);
1428 	g_slist_free_full (ids, g_free);
1429 
1430 	return (status != STATUS_ERROR);
1431 }
1432 
1433 static gboolean
book_backend_file_remove_contacts_sync(EBookBackendSync * backend,const gchar * const * uids,guint32 opflags,GSList ** out_removed_uids,GCancellable * cancellable,GError ** error)1434 book_backend_file_remove_contacts_sync (EBookBackendSync *backend,
1435 					const gchar * const *uids,
1436 					guint32 opflags,
1437 					GSList **out_removed_uids,
1438 					GCancellable *cancellable,
1439 					GError **error)
1440 {
1441 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1442 	GSList           *removed_ids = NULL, *removed_contacts = NULL;
1443 	GError           *local_error = NULL;
1444 	const GSList     *l;
1445 	gboolean success = TRUE;
1446 	guint ii, length;
1447 
1448 	g_return_val_if_fail (out_removed_uids != NULL, FALSE);
1449 
1450 	length = g_strv_length ((gchar **) uids);
1451 
1452 	g_rw_lock_writer_lock (&(bf->priv->lock));
1453 
1454 	if (!e_book_sqlite_lock (bf->priv->sqlitedb,
1455 				 EBSQL_LOCK_WRITE,
1456 				 cancellable, error)) {
1457 		g_rw_lock_writer_unlock (&(bf->priv->lock));
1458 		return FALSE;
1459 	}
1460 
1461 	for (ii = 0; ii < length && success; ii++) {
1462 		EContact *contact = NULL;
1463 
1464 		/* First load the EContacts which need to be removed, we might delete some
1465 		 * photos from disk because of this...
1466 		 *
1467 		 * Note: sqlite backend can probably make this faster by executing a
1468 		 * single query to fetch a list of contacts for a list of ids, the
1469 		 * current method makes a query for each UID.
1470 		 */
1471 		if (e_book_sqlite_get_contact (bf->priv->sqlitedb,
1472 					       uids[ii], FALSE, &contact,
1473 					       &local_error)) {
1474 			removed_ids = g_slist_prepend (removed_ids, g_strdup (uids[ii]));
1475 			removed_contacts = g_slist_prepend (removed_contacts, contact);
1476 		} else {
1477 
1478 			if (g_error_matches (local_error,
1479 					     E_BOOK_SQLITE_ERROR,
1480 					     E_BOOK_SQLITE_ERROR_CONTACT_NOT_FOUND)) {
1481 				g_set_error (
1482 					error, E_BOOK_CLIENT_ERROR,
1483 					E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND,
1484 					_("Contact “%s” not found"), uids[ii]);
1485 				g_error_free (local_error);
1486 			} else {
1487 				g_warning ("Failed to fetch contact to be removed: %s", local_error->message);
1488 				g_propagate_error (error, local_error);
1489 				local_error = NULL;
1490 			}
1491 			/* Abort as soon as missing contact is to be deleted */
1492 			success = FALSE;
1493 			break;
1494 		}
1495 	}
1496 
1497 	if (success) {
1498 
1499 		/* Delete URI associated to those contacts */
1500 		for (l = removed_contacts; l; l = l->next) {
1501 			maybe_delete_unused_uris (bf, E_CONTACT (l->data), NULL);
1502 		}
1503 
1504 		/* Remove from summary as well */
1505 		if (!e_book_sqlite_remove_contacts (bf->priv->sqlitedb, removed_ids,
1506 						    cancellable, &local_error)) {
1507 			if (local_error) {
1508 				g_warning ("Failed to remove contacts: %s", local_error->message);
1509 				g_propagate_error (error, local_error);
1510 			}
1511 		}
1512 
1513 		e_book_backend_file_bump_revision (bf, NULL);
1514 	}
1515 
1516 	/* Commit or rollback transaction */
1517 	if (success) {
1518 		success = e_book_sqlite_unlock (bf->priv->sqlitedb, EBSQL_UNLOCK_COMMIT, error);
1519 	} else {
1520 		/* Rollback transaction */
1521 		if (!e_book_sqlite_unlock (bf->priv->sqlitedb, EBSQL_UNLOCK_ROLLBACK, &local_error)) {
1522 			g_warning (
1523 				"Failed to rollback transaction after failing to modify contacts: %s",
1524 				local_error->message);
1525 			g_clear_error (&local_error);
1526 		}
1527 	}
1528 
1529 	/* After removing any contacts, notify any cursors that the new contacts are added */
1530 	if (success) {
1531 		for (l = removed_contacts; l; l = l->next) {
1532 			cursors_contact_removed (bf, E_CONTACT (l->data));
1533 		}
1534 	}
1535 
1536 	*out_removed_uids = removed_ids;
1537 
1538 	g_rw_lock_writer_unlock (&(bf->priv->lock));
1539 
1540 	g_slist_free_full (removed_contacts, (GDestroyNotify) g_object_unref);
1541 
1542 	return success;
1543 }
1544 
1545 static EContact *
book_backend_file_get_contact_sync(EBookBackendSync * backend,const gchar * uid,GCancellable * cancellable,GError ** error)1546 book_backend_file_get_contact_sync (EBookBackendSync *backend,
1547                                     const gchar *uid,
1548                                     GCancellable *cancellable,
1549                                     GError **error)
1550 {
1551 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1552 	EContact *contact = NULL;
1553 	gboolean success;
1554 	GError *local_error = NULL;
1555 
1556 	g_rw_lock_reader_lock (&(bf->priv->lock));
1557 	success = e_book_sqlite_get_contact (
1558 		bf->priv->sqlitedb,
1559 		uid, FALSE, &contact,
1560 		&local_error);
1561 	g_rw_lock_reader_unlock (&(bf->priv->lock));
1562 
1563 	if (!success) {
1564 		if (g_error_matches (local_error,
1565 				     E_BOOK_SQLITE_ERROR,
1566 				     E_BOOK_SQLITE_ERROR_CONTACT_NOT_FOUND)) {
1567 			g_set_error (
1568 				error, E_BOOK_CLIENT_ERROR,
1569 				E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND,
1570 				_("Contact “%s” not found"), uid);
1571 			g_error_free (local_error);
1572 		} else
1573 			g_propagate_error (error, local_error);
1574 	}
1575 
1576 	return contact;
1577 }
1578 
1579 static gboolean
book_backend_file_get_contact_list_sync(EBookBackendSync * backend,const gchar * query,GSList ** out_contacts,GCancellable * cancellable,GError ** error)1580 book_backend_file_get_contact_list_sync (EBookBackendSync *backend,
1581                                          const gchar *query,
1582                                          GSList **out_contacts,
1583                                          GCancellable *cancellable,
1584                                          GError **error)
1585 {
1586 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1587 	GSList *summary_list = NULL;
1588 	GSList *link;
1589 	gboolean success = TRUE;
1590 	GError *local_error = NULL;
1591 
1592 	g_return_val_if_fail (out_contacts != NULL, FALSE);
1593 
1594 	*out_contacts = NULL;
1595 
1596 	d (printf ("book_backend_file_get_contact_list_sync (%s)\n", query));
1597 
1598 	g_rw_lock_reader_lock (&(bf->priv->lock));
1599 
1600 	success = e_book_sqlite_lock (
1601 		bf->priv->sqlitedb,
1602 		EBSQL_LOCK_READ,
1603 		cancellable, error);
1604 	if (!success) {
1605 		g_rw_lock_writer_unlock (&(bf->priv->lock));
1606 		return FALSE;
1607 	}
1608 
1609 	success = e_book_sqlite_search (
1610 		bf->priv->sqlitedb,
1611 		query,
1612 		FALSE,
1613 		&summary_list,
1614 		cancellable,
1615 		&local_error);
1616 
1617 	e_book_sqlite_unlock (
1618 		bf->priv->sqlitedb,
1619 		EBSQL_UNLOCK_NONE,
1620 		success ? &local_error : NULL);
1621 
1622 	g_rw_lock_reader_unlock (&(bf->priv->lock));
1623 
1624 	if (!success) {
1625 
1626 		g_warn_if_fail (summary_list == NULL);
1627 
1628 		if (g_error_matches (local_error,
1629 				     E_BOOK_SQLITE_ERROR,
1630 				     E_BOOK_SQLITE_ERROR_UNSUPPORTED_QUERY)) {
1631 			g_set_error (
1632 				error, E_CLIENT_ERROR,
1633 				E_CLIENT_ERROR_NOT_SUPPORTED,
1634 				_("Query “%s” not supported"), query);
1635 			g_error_free (local_error);
1636 
1637 		} else if (g_error_matches (local_error,
1638 				     E_BOOK_SQLITE_ERROR,
1639 				     E_BOOK_SQLITE_ERROR_INVALID_QUERY)) {
1640 			g_set_error (
1641 				error, E_CLIENT_ERROR,
1642 				E_CLIENT_ERROR_INVALID_QUERY,
1643 				_("Invalid Query “%s”"), query);
1644 			g_error_free (local_error);
1645 
1646 		} else {
1647 			g_warning ("Failed to fetch contact ids: %s", local_error->message);
1648 			g_propagate_error (error, local_error);
1649 		}
1650 	}
1651 
1652 	for (link = summary_list; link != NULL; link = g_slist_next (link)) {
1653 		EbSqlSearchData *data = link->data;
1654 		EContact *contact;
1655 
1656 		contact = e_contact_new_from_vcard (data->vcard);
1657 		link->data = contact;
1658 
1659 		e_book_sqlite_search_data_free (data);
1660 	}
1661 
1662 	*out_contacts = summary_list;
1663 
1664 	return success;
1665 }
1666 
1667 static gboolean
book_backend_file_get_contact_list_uids_sync(EBookBackendSync * backend,const gchar * query,GSList ** out_uids,GCancellable * cancellable,GError ** error)1668 book_backend_file_get_contact_list_uids_sync (EBookBackendSync *backend,
1669                                               const gchar *query,
1670                                               GSList **out_uids,
1671                                               GCancellable *cancellable,
1672                                               GError **error)
1673 {
1674 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1675 	gboolean success = TRUE;
1676 	GError *local_error = NULL;
1677 
1678 	g_return_val_if_fail (out_uids != NULL, FALSE);
1679 
1680 	*out_uids = NULL;
1681 
1682 	d (printf ("book_backend_file_get_contact_list_sync (%s)\n", query));
1683 
1684 	g_rw_lock_reader_lock (&(bf->priv->lock));
1685 
1686 	success = e_book_sqlite_lock (
1687 		bf->priv->sqlitedb,
1688 		EBSQL_LOCK_READ,
1689 		cancellable, error);
1690 	if (!success) {
1691 		g_rw_lock_writer_unlock (&(bf->priv->lock));
1692 		return FALSE;
1693 	}
1694 
1695 	success = e_book_sqlite_search_uids (
1696 		bf->priv->sqlitedb,
1697 		query,
1698 		out_uids,
1699 		cancellable,
1700 		&local_error);
1701 	e_book_sqlite_unlock (
1702 		bf->priv->sqlitedb,
1703 		EBSQL_UNLOCK_NONE,
1704 		success ? &local_error : NULL);
1705 
1706 	g_rw_lock_reader_unlock (&(bf->priv->lock));
1707 
1708 	if (!success) {
1709 		g_warn_if_fail (*out_uids == NULL);
1710 
1711 		if (g_error_matches (local_error,
1712 				     E_BOOK_SQLITE_ERROR,
1713 				     E_BOOK_SQLITE_ERROR_UNSUPPORTED_QUERY)) {
1714 			g_set_error (
1715 				error, E_CLIENT_ERROR,
1716 				E_CLIENT_ERROR_NOT_SUPPORTED,
1717 				_("Query “%s” not supported"), query);
1718 			g_error_free (local_error);
1719 
1720 		} else if (g_error_matches (local_error,
1721 					    E_BOOK_SQLITE_ERROR,
1722 					    E_BOOK_SQLITE_ERROR_INVALID_QUERY)) {
1723 			g_set_error (
1724 				error, E_CLIENT_ERROR,
1725 				E_CLIENT_ERROR_INVALID_QUERY,
1726 				_("Invalid Query “%s”"), query);
1727 			g_error_free (local_error);
1728 
1729 		} else {
1730 			g_warning (
1731 				"Failed to fetch contact ids: %s",
1732 				local_error->message);
1733 			g_propagate_error (error, local_error);
1734 		}
1735 	}
1736 
1737 	return success;
1738 }
1739 
1740 static void
book_backend_file_start_view(EBookBackend * backend,EDataBookView * book_view)1741 book_backend_file_start_view (EBookBackend *backend,
1742                               EDataBookView *book_view)
1743 {
1744 	FileBackendSearchClosure *closure;
1745 
1746 	closure = init_closure (book_view, E_BOOK_BACKEND_FILE (backend));
1747 
1748 	d (printf ("starting book view thread\n"));
1749 	closure->thread = g_thread_new (NULL, book_view_thread, book_view);
1750 
1751 	e_flag_wait (closure->running);
1752 
1753 	/* at this point we know the book view thread is actually running */
1754 	d (printf ("returning from start_view\n"));
1755 }
1756 
1757 static void
book_backend_file_stop_view(EBookBackend * backend,EDataBookView * book_view)1758 book_backend_file_stop_view (EBookBackend *backend,
1759                              EDataBookView *book_view)
1760 {
1761 	FileBackendSearchClosure *closure = get_closure (book_view);
1762 	gboolean need_join;
1763 
1764 	if (!closure)
1765 		return;
1766 
1767 	d (printf ("stopping query\n"));
1768 	need_join = e_flag_is_set (closure->running);
1769 	e_flag_clear (closure->running);
1770 
1771 	if (need_join) {
1772 		g_thread_join (closure->thread);
1773 		closure->thread = NULL;
1774 	}
1775 }
1776 
1777 static EDataBookDirect *
book_backend_file_get_direct_book(EBookBackend * backend)1778 book_backend_file_get_direct_book (EBookBackend *backend)
1779 {
1780 	EDataBookDirect *direct;
1781 	ESourceRegistry *registry;
1782 	ESource *source;
1783 	gchar *backend_path;
1784 	gchar *dirname;
1785 	const gchar *modules_env = NULL;
1786 
1787 	modules_env = g_getenv (EDS_ADDRESS_BOOK_MODULES);
1788 
1789 	source = e_backend_get_source (E_BACKEND (backend));
1790 	registry = e_book_backend_get_registry (backend);
1791 	dirname = e_book_backend_file_extract_path_from_source (
1792 		registry, source, GET_PATH_DB_DIR);
1793 
1794 	/* Support in-tree testing / relocated modules */
1795 	if (modules_env) {
1796 		backend_path = g_build_filename (
1797 			modules_env, "libebookbackendfile.so", NULL);
1798 	} else {
1799 		backend_path = g_build_filename (
1800 			BACKENDDIR, "libebookbackendfile.so", NULL);
1801 	}
1802 
1803 	direct = e_data_book_direct_new (
1804 		backend_path, "EBookBackendFileFactory", dirname);
1805 
1806 	g_free (backend_path);
1807 	g_free (dirname);
1808 
1809 	return direct;
1810 }
1811 
1812 static void
book_backend_file_configure_direct(EBookBackend * backend,const gchar * config)1813 book_backend_file_configure_direct (EBookBackend *backend,
1814                                     const gchar *config)
1815 {
1816 	EBookBackendFilePrivate *priv;
1817 
1818 	priv = E_BOOK_BACKEND_FILE (backend)->priv;
1819 	priv->base_directory = g_strdup (config);
1820 }
1821 
1822 static void
book_backend_file_vcard_changed(EbSqlChangeType change_type,const gchar * uid,const gchar * extra,const gchar * vcard,gpointer user_data)1823 book_backend_file_vcard_changed (EbSqlChangeType change_type,
1824                                  const gchar *uid,
1825                                  const gchar *extra,
1826                                  const gchar *vcard,
1827                                  gpointer user_data)
1828 {
1829 	EBookBackend *backend = E_BOOK_BACKEND (user_data);
1830 	EContact     *contact;
1831 
1832 	if (change_type == EBSQL_CHANGE_LOCALE_CHANGED) {
1833 		contact = e_contact_new_from_vcard_with_uid (vcard, uid);
1834 		e_book_backend_notify_update (backend, contact);
1835 		g_object_unref (contact);
1836 	}
1837 }
1838 
1839 static gboolean
book_backend_file_set_locale(EBookBackend * backend,const gchar * locale,GCancellable * cancellable,GError ** error)1840 book_backend_file_set_locale (EBookBackend *backend,
1841                               const gchar *locale,
1842                               GCancellable *cancellable,
1843                               GError **error)
1844 {
1845 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1846 	gboolean success;
1847 	GList *l;
1848 
1849 	g_rw_lock_writer_lock (&(bf->priv->lock));
1850 
1851 	success = e_book_sqlite_lock (
1852 		bf->priv->sqlitedb,
1853 		EBSQL_LOCK_WRITE,
1854 		cancellable, error);
1855 	if (!success) {
1856 		g_rw_lock_writer_unlock (&(bf->priv->lock));
1857 		return FALSE;
1858 	}
1859 
1860 	success = e_book_sqlite_set_locale (
1861 		bf->priv->sqlitedb, locale,
1862 		cancellable, error);
1863 
1864 	if (success)
1865 		success = e_book_backend_file_bump_revision (bf, error);
1866 
1867 	if (success) {
1868 		success = e_book_sqlite_unlock (
1869 			bf->priv->sqlitedb,
1870 			EBSQL_UNLOCK_COMMIT,
1871 			error);
1872 
1873 	} else {
1874 		GError *local_error = NULL;
1875 
1876 		/* Rollback transaction */
1877 		e_book_sqlite_unlock (
1878 			bf->priv->sqlitedb,
1879 			EBSQL_UNLOCK_ROLLBACK,
1880 			&local_error);
1881 
1882 		if (local_error != NULL) {
1883 			g_warning (
1884 				"Failed to rollback transaction "
1885 				"after failing to set locale: %s",
1886 				local_error->message);
1887 			g_clear_error (&local_error);
1888 		}
1889 	}
1890 
1891 	/* This must be done outside the EBookSqlite lock,
1892 	 * as it may try to acquire the lock as well. */
1893 	for (l = bf->priv->cursors; success && l; l = l->next) {
1894 		EDataBookCursor *cursor = l->data;
1895 
1896 		success = e_data_book_cursor_load_locale (
1897 			cursor, NULL, cancellable, error);
1898 	}
1899 
1900 	/* We set the new locale, now update our local variable */
1901 	if (success) {
1902 		g_free (bf->priv->locale);
1903 		bf->priv->locale = g_strdup (locale);
1904 	}
1905 
1906 	g_rw_lock_writer_unlock (&(bf->priv->lock));
1907 
1908 	return success;
1909 }
1910 
1911 static gchar *
book_backend_file_dup_locale(EBookBackend * backend)1912 book_backend_file_dup_locale (EBookBackend *backend)
1913 {
1914 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1915 	gchar *locale;
1916 
1917 	g_rw_lock_reader_lock (&(bf->priv->lock));
1918 	locale = g_strdup (bf->priv->locale);
1919 	g_rw_lock_reader_unlock (&(bf->priv->lock));
1920 
1921 	return locale;
1922 }
1923 
1924 static EDataBookCursor *
book_backend_file_create_cursor(EBookBackend * backend,EContactField * sort_fields,EBookCursorSortType * sort_types,guint n_fields,GError ** error)1925 book_backend_file_create_cursor (EBookBackend *backend,
1926                                  EContactField *sort_fields,
1927                                  EBookCursorSortType *sort_types,
1928                                  guint n_fields,
1929                                  GError **error)
1930 {
1931 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1932 	EDataBookCursor *cursor;
1933 
1934 	g_rw_lock_writer_lock (&(bf->priv->lock));
1935 
1936 	cursor = e_data_book_cursor_sqlite_new (
1937 		backend,
1938 		bf->priv->sqlitedb,
1939 		SQLITE_REVISION_KEY,
1940 		sort_fields,
1941 		sort_types,
1942 		n_fields,
1943 		error);
1944 
1945 	if (cursor != NULL) {
1946 		bf->priv->cursors =
1947 			g_list_prepend (bf->priv->cursors, cursor);
1948 	}
1949 
1950 	g_rw_lock_writer_unlock (&(bf->priv->lock));
1951 
1952 	return cursor;
1953 }
1954 
1955 static gboolean
book_backend_file_delete_cursor(EBookBackend * backend,EDataBookCursor * cursor,GError ** error)1956 book_backend_file_delete_cursor (EBookBackend *backend,
1957                                  EDataBookCursor *cursor,
1958                                  GError **error)
1959 {
1960 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
1961 	GList *link;
1962 
1963 	g_rw_lock_writer_lock (&(bf->priv->lock));
1964 
1965 	link = g_list_find (bf->priv->cursors, cursor);
1966 
1967 	if (link != NULL) {
1968 		bf->priv->cursors = g_list_delete_link (bf->priv->cursors, link);
1969 		g_object_unref (cursor);
1970 	} else {
1971 		g_set_error_literal (
1972 			error,
1973 			E_CLIENT_ERROR,
1974 			E_CLIENT_ERROR_INVALID_ARG,
1975 			_("Requested to delete an unrelated cursor"));
1976 	}
1977 
1978 	g_rw_lock_writer_unlock (&(bf->priv->lock));
1979 
1980 	return link != NULL;
1981 }
1982 
1983 static gboolean
book_backend_file_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)1984 book_backend_file_initable_init (GInitable *initable,
1985                                  GCancellable *cancellable,
1986                                  GError **error)
1987 {
1988 	EBookBackendFilePrivate *priv;
1989 	ESourceBackendSummarySetup *setup_extension;
1990 	ESourceRegistry *registry;
1991 	ESource *source;
1992 	const gchar *extension_name;
1993 #ifdef HAVE_LIBDB
1994 	gchar *backup, *filename;
1995 #endif /* HAVE_LIBDB */
1996 	gchar *dirname, *fullpath;
1997 	gboolean success = TRUE;
1998 
1999 	priv = E_BOOK_BACKEND_FILE (initable)->priv;
2000 
2001 	source = e_backend_get_source (E_BACKEND (initable));
2002 	registry = e_book_backend_get_registry (E_BOOK_BACKEND (initable));
2003 
2004 	g_type_ensure (E_TYPE_SOURCE_BACKEND_SUMMARY_SETUP);
2005 	extension_name = E_SOURCE_EXTENSION_BACKEND_SUMMARY_SETUP;
2006 	setup_extension = e_source_get_extension (source, extension_name);
2007 
2008 	if (priv->base_directory)
2009 		dirname = g_strdup (priv->base_directory);
2010 	else
2011 		dirname = e_book_backend_file_extract_path_from_source (
2012 			registry, source, GET_PATH_DB_DIR);
2013 
2014 	fullpath = g_build_filename (dirname, "contacts.db", NULL);
2015 
2016 #ifdef HAVE_LIBDB
2017 	filename = g_build_filename (dirname, "addressbook.db", NULL);
2018 	backup = g_build_filename (dirname, "addressbook.db.old", NULL);
2019 
2020 	/* The old BDB exists, lets migrate that to sqlite right away. */
2021 	if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
2022 		priv->sqlitedb = e_book_sqlite_new_full (
2023 			fullpath, source, setup_extension,
2024 			NULL,
2025 			book_backend_file_vcard_changed,
2026 			initable, NULL, cancellable, error);
2027 
2028 		if (priv->sqlitedb == NULL) {
2029 			success = FALSE;
2030 			goto exit;
2031 		}
2032 
2033 		/* Do the migration from BDB, see e-book-backend-file-migrate-bdb.c */
2034 		success = e_book_backend_file_migrate_bdb (
2035 			priv->sqlitedb, dirname, filename, cancellable, error);
2036 
2037 		if (!success)
2038 			goto exit;
2039 
2040 		/* Now that we've migrated the database,
2041 		 * lets rename it instead of unlinking it. */
2042 		if (g_rename (filename, backup) < 0) {
2043 			g_set_error (
2044 				error, G_FILE_ERROR,
2045 				g_file_error_from_errno (errno),
2046 				_("Failed to rename old database from "
2047 				"“%s” to “%s”: %s"), filename, backup,
2048 				g_strerror (errno));
2049 			success = FALSE;
2050 			goto exit;
2051 		}
2052 	}
2053 #endif /* HAVE_LIBDB */
2054 
2055 	/* If we already have a handle on this, it means there
2056 	 * was an old BDB migrated and no need to reopen it. */
2057 	if (priv->sqlitedb == NULL) {
2058 		gint populated = 0;
2059 		GError *local_error = NULL;
2060 
2061 		/* Ensure the directory exists first. */
2062 		success = create_directory (dirname, error);
2063 
2064 		if (!success)
2065 			goto exit;
2066 
2067 		/* Create the sqlitedb. */
2068 		priv->sqlitedb = e_book_sqlite_new_full (
2069 			fullpath, source, setup_extension,
2070 			NULL,
2071 			book_backend_file_vcard_changed,
2072 			initable, NULL, cancellable, error);
2073 
2074 		if (priv->sqlitedb == NULL) {
2075 			success = FALSE;
2076 			goto exit;
2077 		}
2078 
2079 		/* An sqlite DB only 'exists' if the populated flag is set. */
2080 		e_book_sqlite_get_key_value_int (
2081 			priv->sqlitedb,
2082 			E_BOOK_SQL_IS_POPULATED_KEY,
2083 			&populated,
2084 			&local_error);
2085 
2086 		if (local_error != NULL) {
2087 			g_propagate_error (error, local_error);
2088 			success = FALSE;
2089 			goto exit;
2090 		}
2091 
2092 		if (!populated) {
2093 			/* Set the populated flag. */
2094 			success = e_book_sqlite_set_key_value_int (
2095 				priv->sqlitedb,
2096 				E_BOOK_SQL_IS_POPULATED_KEY,
2097 				TRUE,
2098 				error);
2099 
2100 			if (!success)
2101 				goto exit;
2102 		}
2103 	}
2104 
2105 	/* Load the locale */
2106 	e_book_backend_file_load_locale (E_BOOK_BACKEND_FILE (initable));
2107 
2108 	/* Resolve the photo directory here. */
2109 	priv->photo_dirname =
2110 		e_book_backend_file_extract_path_from_source (
2111 		registry, source, GET_PATH_PHOTO_DIR);
2112 	success = create_directory (priv->photo_dirname, error);
2113 
2114 exit:
2115 	g_free (dirname);
2116 	g_free (fullpath);
2117 #ifdef HAVE_LIBDB
2118 	g_free (filename);
2119 	g_free (backup);
2120 #endif /* HAVE_LIBDB */
2121 
2122 	return success;
2123 }
2124 
2125 static void
e_book_backend_file_class_init(EBookBackendFileClass * class)2126 e_book_backend_file_class_init (EBookBackendFileClass *class)
2127 {
2128 	GObjectClass *object_class;
2129 	EBookBackendClass *backend_class;
2130 	EBookBackendSyncClass *backend_sync_class;
2131 
2132 	object_class = G_OBJECT_CLASS (class);
2133 	object_class->dispose = book_backend_file_dispose;
2134 	object_class->finalize = book_backend_file_finalize;
2135 
2136 	backend_sync_class = E_BOOK_BACKEND_SYNC_CLASS (class);
2137 	backend_sync_class->open_sync = book_backend_file_open_sync;
2138 	backend_sync_class->create_contacts_sync = book_backend_file_create_contacts_sync;
2139 	backend_sync_class->modify_contacts_sync = book_backend_file_modify_contacts_sync;
2140 	backend_sync_class->remove_contacts_sync = book_backend_file_remove_contacts_sync;
2141 	backend_sync_class->get_contact_sync = book_backend_file_get_contact_sync;
2142 	backend_sync_class->get_contact_list_sync = book_backend_file_get_contact_list_sync;
2143 	backend_sync_class->get_contact_list_uids_sync = book_backend_file_get_contact_list_uids_sync;
2144 
2145 	backend_class = E_BOOK_BACKEND_CLASS (class);
2146 	backend_class->impl_get_backend_property = book_backend_file_get_backend_property;
2147 	backend_class->impl_start_view = book_backend_file_start_view;
2148 	backend_class->impl_stop_view = book_backend_file_stop_view;
2149 	backend_class->impl_get_direct_book = book_backend_file_get_direct_book;
2150 	backend_class->impl_configure_direct = book_backend_file_configure_direct;
2151 	backend_class->impl_set_locale = book_backend_file_set_locale;
2152 	backend_class->impl_dup_locale = book_backend_file_dup_locale;
2153 	backend_class->impl_create_cursor = book_backend_file_create_cursor;
2154 	backend_class->impl_delete_cursor = book_backend_file_delete_cursor;
2155 }
2156 
2157 static void
e_book_backend_file_initable_init(GInitableIface * iface)2158 e_book_backend_file_initable_init (GInitableIface *iface)
2159 {
2160 	iface->init = book_backend_file_initable_init;
2161 }
2162 
2163 static void
e_book_backend_file_init(EBookBackendFile * backend)2164 e_book_backend_file_init (EBookBackendFile *backend)
2165 {
2166 	backend->priv = e_book_backend_file_get_instance_private (backend);
2167 
2168 	g_rw_lock_init (&(backend->priv->lock));
2169 }
2170 
2171