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