Remove some deprecation warnings from python-shell
[claws.git] / src / plugins / gdata / cm_gdata_contacts.c
blob6c689613c8d2cf264d35a445387ad44d27329e93
1 /* GData plugin for Claws Mail
2 * Copyright (C) 2011 Holger Berndt
3 * Copyright (C) 2011-2019 the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 # include "claws-features.h"
22 #endif
24 #include <glib.h>
25 #include <glib/gi18n.h>
27 #include <gtk/gtk.h>
29 #include "cm_gdata_contacts.h"
30 #include "cm_gdata_prefs.h"
32 #include "addr_compl.h"
33 #include "main.h"
34 #include "passwordstore.h"
35 #include "prefs_common.h"
36 #include "mainwindow.h"
37 #include "common/log.h"
38 #include "common/xml.h"
39 #include "common/utils.h"
40 #include "common/passcrypt.h"
41 #include "gtk/gtkutils.h"
43 #include <gdata/gdata.h>
45 #define GDATA_CONTACTS_FILENAME "gdata_cache.xml"
47 #define GDATA_C1 "EOnuQt4fxED3WrO//iub3KLQMScIxXiT0VBD8RZUeKjkcm1zEBVMboeWDLYyqjJKZaL4oaZ24umWygbj19T2oJR6ZpjbCw=="
48 #define GDATA_C2 "QYjIgZblg/4RMCnEqNQypcHZba9ePqAN"
49 #define GDATA_C3 "XHEZEgO06YbWfQWOyYhE/ny5Q10aNOZlkQ=="
51 #define REFRESH_TIMEOUT_MINUTES 45.0
54 typedef struct
56 const gchar *family_name;
57 const gchar *given_name;
58 const gchar *full_name;
59 const gchar *address;
60 } Contact;
62 typedef struct
64 GSList *contacts;
65 } CmGDataContactsCache;
68 static CmGDataContactsCache contacts_cache;
69 static gboolean cm_gdata_contacts_query_running = FALSE;
70 static gchar *contacts_group_id = NULL;
71 static GDataOAuth2Authorizer *authorizer = NULL;
72 static GDataContactsService *service = NULL;
73 static GTimer *refresh_timer = NULL;
76 static void protect_fields_against_NULL(Contact *contact)
78 g_return_if_fail(contact != NULL);
80 /* protect fields against being NULL */
81 if(contact->full_name == NULL)
82 contact->full_name = g_strdup("");
83 if(contact->given_name == NULL)
84 contact->given_name = g_strdup("");
85 if(contact->family_name == NULL)
86 contact->family_name = g_strdup("");
89 typedef struct
91 const gchar *auth_uri;
92 GtkWidget *entry;
93 } AuthCodeQueryButtonData;
96 static void auth_uri_link_button_clicked_cb(GtkButton *button, gpointer data)
98 AuthCodeQueryButtonData *auth_code_query_data = data;
99 open_uri(auth_code_query_data->auth_uri, prefs_common_get_uri_cmd());
100 gtk_widget_grab_focus(auth_code_query_data->entry);
103 static void auth_code_entry_changed_cb(GtkEditable *entry, gpointer data)
105 gtk_widget_set_sensitive(GTK_WIDGET(data), gtk_entry_get_text_length(GTK_ENTRY(entry)) > 0);
109 /* Returns the authorization code as newly allocated string, or NULL */
110 gchar* ask_user_for_auth_code(const gchar *auth_uri)
112 GtkWidget *dialog;
113 GtkWidget *vbox;
114 GtkWidget *table;
115 GtkWidget *link_button;
116 GtkWidget *label;
117 GtkWidget *entry;
118 gchar *str;
119 gchar *retval = NULL;
120 MainWindow *mainwin;
121 gint dlg_res;
122 GtkWidget *btn_ok;
123 AuthCodeQueryButtonData *auth_code_query_data;
125 mainwin = mainwindow_get_mainwindow();
126 dialog = gtk_message_dialog_new_with_markup(mainwin ? GTK_WINDOW(mainwin->window) : NULL,
127 GTK_DIALOG_DESTROY_WITH_PARENT,
128 GTK_MESSAGE_INFO,
129 GTK_BUTTONS_NONE,
130 "<span weight=\"bold\" size=\"larger\">%s</span>", _("GData plugin: Authorization required"));
131 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
132 _("You need to authorize Claws Mail to access your Google contact list to use the GData plugin."
133 "\n\nVisit Google's authorization page by pressing the button below. After you "
134 "confirmed the authorization, you will get an authorization code. Enter that code "
135 "in the field below to grant Claws Mail access to your Google contact list."));
136 gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Cancel"), GTK_RESPONSE_CANCEL);
137 btn_ok = gtk_dialog_add_button(GTK_DIALOG(dialog), _("_OK"), GTK_RESPONSE_OK);
138 gtk_window_set_resizable(GTK_WINDOW(dialog), TRUE);
139 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
141 gtk_widget_set_sensitive(btn_ok, FALSE);
143 table = gtk_grid_new();
144 gtk_grid_set_row_spacing(GTK_GRID(table), 8);
145 gtk_grid_set_column_spacing(GTK_GRID(table), 8);
147 str = g_strconcat("<b>", _("Step 1:"), "</b>", NULL);
148 label = gtk_label_new(str);
149 g_free(str);
150 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
151 gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1);
153 link_button = gtk_button_new_with_label(_("Click here to open the Google authorization page in a browser"));
154 auth_code_query_data = g_new0(AuthCodeQueryButtonData,1);
155 gtk_grid_attach(GTK_GRID(table), link_button, 1, 0, 1, 1);
157 str = g_strconcat("<b>", _("Step 2:"), "</b>", NULL);
158 label = gtk_label_new(str);
159 g_free(str);
160 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
161 gtk_grid_attach(GTK_GRID(table), label, 0, 1, 1, 1);
163 gtk_grid_attach(GTK_GRID(table), gtk_label_new(_("Enter code:")), 1, 1, 1, 1);
165 entry = gtk_entry_new();
166 g_signal_connect(G_OBJECT(entry), "changed", (GCallback)auth_code_entry_changed_cb, (gpointer)btn_ok);
167 gtk_grid_attach(GTK_GRID(table), entry, 2, 1, 1, 1);
168 gtk_widget_set_hexpand(entry, TRUE);
169 gtk_widget_set_halign(entry, GTK_ALIGN_FILL);
171 auth_code_query_data->auth_uri = auth_uri;
172 auth_code_query_data->entry = entry;
173 g_signal_connect(G_OBJECT(link_button), "clicked", (GCallback)auth_uri_link_button_clicked_cb, (gpointer)auth_code_query_data);
175 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
176 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
178 gtk_box_pack_start(GTK_BOX(gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(dialog))), vbox, FALSE, FALSE, 0);
180 gtk_widget_show_all(dialog);
182 dlg_res = gtk_dialog_run(GTK_DIALOG(dialog));
183 switch(dlg_res)
185 case GTK_RESPONSE_DELETE_EVENT:
186 case GTK_RESPONSE_CANCEL:
187 break;
188 case GTK_RESPONSE_OK:
189 retval = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
190 break;
193 g_free(auth_code_query_data);
194 gtk_widget_destroy(dialog);
196 return retval;
200 static void write_cache_to_file(void)
202 gchar *path;
203 PrefFile *pfile;
204 XMLTag *tag;
205 XMLNode *xmlnode;
206 GNode *rootnode;
207 GNode *contactsnode;
208 GSList *walk;
210 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, GDATA_CONTACTS_FILENAME, NULL);
211 pfile = prefs_write_open(path);
212 g_free(path);
213 if(pfile == NULL) {
214 debug_print("GData plugin error: Cannot open file " GDATA_CONTACTS_FILENAME " for writing\n");
215 return;
218 /* XML declarations */
219 xml_file_put_xml_decl(pfile->fp);
221 /* Build up XML tree */
223 /* root node */
224 tag = xml_tag_new("gdata");
225 xmlnode = xml_node_new(tag, NULL);
226 rootnode = g_node_new(xmlnode);
228 /* contacts node */
229 tag = xml_tag_new("contacts");
230 xmlnode = xml_node_new(tag, NULL);
231 contactsnode = g_node_new(xmlnode);
232 g_node_append(rootnode, contactsnode);
234 /* walk contacts cache */
235 for(walk = contacts_cache.contacts; walk; walk = walk->next)
237 GNode *contactnode;
238 Contact *contact = walk->data;
239 tag = xml_tag_new("contact");
240 xml_tag_add_attr(tag, xml_attr_new("family_name",contact->family_name));
241 xml_tag_add_attr(tag, xml_attr_new("given_name",contact->given_name));
242 xml_tag_add_attr(tag, xml_attr_new("full_name",contact->full_name));
243 xml_tag_add_attr(tag, xml_attr_new("address",contact->address));
244 xmlnode = xml_node_new(tag, NULL);
245 contactnode = g_node_new(xmlnode);
246 g_node_append(contactsnode, contactnode);
249 /* Actual writing and cleanup */
250 xml_write_tree(rootnode, pfile->fp);
251 if (prefs_file_close(pfile) < 0)
252 debug_print("GData plugin error: Failed to write file " GDATA_CONTACTS_FILENAME "\n");
253 else
254 debug_print("GData plugin: Wrote cache to file " GDATA_CONTACTS_FILENAME "\n");
256 /* Free XML tree */
257 xml_free_tree(rootnode);
260 static int add_gdata_contact_to_cache(GDataContactsContact *contact)
262 GList *walk;
263 int retval;
265 retval = 0;
266 for(walk = gdata_contacts_contact_get_email_addresses(contact); walk; walk = walk->next) {
267 const gchar *email_address;
268 GDataGDEmailAddress *address = GDATA_GD_EMAIL_ADDRESS(walk->data);
270 email_address = gdata_gd_email_address_get_address(address);
271 if(email_address && (*email_address != '\0')) {
272 GDataGDName *name;
273 Contact *cached_contact;
275 name = gdata_contacts_contact_get_name(contact);
277 cached_contact = g_new0(Contact, 1);
278 cached_contact->full_name = g_strdup(gdata_gd_name_get_full_name(name));
279 cached_contact->given_name = g_strdup(gdata_gd_name_get_given_name(name));
280 cached_contact->family_name = g_strdup(gdata_gd_name_get_family_name(name));
281 cached_contact->address = g_strdup(email_address);
283 protect_fields_against_NULL(cached_contact);
285 contacts_cache.contacts = g_slist_prepend(contacts_cache.contacts, cached_contact);
287 debug_print("GData plugin: Added %s <%s>\n", cached_contact->full_name, cached_contact->address);
288 retval = 1;
291 if(retval == 0)
293 debug_print("GData plugin: Skipped received contact \"%s\" because it doesn't have an email address\n",
294 gdata_gd_name_get_full_name(gdata_contacts_contact_get_name(contact)));
296 return retval;
299 static void free_contact(Contact *contact)
301 g_free((gpointer)contact->full_name);
302 g_free((gpointer)contact->family_name);
303 g_free((gpointer)contact->given_name);
304 g_free((gpointer)contact->address);
305 g_free(contact);
308 static void clear_contacts_cache(void)
310 GSList *walk;
311 for(walk = contacts_cache.contacts; walk; walk = walk->next)
312 free_contact(walk->data);
313 g_slist_free(contacts_cache.contacts);
314 contacts_cache.contacts = NULL;
317 static void cm_gdata_query_contacts_ready(GDataContactsService *service, GAsyncResult *res, gpointer data)
319 GDataFeed *feed;
320 GList *walk;
321 GError *error = NULL;
322 guint num_contacts = 0;
323 guint num_contacts_added = 0;
324 gchar *tmpstr1, *tmpstr2;
326 feed = gdata_service_query_finish(GDATA_SERVICE(service), res, &error);
327 cm_gdata_contacts_query_running = FALSE;
328 if(error)
330 g_object_unref(feed);
331 log_error(LOG_PROTOCOL, _("GData plugin: Error querying for contacts: %s\n"), error->message);
332 g_error_free(error);
333 return;
336 /* clear cache */
337 clear_contacts_cache();
339 /* Iterate through the returned contacts and fill the cache */
340 for(walk = gdata_feed_get_entries(feed); walk; walk = walk->next) {
341 num_contacts_added += add_gdata_contact_to_cache(GDATA_CONTACTS_CONTACT(walk->data));
342 num_contacts++;
344 g_object_unref(feed);
345 contacts_cache.contacts = g_slist_reverse(contacts_cache.contacts);
346 /* TRANSLATORS: First part of "Added X of Y contacts to cache" */
347 tmpstr1 = g_strdup_printf(ngettext("Added %d of", "Added %d of", num_contacts_added), num_contacts_added);
348 /* TRANSLATORS: Second part of "Added X of Y contacts to cache" */
349 tmpstr2 = g_strdup_printf(ngettext("1 contact to the cache", "%d contacts to the cache", num_contacts), num_contacts);
350 log_message(LOG_PROTOCOL, "%s %s\n", tmpstr1, tmpstr2);
351 g_free(tmpstr1);
352 g_free(tmpstr2);
355 static void query_contacts(GDataContactsService *service)
357 GDataContactsQuery *query;
359 log_message(LOG_PROTOCOL, _("GData plugin: Starting async contacts query\n"));
361 query = gdata_contacts_query_new(NULL);
362 gdata_contacts_query_set_group(query, contacts_group_id);
363 gdata_query_set_max_results(GDATA_QUERY(query), cm_gdata_config.max_num_results);
364 gdata_contacts_service_query_contacts_async(service, GDATA_QUERY(query), NULL, NULL, NULL,
365 NULL, (GAsyncReadyCallback)cm_gdata_query_contacts_ready, NULL);
367 g_object_unref(query);
370 static void cm_gdata_query_groups_ready(GDataContactsService *service, GAsyncResult *res, gpointer data)
372 GDataFeed *feed;
373 GList *walk;
374 GError *error = NULL;
376 feed = gdata_service_query_finish(GDATA_SERVICE(service), res, &error);
377 if(error)
379 g_object_unref(feed);
380 log_error(LOG_PROTOCOL, _("GData plugin: Error querying for groups: %s\n"), error->message);
381 g_error_free(error);
382 return;
385 /* Iterate through the returned groups and search for Contacts group id */
386 for(walk = gdata_feed_get_entries(feed); walk; walk = walk->next) {
387 const gchar *system_group_id;
388 GDataContactsGroup *group = GDATA_CONTACTS_GROUP(walk->data);
390 system_group_id = gdata_contacts_group_get_system_group_id(group);
391 if(system_group_id && !strcmp(system_group_id, GDATA_CONTACTS_GROUP_CONTACTS)) {
392 gchar *pos;
393 const gchar *id;
395 id = gdata_entry_get_id(GDATA_ENTRY(group));
397 /* possibly replace projection "full" by "base" */
398 pos = g_strrstr(id, "/full/");
399 if(pos) {
400 GString *str = g_string_new("\0");
401 int off = pos-id;
403 g_string_append_len(str, id, off);
404 g_string_append(str, "/base/");
405 g_string_append(str, id+off+strlen("/full/"));
406 g_string_append_c(str, '\0');
407 contacts_group_id = g_string_free(str, FALSE);
409 else
410 contacts_group_id = g_strdup(id);
411 break;
414 g_object_unref(feed);
416 log_message(LOG_PROTOCOL, _("GData plugin: Groups received\n"));
418 query_contacts(service);
421 static void query_for_contacts_group_id(GDataAuthorizer *authorizer)
423 log_message(LOG_PROTOCOL, _("GData plugin: Starting async groups query\n"));
425 gdata_contacts_service_query_groups_async(service, NULL, NULL, NULL, NULL, NULL,
426 (GAsyncReadyCallback)cm_gdata_query_groups_ready, NULL);
430 static void query_after_auth()
432 if(!contacts_group_id)
433 query_for_contacts_group_id(GDATA_AUTHORIZER(authorizer));
434 else
435 query_contacts(service);
439 static void cm_gdata_auth_ready(GDataOAuth2Authorizer *auth, GAsyncResult *res, gpointer data)
441 GError *error = NULL;
443 if(gdata_oauth2_authorizer_request_authorization_finish(auth, res, &error) == FALSE)
445 /* Notify the user of all errors except cancellation errors */
446 if(!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
448 log_error(LOG_PROTOCOL, _("GData plugin: Authorization error: %s\n"), error->message);
450 g_error_free(error);
451 cm_gdata_contacts_query_running = FALSE;
452 return;
455 log_message(LOG_PROTOCOL, _("GData plugin: Authorization successful\n"));
457 query_after_auth();
460 static void cm_gdata_interactive_auth()
462 static gboolean interactive_auth_running = FALSE;
464 gchar *auth_uri;
466 auth_uri = gdata_oauth2_authorizer_build_authentication_uri(authorizer, cm_gdata_config.username, FALSE);
467 g_return_if_fail(auth_uri);
469 if(!interactive_auth_running)
471 gchar *auth_code;
473 interactive_auth_running = TRUE;
475 log_message(LOG_PROTOCOL, _("GData plugin: Starting interactive authorization\n"));
477 auth_code = ask_user_for_auth_code(auth_uri);
479 if(auth_code)
481 cm_gdata_contacts_query_running = TRUE;
482 log_message(LOG_PROTOCOL, _("GData plugin: Got authorization code, requesting authorization\n"));
483 gdata_oauth2_authorizer_request_authorization_async(authorizer, auth_code, NULL, (GAsyncReadyCallback)cm_gdata_auth_ready, NULL);
484 memset(auth_code, 0, strlen(auth_code));
485 g_free(auth_code);
487 else
489 log_warning(LOG_PROTOCOL, _("GData plugin: No authorization code received, authorization request cancelled\n"));
491 interactive_auth_running = FALSE;
493 else
495 log_message(LOG_PROTOCOL, _("GData plugin: Interactive authorization still running, no additional session started\n"));
498 g_free(auth_uri);
502 static void cm_gdata_refresh_ready(GDataOAuth2Authorizer *auth, GAsyncResult *res, gpointer data)
504 GError *error = NULL;
505 gboolean start_interactive_auth = FALSE;
507 if(gdata_authorizer_refresh_authorization_finish(GDATA_AUTHORIZER(auth), res, &error) == FALSE)
509 /* Notify the user of all errors except cancellation errors */
510 if(!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
512 log_error(LOG_PROTOCOL, _("GData plugin: Authorization refresh error: %s\n"), error->message);
514 if(mainwindow_get_mainwindow())
516 mainwindow_show_error();
520 /* Only start an interactive auth session in case of authorization issues, but not
521 * for e.g. sporadic network issues or other non-authorization-related problems. */
522 start_interactive_auth =
523 g_error_matches(error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_AUTHENTICATION_REQUIRED) ||
524 g_error_matches(error, GDATA_SERVICE_ERROR, GDATA_SERVICE_ERROR_FORBIDDEN);
526 g_error_free(error);
528 if(start_interactive_auth)
530 cm_gdata_interactive_auth();
533 return;
536 log_message(LOG_PROTOCOL, _("GData plugin: Authorization refresh successful\n"));
538 g_timer_start(refresh_timer);
540 query_after_auth();
544 /* returns allocated string which must be freed */
545 static guchar* decode(const gchar *in)
547 guchar *tmp;
548 gsize len;
550 tmp = g_base64_decode(in, &len);
551 passcrypt_decrypt(tmp, len);
552 return tmp;
556 static void query()
558 gchar *token;
559 int elapsed_time_min;
561 if(cm_gdata_contacts_query_running)
563 debug_print("GData plugin: Network query already in progress\n");
564 return;
567 if(!authorizer)
569 gchar *c1 = decode(GDATA_C1);
570 gchar *c2 = decode(GDATA_C2);
571 gchar *c3 = decode(GDATA_C3);
573 authorizer = gdata_oauth2_authorizer_new(c1, c2, c3, GDATA_TYPE_CONTACTS_SERVICE);
575 g_free(c1);
576 g_free(c2);
577 g_free(c3);
579 g_return_if_fail(authorizer);
581 if(!service)
583 service = gdata_contacts_service_new(GDATA_AUTHORIZER(authorizer));
585 g_return_if_fail(service);
587 if(!refresh_timer)
589 refresh_timer = g_timer_new();
591 g_return_if_fail(refresh_timer);
593 elapsed_time_min = (int)((g_timer_elapsed(refresh_timer, NULL)/60.0)+0.5);
594 if(elapsed_time_min > REFRESH_TIMEOUT_MINUTES)
596 log_message(LOG_PROTOCOL, _("GData plugin: Elapsed time since last refresh: %d minutes, refreshing now\n"), elapsed_time_min);
597 gdata_authorizer_refresh_authorization_async(GDATA_AUTHORIZER(authorizer), NULL, (GAsyncReadyCallback)cm_gdata_refresh_ready, NULL);
599 else if(!gdata_service_is_authorized(GDATA_SERVICE(service)))
601 /* Try to restore from saved refresh token.*/
602 if((token = passwd_store_get(PWS_PLUGIN, "GData", GDATA_TOKEN_PWD_STRING)) != NULL)
604 log_message(LOG_PROTOCOL, _("GData plugin: Trying to refresh authorization\n"));
605 gdata_oauth2_authorizer_set_refresh_token(authorizer, token);
606 memset(token, 0, strlen(token));
607 g_free(token);
608 gdata_authorizer_refresh_authorization_async(GDATA_AUTHORIZER(authorizer), NULL, (GAsyncReadyCallback)cm_gdata_refresh_ready, NULL);
610 else
612 cm_gdata_interactive_auth();
615 else
617 query_after_auth();
622 static void add_contacts_to_list(GList **address_list, GSList *contacts)
624 GSList *walk;
626 for(walk = contacts; walk; walk = walk->next)
628 address_entry *ae;
629 Contact *contact = walk->data;
631 ae = g_new0(address_entry, 1);
632 ae->name = g_strdup(contact->full_name);
633 ae->address = g_strdup(contact->address);
634 ae->grp_emails = NULL;
636 *address_list = g_list_prepend(*address_list, ae);
637 addr_compl_add_address1(ae->address, ae);
639 if(contact->given_name && *(contact->given_name) != '\0')
640 addr_compl_add_address1(contact->given_name, ae);
642 if(contact->family_name && *(contact->family_name) != '\0')
643 addr_compl_add_address1(contact->family_name, ae);
647 void cm_gdata_add_contacts(GList **address_list)
649 add_contacts_to_list(address_list, contacts_cache.contacts);
652 gboolean cm_gdata_update_contacts_cache(void)
654 if(prefs_common_get_prefs()->work_offline)
656 debug_print("GData plugin: Offline mode\n");
658 else
660 debug_print("GData plugin: Querying contacts\n");
661 query();
663 return TRUE;
666 void cm_gdata_contacts_done(void)
668 gchar *pass;
670 g_free(contacts_group_id);
671 contacts_group_id = NULL;
673 write_cache_to_file();
674 if(contacts_cache.contacts && !claws_is_exiting())
675 clear_contacts_cache();
677 if(authorizer)
679 /* store refresh token */
680 pass = gdata_oauth2_authorizer_dup_refresh_token(authorizer);
681 passwd_store_set(PWS_PLUGIN, "GData", GDATA_TOKEN_PWD_STRING, pass, FALSE);
682 if (pass != NULL) {
683 memset(pass, 0, strlen(pass));
684 g_free(pass);
686 passwd_store_write_config();
688 g_object_unref(G_OBJECT(authorizer));
689 authorizer = NULL;
692 if(service)
694 g_object_unref(G_OBJECT(service));
695 service = NULL;
698 if(refresh_timer)
700 g_timer_destroy(refresh_timer);
701 refresh_timer = NULL;
705 void cm_gdata_load_contacts_cache_from_file(void)
707 gchar *path;
708 GNode *rootnode, *childnode, *contactnode;
709 XMLNode *xmlnode;
711 path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, GDATA_CONTACTS_FILENAME, NULL);
712 if(!is_file_exist(path)) {
713 g_free(path);
714 return;
717 /* no merging; make sure the cache is empty (this should be a noop, but just to be safe...) */
718 clear_contacts_cache();
720 rootnode = xml_parse_file(path);
721 g_free(path);
722 if(!rootnode)
723 return;
724 xmlnode = rootnode->data;
726 /* Check that root entry is "gdata" */
727 if(g_strcmp0(xmlnode->tag->tag, "gdata") != 0) {
728 g_warning("wrong gdata cache file");
729 xml_free_tree(rootnode);
730 return;
733 for(childnode = rootnode->children; childnode; childnode = childnode->next) {
734 GList *attributes;
735 xmlnode = childnode->data;
737 if(g_strcmp0(xmlnode->tag->tag, "contacts") != 0)
738 continue;
740 for(contactnode = childnode->children; contactnode; contactnode = contactnode->next)
742 Contact *cached_contact;
744 xmlnode = contactnode->data;
746 cached_contact = g_new0(Contact, 1);
747 /* Attributes of the branch nodes */
748 for(attributes = xmlnode->tag->attr; attributes; attributes = attributes->next) {
749 XMLAttr *attr = attributes->data;
751 if(attr && attr->name && attr->value) {
752 if(!g_strcmp0(attr->name, "full_name"))
753 cached_contact->full_name = g_strdup(attr->value);
754 else if(!g_strcmp0(attr->name, "given_name"))
755 cached_contact->given_name = g_strdup(attr->value);
756 else if(!g_strcmp0(attr->name, "family_name"))
757 cached_contact->family_name = g_strdup(attr->value);
758 else if(!g_strcmp0(attr->name, "address"))
759 cached_contact->address = g_strdup(attr->value);
763 if(cached_contact->address)
765 protect_fields_against_NULL(cached_contact);
767 contacts_cache.contacts = g_slist_prepend(contacts_cache.contacts, cached_contact);
768 debug_print("Read contact from cache: %s\n", cached_contact->full_name);
770 else {
771 debug_print("Ignored contact without email address: %s\n", cached_contact->full_name ? cached_contact->full_name : "(null)");
772 /* Not added to list: return allocated memory */
773 if (cached_contact->full_name)
774 g_free((gchar *) cached_contact->full_name);
775 if (cached_contact->given_name)
776 g_free((gchar *) cached_contact->given_name);
777 if (cached_contact->family_name)
778 g_free((gchar *) cached_contact->family_name);
779 if (cached_contact->address)
780 g_free((gchar *) cached_contact->address);
781 g_free(cached_contact);
786 /* Free XML tree */
787 xml_free_tree(rootnode);
789 contacts_cache.contacts = g_slist_reverse(contacts_cache.contacts);