]> git.sur5r.net Git - glabels/blob - src/merge-evolution.c
Imported Upstream version 2.2.8
[glabels] / src / merge-evolution.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2
3 /*
4  *  (GLABELS) Label and Business Card Creation program for GNOME
5  *
6  *  merge_evolution.c:  evolution merge backend module
7  *
8  *  Copyright (C) 2001  Jim Evins <evins@snaught.com>.
9  *  and
10  *  Copyright (C) 2005  Austin Henry <ahenry@users.sourceforge.net>
11  *  and
12  *  Copyright (C) 2007  Peter Cherriman <glabels-devel2712@bubieyehyeh.me.uk>
13  *
14  *  This program is free software; you can redistribute it and/or modify
15  *  it under the terms of the GNU General Public License as published by
16  *  the Free Software Foundation; either version 2 of the License, or
17  *  (at your option) any later version.
18  *
19  *  This program is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *
24  *  You should have received a copy of the GNU General Public License
25  *  along with this program; if not, write to the Free Software
26  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
27  */
28
29 #include <config.h>
30
31 #ifdef HAVE_LIBEBOOK
32
33
34 #include "merge-evolution.h"
35
36 #include <libebook/e-book.h>
37 #include <glib/gi18n.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <libglabels/str.h>
41
42 #include "debug.h"
43
44 #define DEFAULT_QUERY "(exists \"full_name\")"
45
46 /*===========================================*/
47 /* Private types                             */
48 /*===========================================*/
49
50 struct _glMergeEvolutionPrivate {
51         gchar            *query;
52         EBook            *book;
53         GList            *contacts;
54         GList            *fields; /* the fields supported by the addressbook */
55 };
56
57 enum {
58         LAST_SIGNAL
59 };
60
61 enum {
62         ARG_0,
63         ARG_QUERY,
64 };
65
66 /*===========================================*/
67 /* Private globals                           */
68 /*===========================================*/
69
70 /*===========================================*/
71 /* Local function prototypes                 */
72 /*===========================================*/
73
74 static void           gl_merge_evolution_finalize        (GObject          *object);
75
76 static void           gl_merge_evolution_set_property    (GObject          *object,
77                                                           guint             param_id,
78                                                           const GValue     *value,
79                                                           GParamSpec       *pspec);
80
81 static void           gl_merge_evolution_get_property    (GObject          *object,
82                                                           guint             param_id,
83                                                           GValue           *value,
84                                                           GParamSpec       *pspec);
85
86 static GList         *gl_merge_evolution_get_key_list    (glMerge          *merge);
87 static gchar         *gl_merge_evolution_get_primary_key (glMerge          *merge);
88 static void           gl_merge_evolution_open            (glMerge          *merge);
89 static void           gl_merge_evolution_close           (glMerge          *merge);
90 static glMergeRecord *gl_merge_evolution_get_record      (glMerge          *merge);
91 static void           gl_merge_evolution_copy            (glMerge          *dst_merge,
92                                                           glMerge          *src_merge);
93
94 /* utility function prototypes go here */
95 static void           free_field_list                    (GList *fields);
96
97 \f
98 /*****************************************************************************/
99 /* Boilerplate object stuff.                                                 */
100 /*****************************************************************************/
101 G_DEFINE_TYPE (glMergeEvolution, gl_merge_evolution, GL_TYPE_MERGE);
102
103 static void
104 gl_merge_evolution_class_init (glMergeEvolutionClass *class)
105 {
106         GObjectClass *object_class = G_OBJECT_CLASS (class);
107         glMergeClass *merge_class  = GL_MERGE_CLASS (class);
108
109         gl_debug (DEBUG_MERGE, "START");
110
111         gl_merge_evolution_parent_class = g_type_class_peek_parent (class);
112
113         object_class->set_property = gl_merge_evolution_set_property;
114         object_class->get_property = gl_merge_evolution_get_property;
115
116         g_object_class_install_property
117                 (object_class,
118                  ARG_QUERY,
119                  g_param_spec_string ("query", NULL, 
120                                       "Query used to select records from the addressbook",
121                                       "(exists \"full_name\")",
122                                       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
123
124         object_class->finalize = gl_merge_evolution_finalize;
125
126         merge_class->get_key_list    = gl_merge_evolution_get_key_list;
127         merge_class->get_primary_key = gl_merge_evolution_get_primary_key;
128         merge_class->open            = gl_merge_evolution_open;
129         merge_class->close           = gl_merge_evolution_close;
130         merge_class->get_record      = gl_merge_evolution_get_record;
131         merge_class->copy            = gl_merge_evolution_copy;
132
133         gl_debug (DEBUG_MERGE, "END");
134 }
135
136 static void
137 gl_merge_evolution_init (glMergeEvolution *merge_evolution)
138 {
139         gl_debug (DEBUG_MERGE, "START");
140
141         merge_evolution->priv = g_new0 (glMergeEvolutionPrivate, 1);
142         merge_evolution->priv->query = g_strdup(DEFAULT_QUERY);
143
144         gl_debug (DEBUG_MERGE, "END");
145 }
146
147 static void
148 gl_merge_evolution_finalize (GObject *object)
149 {
150         glMergeEvolution *merge_evolution = GL_MERGE_EVOLUTION (object);
151
152         gl_debug (DEBUG_MERGE, "START");
153
154         g_return_if_fail (object && GL_IS_MERGE_EVOLUTION (object));
155
156         free_field_list(merge_evolution->priv->fields);
157         g_free (merge_evolution->priv->query);
158         g_free (merge_evolution->priv);
159
160         G_OBJECT_CLASS (gl_merge_evolution_parent_class)->finalize (object);
161
162         gl_debug (DEBUG_MERGE, "END");
163 }
164
165 /*--------------------------------------------------------------------------*/
166 /* Set argument.                                                            */
167 /*--------------------------------------------------------------------------*/
168 static void
169 gl_merge_evolution_set_property (GObject      *object,
170                                  guint         param_id,
171                                  const GValue *value,
172                                  GParamSpec   *pspec)
173 {
174         glMergeEvolution *merge_evolution;
175
176         merge_evolution = GL_MERGE_EVOLUTION (object);
177
178         switch (param_id) {
179
180         case ARG_QUERY:
181                 g_free (merge_evolution->priv->query);
182                 merge_evolution->priv->query = g_value_dup_string (value);
183                 gl_debug (DEBUG_MERGE, "ARG \"query\" = \"%s\"",
184                           merge_evolution->priv->query);
185                 break;
186
187         default:
188                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
189                 break;
190
191         }
192
193 }
194
195 /*--------------------------------------------------------------------------*/
196 /* Get argument.                                                            */
197 /*--------------------------------------------------------------------------*/
198 static void
199 gl_merge_evolution_get_property (GObject     *object,
200                                  guint        param_id,
201                                  GValue      *value,
202                                  GParamSpec  *pspec)
203 {
204         glMergeEvolution *merge_evolution;
205
206         merge_evolution = GL_MERGE_EVOLUTION (object);
207
208         switch (param_id) {
209
210         case ARG_QUERY:
211                 g_value_set_string (value, merge_evolution->priv->query);
212                 break;
213
214         default:
215                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
216                 break;
217
218         }
219
220 }
221
222 /*--------------------------------------------------------------------------*/
223 /* Get key list.                                                            */
224 /*--------------------------------------------------------------------------*/
225 static GList *
226 gl_merge_evolution_get_key_list (glMerge *merge)
227 {
228         glMergeEvolution   *merge_evolution;
229         GList              *key_list = NULL;
230         GList              *iter;
231         
232         gl_debug (DEBUG_MERGE, "BEGIN");
233
234         merge_evolution = GL_MERGE_EVOLUTION (merge);
235
236         /* for the previously retrieved supported fileds, go through them and find
237          * their pretty names */
238         for (iter = merge_evolution->priv->fields; 
239                  iter != NULL; 
240                  iter = g_list_next(iter)) 
241         {
242                 key_list = g_list_prepend (key_list, 
243                         g_strdup (e_contact_pretty_name (*(EContactField *)iter->data)));
244         }
245
246         key_list = g_list_reverse (key_list);
247
248         gl_debug (DEBUG_MERGE, "END");
249
250         return key_list;
251 }
252
253 /*--------------------------------------------------------------------------*/
254 /* Get "primary" key.                                                       */
255 /*--------------------------------------------------------------------------*/
256 static gchar *
257 gl_merge_evolution_get_primary_key (glMerge *merge)
258 {
259         return g_strdup (e_contact_pretty_name(E_CONTACT_FILE_AS));
260 }
261
262 /* Sort compare function for sorting contacts by file-as element
263  * by Peter Cherriman (PJC)
264  * called by GList* g_list_sort(GList *list, sort_contact_by_file_as);
265  */
266 static gint sort_contact_by_file_as(gconstpointer *a, gconstpointer *b)
267 {
268   /* 
269    * Returns :  negative value if a < b; zero if a = b; positive value if a > b 
270    */
271
272   // Check and cast a and b to EContact
273   EContact *contact_a = E_CONTACT(a);
274   EContact *contact_b = E_CONTACT(b);
275
276   // Extract file_as for each contact and compare...
277   gchar *a_file_as = e_contact_get (contact_a, E_CONTACT_FILE_AS);
278   gchar *b_file_as = e_contact_get (contact_b, E_CONTACT_FILE_AS);
279   gint res = lgl_str_utf8_casecmp(a_file_as, b_file_as);
280
281   gl_debug(DEBUG_MERGE, "Sort comparing contacts '%s' and '%s' = %d", a_file_as, b_file_as, res);
282
283   // free file_as strings created earlier....
284   g_free (a_file_as);
285   g_free (b_file_as);
286
287   return res;
288 }
289
290 /*--------------------------------------------------------------------------*/
291 /* Open merge source.                                                       */
292 /*--------------------------------------------------------------------------*/
293 static void
294 gl_merge_evolution_open (glMerge *merge)
295 {
296         glMergeEvolution *merge_evolution;
297         EBookQuery *query;
298         gboolean status;
299         GList *fields, *iter;
300         EContactField *field_id;
301         GError *error = NULL;
302
303         gl_debug (DEBUG_MERGE, "BEGIN");
304
305         merge_evolution = GL_MERGE_EVOLUTION (merge);
306
307         query = e_book_query_from_string(merge_evolution->priv->query);
308         if (!query) {
309                 g_warning (_("Couldn't construct query"));
310                 return;
311         }
312
313         merge_evolution->priv->book = e_book_new_system_addressbook(&error);
314         if (!merge_evolution->priv->book) {
315                 g_warning (_("Couldn't open addressbook."));
316                 if (error)
317                 {
318                         g_warning ("e_book_new_system_addressbook: %s", error->message);
319                         g_error_free (error);
320                 }
321                 e_book_query_unref(query);
322                 return;
323         }
324
325         if (!e_book_open(merge_evolution->priv->book, FALSE, &error)) {
326                 g_warning (_("Couldn't open addressbook."));
327                 if (error)
328                 {
329                         g_warning ("e_book_open: %s", error->message);
330                         g_error_free (error);
331                 }
332                 e_book_query_unref(query);
333                 g_object_unref(merge_evolution->priv->book);
334                 merge_evolution->priv->book = NULL;
335                 return;
336         }
337
338         /* fetch the list of fields supported by this address book */
339         status = e_book_get_supported_fields(merge_evolution->priv->book, &fields, &error);
340         if (status == FALSE) {
341                 g_warning (_("Couldn't list available fields."));
342                 if (error)
343                 {
344                         g_warning ("e_book_get_supported_fields: %s", error->message);
345                         g_error_free (error);
346                 }
347                 e_book_query_unref(query);
348                 g_object_unref(merge_evolution->priv->book);
349                 merge_evolution->priv->book = NULL;
350                 return;
351         }
352
353         /* generate a list of field_ids, and put that into private->fields */
354         for (iter = fields; iter != NULL; iter = g_list_next(iter)) {
355                 field_id = g_new(EContactField, 1);
356                 *field_id = e_contact_field_id(iter->data);
357
358                 /* above this value, the data aren't strings anymore */
359                 if (*field_id >= E_CONTACT_LAST_SIMPLE_STRING) {
360                         g_free (field_id);
361                         continue;
362                 }
363
364                 merge_evolution->priv->fields = 
365                         g_list_prepend(merge_evolution->priv->fields, field_id);
366         }
367         free_field_list(fields); /* don't need the list of names anymore */
368
369         gl_debug(DEBUG_MERGE, "Field list length: %d", g_list_length(merge_evolution->priv->fields));
370
371         /* finally retrieve the contacts */
372         status = e_book_get_contacts (merge_evolution->priv->book,
373                                       query,
374                                       &merge_evolution->priv->contacts,
375                                       &error);
376         if (status == FALSE) {
377                 g_warning (_("Couldn't get contacts."));
378                 if (error)
379                 {
380                         g_warning ("e_book_get_contacts: %s", error->message);
381                         g_error_free (error);
382                 }
383                 e_book_query_unref(query);
384                 free_field_list(merge_evolution->priv->fields);
385                 g_object_unref(merge_evolution->priv->book);
386                 merge_evolution->priv->book = NULL;
387
388                 return;
389         }
390
391         e_book_query_unref(query);
392
393         /* Sort contacts using file-as element.... 
394          * by Peter Cherriman (PJC)
395          */
396         gl_debug (DEBUG_MERGE, "Starting sort");
397         merge_evolution->priv->contacts = g_list_sort(merge_evolution->priv->contacts, (GCompareFunc)sort_contact_by_file_as);
398         gl_debug (DEBUG_MERGE, "Ended sort");
399
400         gl_debug (DEBUG_MERGE, "END");
401
402         return;
403 }
404
405 /*--------------------------------------------------------------------------*/
406 /* Close merge source.                                                      */
407 /*--------------------------------------------------------------------------*/
408 static void
409 gl_merge_evolution_close (glMerge *merge)
410 {
411         glMergeEvolution *merge_evolution;
412         GList *iter;
413
414         merge_evolution = GL_MERGE_EVOLUTION (merge);
415
416         /* unref all of the objects created in _open */
417         g_object_unref(merge_evolution->priv->book);
418         merge_evolution->priv->book = NULL;
419
420         for (iter = merge_evolution->priv->contacts; 
421              iter != NULL; 
422              iter = g_list_next(iter))
423         {
424                 EContact *contact = E_CONTACT (iter->data);
425
426                 g_object_unref(contact);
427         }
428         g_list_free(merge_evolution->priv->contacts);
429         merge_evolution->priv->contacts = NULL;
430 }
431
432 /*--------------------------------------------------------------------------*/
433 /* Get next record from merge source, NULL if no records left (i.e EOF)     */
434 /*--------------------------------------------------------------------------*/
435 static glMergeRecord *
436 gl_merge_evolution_get_record (glMerge *merge)
437 {
438         glMergeEvolution   *merge_evolution;
439         glMergeRecord *record;
440         glMergeField  *field;
441         EContactField field_id;
442
443         GList *head, *iter; 
444         EContact *contact;
445
446         merge_evolution = GL_MERGE_EVOLUTION (merge);
447
448         head = merge_evolution->priv->contacts;
449         if (head == NULL) {
450                 return NULL; /* past the last record */
451         }
452         contact = E_CONTACT(head->data);
453
454         record = g_new0 (glMergeRecord, 1);
455         record->select_flag = TRUE;
456
457         /* Take the interesting fields one by one from the contact, and put them
458          * into the glMergeRecord structure. When done, free up the resources for
459          * that contact */
460
461         /* iterate through the supported fields, and add them to the list */
462         for (iter = merge_evolution->priv->fields;
463              iter != NULL;
464              iter = g_list_next(iter))
465         {
466                 gchar *value;
467                 field_id = *(EContactField *)iter->data;
468                 value = g_strdup (e_contact_get_const (contact, field_id));
469
470                 if (value) {
471                         field = g_new0 (glMergeField, 1);
472                         field->key = g_strdup (e_contact_pretty_name (field_id));
473                         field->value = value;
474                         record->field_list = g_list_prepend (record->field_list, field);
475                 }
476         }
477
478         record->field_list = g_list_reverse (record->field_list);
479
480         /* do a destructive read */
481         g_object_unref (contact);
482         merge_evolution->priv->contacts = 
483                 g_list_remove_link (merge_evolution->priv->contacts, head);
484         g_list_free_1 (head);
485
486         return record;
487 }
488
489 /*---------------------------------------------------------------------------*/
490 /* Copy merge_evolution specific fields.                                     */
491 /*---------------------------------------------------------------------------*/
492 static void
493 gl_merge_evolution_copy (glMerge *dst_merge,
494                     glMerge *src_merge)
495 {
496         GList *src_iter, *dst_iter;
497
498         gl_debug (DEBUG_MERGE, "BEGIN");
499
500         glMergeEvolution *dst_merge_evolution;
501         glMergeEvolution *src_merge_evolution;
502
503         dst_merge_evolution = GL_MERGE_EVOLUTION (dst_merge);
504         src_merge_evolution = GL_MERGE_EVOLUTION (src_merge);
505
506         dst_merge_evolution->priv->query = g_strdup(src_merge_evolution->priv->query);
507
508         dst_merge_evolution->priv->fields = g_list_copy(src_merge_evolution->priv->fields);
509         for (src_iter = src_merge_evolution->priv->fields,
510                      dst_iter = dst_merge_evolution->priv->fields; 
511              src_iter != NULL && dst_iter != NULL; 
512              src_iter = g_list_next(src_iter), dst_iter = g_list_next(dst_iter))
513         {
514                 dst_iter->data = g_new(EContactField, 1);
515                 if (src_iter->data) { /* this better not be null, but... */
516                         memcpy(dst_iter->data, src_iter->data, sizeof(EContactField));
517                 }
518         }
519
520         /* I don't know that there's a good way to do a deep copy of the various
521          * libebook structures/objects, so I'm just going to leave them out.  They
522          * are all regenerated on gl_merge_evolution_open, anyway */
523
524         gl_debug (DEBUG_MERGE, "END");
525 }
526
527 /*---------------------------------------------------------------------------*/
528 /* Free the list of supported fields                                         */
529 /*---------------------------------------------------------------------------*/
530 static void
531 free_field_list (GList *fields)
532 {
533         GList *iter;
534
535         for (iter = fields; iter != NULL; iter = g_list_next(iter)) 
536         {
537                 if (iter->data) {
538                         g_free(iter->data);
539                 }
540         }
541         g_list_free(fields);
542         fields = NULL;
543 }
544
545
546 #endif /* HAVE_LIBEBOOK */