1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * This program is free software: you can redistribute it and/or modify it
4  * under the terms of the GNU Lesser General Public License as published by
5  * the Free Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful, but
8  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
10  * for more details.
11  *
12  * You should have received a copy of the GNU Lesser General Public License
13  * along with this program. If not, see <http://www.gnu.org/licenses/>.
14  *
15  */
16 
17 #include <stdlib.h>
18 #include <libebook/libebook.h>
19 
20 #include "client-test-utils.h"
21 #include "e-test-server-utils.h"
22 
23 static ETestServerClosure book_closure = { E_TEST_SERVER_ADDRESS_BOOK, NULL, 0 };
24 
25 static const gchar *photo_data =
26 "/9j / 4AAQSkZJRgABAQEARwBHAAD//gAXQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q / 9sAQwAIBgYHB\
27 gUIBwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04Mjw\
28 uMzQy / 9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyM\
29 jIyMjIyMjIyMjIyMjIyMjIy / 8AAEQgAMgAyAwEiAAIRAQMRAf / EABsAAQACAwEBAAAAAAAAAAA\
30 AAAAHCAQFBgID / 8QAMBAAAgEDAQYEBQQDAAAAAAAAAQIDAAQRBQYSEyExQQdhcYEiI0JRkRQVM\
31 qFiguH / xAAaAQADAQEBAQAAAAAAAAAAAAAABAUCBgED / 8QAIxEAAgICAQQCAwAAAAAAAAAAAAE\
32 CAwQRQRITITEUYQUiUf / aAAwDAQACEQMRAD8An + sHUtWtNKjVrmQ7754cajLvjrgfbzPIdzWdV\
33 fds9pJb3XdQkMrcFZGj + HqY0bdVV9Tz / wBia + N9vbjvkaxMb5E9N6SJB1HxLEEjJaWsUjD6QzS\
34 MPXdGB7E1zV74t63HINy1s4F7CWCTn77wrA0TY86jY3N1qsUk6wxBxBDvYjLHkoUH4j3JP / a0V\
35 3s1CvF / QM9tKpw0THeU + TLkj8VLnmzT8y0n9FujBx5bioba / rZLWx3iPZ7RzLp95GtnqRGVTez\
36 HNjruH7 / 4n + 67iqpq7Qi3uYWMMsNynfnE6sM8 / Lr6VamFi0KMepUE1Sx7XZHbI + fjxos1H0z3S\
37 lKYEjzISI2I64OKqsyu8sck2QYrmPjBvpIYg598Vauoh8VtlY7JW2isoBwpPl6hGByZTyD + o6E\
38 +h7UtlVOcPHA/+PyI1Wal6Zp7vaC / 06wnTTLtEeUDiKwzu4H8vI9AM9Tiuctkng1Nnk1G5cOoY\
39 ifB4nI / jB7VjWuoT21qPmwXUCHKlphHKvqG5N6g0 / cLi / Rg88FhbkbxlaUSu3kqpnn6kDzqGqb\
40 NdPB0XyK4 / svZr9RVntL50GePdcKEDqzhVBx7sKtPpayppNosxzKIlDHzxUFeG2zo2n2kivWhK\
41 6PpHwwoTnfk65J7kZyT9z5VYADAwKuYtfRA5zPv7tnjgUpSmREV8bq1hvbWW1uY1khlUo6MMhg\
42 eor7UoAje18FtmLe9eeQT3EXPcglkJRPbv71EWu7Dajp2o3MGmlRCkjKQ30jPUe1WlrlNW0Rpt\
43 TleNB84DnjkD0P9VlxT4Nqck9pmn8JuFp2zo0cgCWFi2e7555 / NSHXLadso2m3sU0NxlV65HM+\
44 VdTW3rgwvsUpSvAFKUoAUxSlAClKUAKUpQB//2Q==";
45 
46 static GMainLoop *loop = NULL;
47 static gchar     *micheal_jackson_uid = NULL;
48 static gchar     *james_brown_uid = NULL;
49 
50 /* Decide what to do with every "view-completed" signal */
51 enum {
52 	ITERATION_SWAP_FACE = 0,
53 	ITERATION_DELETE_JAMES,
54 	ITERATION_UPDATE_MICHEAL,
55 	ITERATION_DELETE_MICHEAL,
56 	ITERATION_FINISH
57 };
58 static gint       iteration = ITERATION_SWAP_FACE;
59 
60 static void
print_contact(EContact * contact)61 print_contact (EContact *contact)
62 {
63 	EContactPhoto *photo = e_contact_get (contact, E_CONTACT_PHOTO);
64 
65 	g_assert (photo != NULL);
66 	g_assert (photo->type == E_CONTACT_PHOTO_TYPE_URI);
67 	g_print ("Test passed with photo uri: %s\n", photo->data.uri);
68 
69 	e_contact_photo_free (photo);
70 }
71 
72 static void
objects_added(EBookView * book_view,const GSList * contacts)73 objects_added (EBookView *book_view,
74                const GSList *contacts)
75 {
76 	const GSList *l;
77 
78 	for (l = (GSList *) contacts; l; l = l->next) {
79 		print_contact (l->data);
80 	}
81 }
82 
83 static void
objects_modified(EBookView * book_view,const GSList * contacts)84 objects_modified (EBookView *book_view,
85                   const GSList *contacts)
86 {
87 	GSList *l;
88 
89 	for (l = (GSList *) contacts; l; l = l->next) {
90 		print_contact (l->data);
91 	}
92 }
93 
94 static void
objects_removed(EBookClientView * book_view,const GSList * ids)95 objects_removed (EBookClientView *book_view,
96                  const GSList *ids)
97 {
98 	GSList *l;
99 
100 	for (l = (GSList *) ids; l; l = l->next) {
101 		g_print ("Removed contact: %s\n", (gchar *) l->data);
102 	}
103 }
104 
105 /* This provokes the backend to handle a cross-referenced photo
106  * between contacts, how the backend handles this is its choice,
107  * we should test that when deleting one of the contacts, the other
108  * contact does not lose its photo on disk as a result.
109  */
110 static void
give_james_brown_micheal_jacksons_face(EBookClient * book)111 give_james_brown_micheal_jacksons_face (EBookClient *book)
112 {
113 	EContact       *micheal = NULL, *james = NULL;
114 	EContactPhoto  *micheal_face;
115 	EContactPhoto  *james_face;
116 	GError         *error = NULL;
117 
118 	if (!e_book_client_get_contact_sync (book, micheal_jackson_uid, &micheal, NULL, &error))
119 		g_error ("Unable to get micheal jackson's contact information: %s", error->message);
120 
121 	if (!e_book_client_get_contact_sync (book, james_brown_uid, &james, NULL, &error))
122 		g_error ("Unable to get james brown's contact information: %s", error->message);
123 
124 	g_assert (micheal);
125 	g_assert (james);
126 
127 	micheal_face = e_contact_get (micheal, E_CONTACT_PHOTO);
128 	g_assert_nonnull (micheal_face);
129 	g_assert (micheal_face->type == E_CONTACT_PHOTO_TYPE_URI);
130 
131 	james_face = e_contact_photo_new ();
132 	james_face->type = E_CONTACT_PHOTO_TYPE_URI;
133 	james_face->data.uri = g_strdup (micheal_face->data.uri);
134 
135 	e_contact_set (james, E_CONTACT_PHOTO, james_face);
136 
137 	g_print ("Giving james brown micheal jacksons face: %s\n", micheal_face->data.uri);
138 
139 	e_contact_photo_free (micheal_face);
140 	e_contact_photo_free (james_face);
141 
142 	if (!e_book_client_modify_contact_sync (book, james, E_BOOK_OPERATION_FLAG_NONE, NULL, &error))
143 		g_error ("Failed to modify contact with cross referenced photo: %s", error->message);
144 
145 	g_object_unref (micheal);
146 	g_object_unref (james);
147 }
148 
149 static void
update_contact_inline(EBookClient * book,const gchar * uid)150 update_contact_inline (EBookClient *book,
151                        const gchar *uid)
152 {
153 	EContact *contact = NULL;
154 	EContactPhoto *photo;
155 	guchar *data;
156 	gsize length = 0;
157 	GError *error = NULL;
158 
159 	if (!e_book_client_get_contact_sync (book, uid, &contact, NULL, &error))
160 		g_error ("Unable to get contact: %s", error->message);
161 
162 	g_assert (contact);
163 
164 	data = g_base64_decode (photo_data, &length);
165 
166 	photo = e_contact_photo_new ();
167 	photo->type = E_CONTACT_PHOTO_TYPE_INLINED;
168 	photo->data.inlined.mime_type = g_strdup ("image/png");
169 	photo->data.inlined.data = data;
170 	photo->data.inlined.length = length;
171 
172 	/* set the photo */
173 	e_contact_set (contact, E_CONTACT_PHOTO, photo);
174 
175 	e_contact_photo_free (photo);
176 
177 	if (!e_book_client_modify_contact_sync (book, contact, E_BOOK_OPERATION_FLAG_NONE, NULL, &error))
178 		g_error ("Failed to modify contact with inline photo data: %s", error->message);
179 
180 	g_object_unref (contact);
181 }
182 
183 /* This assertion is made a couple of times in the view-complete
184  * handler, we run it to ensure that binary blobs and cross-referenced
185  * photo uris exist on disk while they should */
186 static void
assert_uri_exists(EBookClient * book,const gchar * uid)187 assert_uri_exists (EBookClient *book,
188                    const gchar *uid)
189 {
190 	EContact *contact;
191 	EContactPhoto *photo;
192 	gchar *filename;
193 	gboolean success;
194 	GError *error = NULL;
195 
196 	if (!e_book_client_get_contact_sync (book, uid, &contact, NULL, &error))
197 		g_error ("Unable to get contact: %s", error->message);
198 
199 	g_assert (contact);
200 
201 	photo = e_contact_get (contact, E_CONTACT_PHOTO);
202 	g_assert (photo);
203 	g_assert (photo->type == E_CONTACT_PHOTO_TYPE_URI);
204 
205 	filename = g_filename_from_uri (photo->data.uri, NULL, NULL);
206 	g_assert (filename);
207 
208 	/* The file should absolutely exist at this point */
209 	g_assert (g_file_test (filename, G_FILE_TEST_EXISTS));
210 
211 	e_contact_photo_free (photo);
212 
213 	success = e_contact_inline_local_photos (contact, &error);
214 	g_assert_no_error (error);
215 	g_assert (success);
216 
217 	photo = e_contact_get (contact, E_CONTACT_PHOTO);
218 	g_assert (photo);
219 	g_assert (photo->type == E_CONTACT_PHOTO_TYPE_INLINED);
220 	g_assert_cmpstr (e_contact_photo_get_mime_type (photo), ==, "image/png");
221 
222 	e_contact_photo_free (photo);
223 	g_object_unref (contact);
224 	g_free (filename);
225 }
226 
227 static void
complete(EBookClientView * view,const GError * error)228 complete (EBookClientView *view,
229           const GError *error)
230 {
231 	EBookClient *book = e_book_client_view_ref_client (view);
232 	GError *local_error = NULL;
233 
234 	g_print ("View complete, iteration %d\n", iteration);
235 
236 	/* We get another "complete" notification after removing or modifying a contact */
237 	switch (iteration++) {
238 	case ITERATION_SWAP_FACE:
239 		give_james_brown_micheal_jacksons_face (book);
240 		break;
241 	case ITERATION_DELETE_JAMES:
242 		assert_uri_exists (book, james_brown_uid);
243 
244 		if (!e_book_client_remove_contact_by_uid_sync (book, james_brown_uid, E_BOOK_OPERATION_FLAG_NONE, NULL, &local_error))
245 			g_error ("Error removing contact: %s", local_error->message);
246 
247 		g_free (james_brown_uid);
248 		james_brown_uid = NULL;
249 		break;
250 	case ITERATION_UPDATE_MICHEAL:
251 		assert_uri_exists (book, micheal_jackson_uid);
252 
253 		update_contact_inline (book, micheal_jackson_uid);
254 		break;
255 	case ITERATION_DELETE_MICHEAL:
256 		assert_uri_exists (book, micheal_jackson_uid);
257 
258 		if (!e_book_client_remove_contact_by_uid_sync (book, micheal_jackson_uid, E_BOOK_OPERATION_FLAG_NONE, NULL, &local_error))
259 			g_error ("Error removing contact: %s", local_error->message);
260 
261 		g_free (micheal_jackson_uid);
262 		micheal_jackson_uid = NULL;
263 		break;
264 	case ITERATION_FINISH:
265 		e_book_client_view_stop (view, NULL);
266 		g_object_unref (view);
267 		g_main_loop_quit (loop);
268 		break;
269 	default:
270 		g_assert_not_reached ();
271 		break;
272 	}
273 
274 	g_object_unref (book);
275 }
276 
277 static void
setup_and_start_view(EBookClientView * view)278 setup_and_start_view (EBookClientView *view)
279 {
280 	GError *error = NULL;
281 
282 	g_signal_connect (view, "objects-added", G_CALLBACK (objects_added), NULL);
283 	g_signal_connect (view, "objects-removed", G_CALLBACK (objects_removed), NULL);
284 	g_signal_connect (view, "objects-modified", G_CALLBACK (objects_modified), NULL);
285 	g_signal_connect (view, "complete", G_CALLBACK (complete), NULL);
286 
287 	e_book_client_view_set_fields_of_interest (view, NULL, &error);
288 	if (error)
289 		g_error ("set fields of interest: %s", error->message);
290 
291 	e_book_client_view_start (view, &error);
292 	if (error)
293 		g_error ("start view: %s", error->message);
294 }
295 
296 static void
add_contact_inline(EBookClient * book)297 add_contact_inline (EBookClient *book)
298 {
299 	EContact *contact;
300 	EContactPhoto *photo;
301 	guchar *data;
302 	gsize length = 0;
303 
304 	contact = e_contact_new ();
305 
306 	data = g_base64_decode (photo_data, &length);
307 
308 	photo = e_contact_photo_new ();
309 	photo->type = E_CONTACT_PHOTO_TYPE_INLINED;
310 	photo->data.inlined.mime_type = g_strdup ("image/png");
311 	photo->data.inlined.data = data;
312 	photo->data.inlined.length = length;
313 
314 	/* set the photo */
315 	e_contact_set (contact, E_CONTACT_PHOTO, photo);
316 	e_contact_set (contact, E_CONTACT_FULL_NAME, "Micheal Jackson");
317 
318 	e_contact_photo_free (photo);
319 
320 	if (!add_contact_verify (book, contact))
321 		g_error ("Failed to add contact");
322 
323 	micheal_jackson_uid = e_contact_get (contact, E_CONTACT_UID);
324 
325 	g_object_unref (contact);
326 }
327 
328 static void
add_contact_uri(EBookClient * book)329 add_contact_uri (EBookClient *book)
330 {
331 	EContact *contact;
332 	EContactPhoto *photo;
333 
334 	contact = e_contact_new ();
335 
336 	photo = e_contact_photo_new ();
337 	photo->type = E_CONTACT_PHOTO_TYPE_URI;
338 	photo->data.uri = g_strdup ("http://en.wikipedia.org/wiki/File:Jamesbrown4.jpg");
339 
340 	/* set the photo */
341 	e_contact_set (contact, E_CONTACT_PHOTO, photo);
342 	e_contact_set (contact, E_CONTACT_FULL_NAME, "James Brown");
343 
344 	e_contact_photo_free (photo);
345 
346 	if (!add_contact_verify (book, contact))
347 		g_error ("Failed to add contact");
348 
349 	james_brown_uid = e_contact_get (contact, E_CONTACT_UID);
350 
351 	g_object_unref (contact);
352 }
353 
354 static void
test_photo_is_uri(ETestServerFixture * fixture,gconstpointer user_data)355 test_photo_is_uri (ETestServerFixture *fixture,
356                    gconstpointer user_data)
357 {
358 	EBookClient *book_client;
359 	EBookClientView *view;
360 	EBookQuery *query;
361 	GError     *error = NULL;
362 	gchar      *sexp;
363 
364 	book_client = E_TEST_SERVER_UTILS_SERVICE (fixture, EBookClient);
365 
366 	add_contact_inline (book_client);
367 	add_contact_uri (book_client);
368 
369 	query = e_book_query_any_field_contains ("");
370 	sexp = e_book_query_to_string (query);
371 	e_book_query_unref (query);
372 	if (!e_book_client_get_view_sync (book_client, sexp, &view, NULL, &error))
373 		g_error ("get book view sync: %s", error->message);
374 
375 	g_free (sexp);
376 
377 	setup_and_start_view (view);
378 
379 	loop = fixture->loop;
380 	g_main_loop_run (loop);
381 }
382 
383 gint
main(gint argc,gchar ** argv)384 main (gint argc,
385       gchar **argv)
386 {
387 	g_test_init (&argc, &argv, NULL);
388 	g_test_bug_base ("https://gitlab.gnome.org/GNOME/evolution-data-server/");
389 
390 	client_test_utils_read_args (argc, argv);
391 
392 	g_test_add (
393 		"/EBookClient/PhotoIsUri",
394 		ETestServerFixture,
395 		&book_closure,
396 		e_test_server_utils_setup,
397 		test_photo_is_uri,
398 		e_test_server_utils_teardown);
399 
400 	return e_test_server_utils_run (argc, argv);
401 }
402