]> git.sur5r.net Git - glabels/blob - src/merge-evolution.c
Imported Upstream version 3.0.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/e-book.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 void
301 gl_merge_evolution_open (glMerge *merge)
302 {
303         glMergeEvolution *merge_evolution;
304         EBookQuery *query;
305         gboolean status;
306         GList *fields, *iter;
307         EContactField *field_id;
308         GError *error = NULL;
309
310         gl_debug (DEBUG_MERGE, "BEGIN");
311
312         merge_evolution = GL_MERGE_EVOLUTION (merge);
313
314         query = e_book_query_from_string(merge_evolution->priv->query);
315         if (!query) {
316                 g_warning ("Couldn't construct query");
317                 return;
318         }
319
320         merge_evolution->priv->book = e_book_new_system_addressbook(&error);
321         if (!merge_evolution->priv->book) {
322                 g_warning ("Couldn't open addressbook.");
323                 if (error)
324                 {
325                         g_warning ("e_book_new_system_addressbook: %s", error->message);
326                         g_error_free (error);
327                 }
328                 e_book_query_unref(query);
329                 return;
330         }
331
332         if (!e_book_open(merge_evolution->priv->book, FALSE, &error)) {
333                 g_warning ("Couldn't open addressbook.");
334                 if (error)
335                 {
336                         g_warning ("e_book_open: %s", error->message);
337                         g_error_free (error);
338                 }
339                 e_book_query_unref(query);
340                 g_object_unref(merge_evolution->priv->book);
341                 merge_evolution->priv->book = NULL;
342                 return;
343         }
344
345         /* fetch the list of fields supported by this address book */
346         status = e_book_get_supported_fields(merge_evolution->priv->book, &fields, &error);
347         if (status == FALSE) {
348                 g_warning ("Couldn't list available fields.");
349                 if (error)
350                 {
351                         g_warning ("e_book_get_supported_fields: %s", error->message);
352                         g_error_free (error);
353                 }
354                 e_book_query_unref(query);
355                 g_object_unref(merge_evolution->priv->book);
356                 merge_evolution->priv->book = NULL;
357                 return;
358         }
359
360         /* generate a list of field_ids, and put that into private->fields */
361         for (iter = fields; iter != NULL; iter = g_list_next(iter)) {
362                 field_id = g_new(EContactField, 1);
363                 *field_id = e_contact_field_id(iter->data);
364
365                 /* above this value, the data aren't strings anymore */
366                 if (*field_id > E_CONTACT_LAST_SIMPLE_STRING) {
367                         g_free (field_id);
368                         continue;
369                 }
370
371                 merge_evolution->priv->fields = 
372                         g_list_prepend(merge_evolution->priv->fields, field_id);
373         }
374         free_field_list(fields); /* don't need the list of names anymore */
375
376         gl_debug(DEBUG_MERGE, "Field list length: %d", g_list_length(merge_evolution->priv->fields));
377
378         /* finally retrieve the contacts */
379         status = e_book_get_contacts (merge_evolution->priv->book,
380                                       query,
381                                       &merge_evolution->priv->contacts,
382                                       &error);
383         if (status == FALSE) {
384                 g_warning ("Couldn't get contacts.");
385                 if (error)
386                 {
387                         g_warning ("e_book_get_contacts: %s", error->message);
388                         g_error_free (error);
389                 }
390                 e_book_query_unref(query);
391                 free_field_list(merge_evolution->priv->fields);
392                 g_object_unref(merge_evolution->priv->book);
393                 merge_evolution->priv->book = NULL;
394
395                 return;
396         }
397
398         e_book_query_unref(query);
399
400         /* Sort contacts using file-as element.... 
401          * by Peter Cherriman (PJC)
402          */
403         gl_debug (DEBUG_MERGE, "Starting sort");
404         merge_evolution->priv->contacts = g_list_sort(merge_evolution->priv->contacts, (GCompareFunc)sort_contact_by_file_as);
405         gl_debug (DEBUG_MERGE, "Ended sort");
406
407         gl_debug (DEBUG_MERGE, "END");
408
409         return;
410 }
411
412
413 /*--------------------------------------------------------------------------*/
414 /* Close merge source.                                                      */
415 /*--------------------------------------------------------------------------*/
416 static void
417 gl_merge_evolution_close (glMerge *merge)
418 {
419         glMergeEvolution *merge_evolution;
420         GList *iter;
421
422         merge_evolution = GL_MERGE_EVOLUTION (merge);
423
424         /* unref all of the objects created in _open */
425         g_object_unref(merge_evolution->priv->book);
426         merge_evolution->priv->book = NULL;
427
428         for (iter = merge_evolution->priv->contacts; 
429              iter != NULL; 
430              iter = g_list_next(iter))
431         {
432                 EContact *contact = E_CONTACT (iter->data);
433
434                 g_object_unref(contact);
435         }
436         g_list_free(merge_evolution->priv->contacts);
437         merge_evolution->priv->contacts = NULL;
438 }
439
440
441 /*--------------------------------------------------------------------------*/
442 /* Get next record from merge source, NULL if no records left (i.e EOF)     */
443 /*--------------------------------------------------------------------------*/
444 static glMergeRecord *
445 gl_merge_evolution_get_record (glMerge *merge)
446 {
447         glMergeEvolution   *merge_evolution;
448         glMergeRecord *record;
449         glMergeField  *field;
450         EContactField field_id;
451
452         GList *head, *iter; 
453         EContact *contact;
454
455         merge_evolution = GL_MERGE_EVOLUTION (merge);
456
457         head = merge_evolution->priv->contacts;
458         if (head == NULL) {
459                 return NULL; /* past the last record */
460         }
461         contact = E_CONTACT(head->data);
462
463         record = g_new0 (glMergeRecord, 1);
464         record->select_flag = TRUE;
465
466         /* Take the interesting fields one by one from the contact, and put them
467          * into the glMergeRecord structure. When done, free up the resources for
468          * that contact */
469
470         /* iterate through the supported fields, and add them to the list */
471         for (iter = merge_evolution->priv->fields;
472              iter != NULL;
473              iter = g_list_next(iter))
474         {
475                 gchar *value;
476                 field_id = *(EContactField *)iter->data;
477                 value = g_strdup (e_contact_get_const (contact, field_id));
478
479                 if (value) {
480                         field = g_new0 (glMergeField, 1);
481                         field->key = g_strdup (e_contact_pretty_name (field_id));
482                         field->value = value;
483                         record->field_list = g_list_prepend (record->field_list, field);
484                 }
485         }
486
487         record->field_list = g_list_reverse (record->field_list);
488
489         /* do a destructive read */
490         g_object_unref (contact);
491         merge_evolution->priv->contacts = 
492                 g_list_remove_link (merge_evolution->priv->contacts, head);
493         g_list_free_1 (head);
494
495         return record;
496 }
497
498
499 /*---------------------------------------------------------------------------*/
500 /* Copy merge_evolution specific fields.                                     */
501 /*---------------------------------------------------------------------------*/
502 static void
503 gl_merge_evolution_copy (glMerge       *dst_merge,
504                          const glMerge *src_merge)
505 {
506         GList *src_iter, *dst_iter;
507
508         glMergeEvolution *dst_merge_evolution;
509         glMergeEvolution *src_merge_evolution;
510
511         gl_debug (DEBUG_MERGE, "BEGIN");
512
513         dst_merge_evolution = GL_MERGE_EVOLUTION (dst_merge);
514         src_merge_evolution = GL_MERGE_EVOLUTION (src_merge);
515
516         dst_merge_evolution->priv->query = g_strdup(src_merge_evolution->priv->query);
517
518         dst_merge_evolution->priv->fields = g_list_copy(src_merge_evolution->priv->fields);
519         for (src_iter = src_merge_evolution->priv->fields,
520                      dst_iter = dst_merge_evolution->priv->fields; 
521              src_iter != NULL && dst_iter != NULL; 
522              src_iter = g_list_next(src_iter), dst_iter = g_list_next(dst_iter))
523         {
524                 dst_iter->data = g_new(EContactField, 1);
525                 if (src_iter->data) { /* this better not be null, but... */
526                         memcpy(dst_iter->data, src_iter->data, sizeof(EContactField));
527                 }
528         }
529
530         /* I don't know that there's a good way to do a deep copy of the various
531          * libebook structures/objects, so I'm just going to leave them out.  They
532          * are all regenerated on gl_merge_evolution_open, anyway */
533
534         gl_debug (DEBUG_MERGE, "END");
535 }
536
537
538 /*---------------------------------------------------------------------------*/
539 /* Free the list of supported fields                                         */
540 /*---------------------------------------------------------------------------*/
541 static void
542 free_field_list (GList *fields)
543 {
544         GList *iter;
545
546         for (iter = fields; iter != NULL; iter = g_list_next(iter)) 
547         {
548                 if (iter->data) {
549                         g_free(iter->data);
550                 }
551         }
552         g_list_free(fields);
553         fields = NULL;
554 }
555
556
557 #endif /* HAVE_LIBEBOOK */
558
559
560
561 /*
562  * Local Variables:       -- emacs
563  * mode: C                -- emacs
564  * c-basic-offset: 8      -- emacs
565  * tab-width: 8           -- emacs
566  * indent-tabs-mode: nil  -- emacs
567  * End:                   -- emacs
568  */