[Commits] Makefile.am NONE 1.1.2.1 cm_gdata_contacts.c NONE 1.1.2.1 cm_gdata_contacts.h NONE 1.1.2.1 cm_gdata_prefs.c NONE 1.1.2.1 cm_gdata_prefs.h NONE 1.1.2.1 gdata_plugin.c NONE 1.1.2.1 gdata_plugin.h NONE 1.1.2.1 placeholder.txt 1.1.2.1 NONE

colin at claws-mail.org colin at claws-mail.org
Sun Feb 17 22:21:59 CET 2013


Update of /home/claws-mail/claws/src/plugins/gdata
In directory srv:/tmp/cvs-serv7104/src/plugins/gdata

Added Files:
      Tag: gtk2
	Makefile.am cm_gdata_contacts.c cm_gdata_contacts.h 
	cm_gdata_prefs.c cm_gdata_prefs.h gdata_plugin.c 
	gdata_plugin.h 
Removed Files:
      Tag: gtk2
	placeholder.txt 
Log Message:
2013-02-17 [colin]	3.9.0cvs75

	* src/plugins/Makefile.am
	* src/plugins/archive/Makefile.am
	* src/plugins/clamd/Makefile.am
	* src/plugins/clamd/clamav_plugin.c
	* src/plugins/clamd/clamav_plugin.h
	* src/plugins/clamd/clamav_plugin_gtk.c
	* src/plugins/clamd/placeholder.txt
	* src/plugins/clamd/libclamd/Makefile.am
	* src/plugins/clamd/libclamd/clamd-plugin.c
	* src/plugins/clamd/libclamd/clamd-plugin.h
	* src/plugins/fetchinfo/Makefile.am
	* src/plugins/fetchinfo/fetchinfo_plugin.c
	* src/plugins/fetchinfo/fetchinfo_plugin.h
	* src/plugins/fetchinfo/fetchinfo_plugin_gtk.c
	* src/plugins/fetchinfo/placeholder.txt
	* src/plugins/gdata/Makefile.am
	* src/plugins/gdata/cm_gdata_contacts.c
	* src/plugins/gdata/cm_gdata_contacts.h
	* src/plugins/gdata/cm_gdata_prefs.c
	* src/plugins/gdata/cm_gdata_prefs.h
	* src/plugins/gdata/gdata_plugin.c
	* src/plugins/gdata/gdata_plugin.h
	* src/plugins/gdata/placeholder.txt
	* src/plugins/geolocation/placeholder.txt
	* src/plugins/gtkhtml2_viewer/placeholder.txt
	* src/plugins/mailmbox/Makefile.am
	* src/plugins/mailmbox/carray.c
	* src/plugins/mailmbox/carray.h
	* src/plugins/mailmbox/chash.c
	* src/plugins/mailmbox/chash.h
	* src/plugins/mailmbox/clist.c
	* src/plugins/mailmbox/clist.h
	* src/plugins/mailmbox/mailimf.c
	* src/plugins/mailmbox/mailimf.h
	* src/plugins/mailmbox/mailimf_types.c
	* src/plugins/mailmbox/mailimf_types.h
	* src/plugins/mailmbox/mailimf_types_helper.c
	* src/plugins/mailmbox/mailimf_types_helper.h
	* src/plugins/mailmbox/mailimf_write.c
	* src/plugins/mailmbox/mailimf_write.h
	* src/plugins/mailmbox/maillock.c
	* src/plugins/mailmbox/maillock.h
	* src/plugins/mailmbox/mailmbox.c
	* src/plugins/mailmbox/mailmbox.h
	* src/plugins/mailmbox/mailmbox_folder.c
	* src/plugins/mailmbox/mailmbox_folder.h
	* src/plugins/mailmbox/mailmbox_gtk.deps
	* src/plugins/mailmbox/mailmbox_parse.c
	* src/plugins/mailmbox/mailmbox_parse.h
	* src/plugins/mailmbox/mailmbox_types.c
	* src/plugins/mailmbox/mailmbox_types.h
	* src/plugins/mailmbox/mmapstring.c
	* src/plugins/mailmbox/mmapstring.h
	* src/plugins/mailmbox/placeholder.txt
	* src/plugins/mailmbox/plugin.c
	* src/plugins/mailmbox/plugin_gtk.c
	* src/plugins/mailmbox/plugin_gtk.h
	* src/plugins/newmail/Makefile.am
	* src/plugins/newmail/newmail.c
	* src/plugins/newmail/placeholder.txt
	* src/plugins/notification/Makefile.am
	* src/plugins/notification/claws.def
	* src/plugins/notification/notification_banner.c
	* src/plugins/notification/notification_banner.h
	* src/plugins/notification/notification_command.c
	* src/plugins/notification/notification_command.h
	* src/plugins/notification/notification_core.c
	* src/plugins/notification/notification_core.h
	* src/plugins/notification/notification_foldercheck.c
	* src/plugins/notification/notification_foldercheck.h
	* src/plugins/notification/notification_hotkeys.c
	* src/plugins/notification/notification_hotkeys.h
	* src/plugins/notification/notification_indicator.c
	* src/plugins/notification/notification_indicator.h
	* src/plugins/notification/notification_lcdproc.c
	* src/plugins/notification/notification_lcdproc.h
	* src/plugins/notification/notification_pixbuf.c
	* src/plugins/notification/notification_pixbuf.h
	* src/plugins/notification/notification_plugin.c
	* src/plugins/notification/notification_plugin.h
	* src/plugins/notification/notification_popup.c
	* src/plugins/notification/notification_popup.h
	* src/plugins/notification/notification_prefs.c
	* src/plugins/notification/notification_prefs.h
	* src/plugins/notification/notification_trayicon.c
	* src/plugins/notification/notification_trayicon.h
	* src/plugins/notification/placeholder.txt
	* src/plugins/notification/plugin.def
	* src/plugins/notification/raw_claws_mail_logo_64x64.h
	* src/plugins/notification/version.rc
	* src/plugins/pdf_viewer/Makefile.am
	* src/plugins/pdf_viewer/doc_index.xpm
	* src/plugins/pdf_viewer/doc_index_close.xpm
	* src/plugins/pdf_viewer/doc_info.xpm
	* src/plugins/pdf_viewer/first_arrow.xpm
	* src/plugins/pdf_viewer/last_arrow.xpm
	* src/plugins/pdf_viewer/left_arrow.xpm
	* src/plugins/pdf_viewer/placeholder.txt
	* src/plugins/pdf_viewer/poppler_viewer.c
	* src/plugins/pdf_viewer/poppler_viewer.h
	* src/plugins/pdf_viewer/right_arrow.xpm
	* src/plugins/pdf_viewer/rotate_left.xpm
	* src/plugins/pdf_viewer/rotate_right.xpm
	* src/plugins/pdf_viewer/zoom_fit.xpm
	* src/plugins/pdf_viewer/zoom_in.xpm
	* src/plugins/pdf_viewer/zoom_out.xpm
	* src/plugins/pdf_viewer/zoom_width.xpm
	* src/plugins/perl/Makefile.am
	* src/plugins/perl/perl_gtk.c
	* src/plugins/perl/perl_gtk.h
	* src/plugins/perl/perl_plugin.c
	* src/plugins/perl/perl_plugin.h
	* src/plugins/perl/placeholder.txt
	* src/plugins/python/Makefile.am
	* src/plugins/python/clawsmailmodule.c
	* src/plugins/python/clawsmailmodule.h
	* src/plugins/python/composewindowtype.c
	* src/plugins/python/composewindowtype.h
	* src/plugins/python/foldertype.c
	* src/plugins/python/foldertype.h
	* src/plugins/python/messageinfotype.c
	* src/plugins/python/messageinfotype.h
	* src/plugins/python/nodetype.c
	* src/plugins/python/nodetype.h
	* src/plugins/python/placeholder.txt
	* src/plugins/python/python-hooks.c
	* src/plugins/python/python-hooks.h
	* src/plugins/python/python-shell.c
	* src/plugins/python/python-shell.h
	* src/plugins/python/python_plugin.c
	* src/plugins/vcalendar/Makefile.in
		Add some plugins (clamd, fetchinfo, gdata, mailmbox, newmail,
		notification, pdf_viewer, perl, python). Notification not yet
		enabled because it has too much autoconf switches for my taste.

--- NEW FILE: cm_gdata_contacts.c ---
/* GData plugin for Claws-Mail
 * Copyright (C) 2011 Holger Berndt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */


#ifdef HAVE_CONFIG_H
#  include "config.h"
#  include "claws-features.h"
#endif

#include <glib.h>
#include <glib/gi18n.h>

#include "cm_gdata_contacts.h"
#include "cm_gdata_prefs.h"

#include <gtk/gtk.h>
#include "addr_compl.h"
#include "main.h"
#include "prefs_common.h"
#include "common/log.h"
#include "common/xml.h"

#include <gdata/gdata.h>

#define GDATA_CONTACTS_FILENAME "gdata_cache.xml"

typedef struct
{
  const gchar *family_name;
  const gchar *given_name;
  const gchar *full_name;
  const gchar *address;
} Contact;

typedef struct
{
  GSList *contacts;
} CmGDataContactsCache;


CmGDataContactsCache contacts_cache;
gboolean cm_gdata_contacts_query_running = FALSE;
gchar *contacts_group_id = NULL;

static void write_cache_to_file(void)
{
  gchar *path;
  PrefFile *pfile;
  XMLTag *tag;
  XMLNode *xmlnode;
  GNode *rootnode;
  GNode *contactsnode;
  GSList *walk;

  path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, GDATA_CONTACTS_FILENAME, NULL);
  pfile = prefs_write_open(path);
  g_free(path);
  if(pfile == NULL) {
    debug_print("GData plugin error: Cannot open file " GDATA_CONTACTS_FILENAME " for writing\n");
    return;
  }

  /* XML declarations */
  xml_file_put_xml_decl(pfile->fp);

  /* Build up XML tree */

  /* root node */
  tag = xml_tag_new("gdata");
  xmlnode = xml_node_new(tag, NULL);
  rootnode = g_node_new(xmlnode);

  /* contacts node */
  tag = xml_tag_new("contacts");
  xmlnode = xml_node_new(tag, NULL);
  contactsnode = g_node_new(xmlnode);
  g_node_append(rootnode, contactsnode);

  /* walk contacts cache */
  for(walk = contacts_cache.contacts; walk; walk = walk->next)
  {
    GNode *contactnode;
    Contact *contact = walk->data;
    tag = xml_tag_new("contact");
    xml_tag_add_attr(tag, xml_attr_new("family_name",contact->family_name));
    xml_tag_add_attr(tag, xml_attr_new("given_name",contact->given_name));
    xml_tag_add_attr(tag, xml_attr_new("full_name",contact->full_name));
    xml_tag_add_attr(tag, xml_attr_new("address",contact->address));
    xmlnode = xml_node_new(tag, NULL);
    contactnode = g_node_new(xmlnode);
    g_node_append(contactsnode, contactnode);
  }

  /* Actual writing and cleanup */
  xml_write_tree(rootnode, pfile->fp);
  if(prefs_file_close(pfile) < 0)
    debug_print("GData plugin error: Failed to write file " GDATA_CONTACTS_FILENAME "\n");

  debug_print("GData plugin error: Wrote cache to file " GDATA_CONTACTS_FILENAME "\n");

  /* Free XML tree */
  xml_free_tree(rootnode);
}

static int add_gdata_contact_to_cache(GDataContactsContact *contact)
{
  GList *walk;
  int retval;

  retval = 0;
  for(walk = gdata_contacts_contact_get_email_addresses(contact); walk; walk = walk->next) {
    const gchar *email_address;
    GDataGDEmailAddress *address = GDATA_GD_EMAIL_ADDRESS(walk->data);

    email_address = gdata_gd_email_address_get_address(address);
    if(email_address && (*email_address != '\0')) {
      GDataGDName *name;
      Contact *cached_contact;

      name = gdata_contacts_contact_get_name(contact);

      cached_contact = g_new0(Contact, 1);
      cached_contact->full_name = g_strdup(gdata_gd_name_get_full_name(name));
      cached_contact->given_name = g_strdup(gdata_gd_name_get_given_name(name));
      cached_contact->family_name = g_strdup(gdata_gd_name_get_family_name(name));
      cached_contact->address = g_strdup(email_address);

      contacts_cache.contacts = g_slist_prepend(contacts_cache.contacts, cached_contact);

      debug_print("GData plugin: Added %s <%s>\n", cached_contact->full_name, cached_contact->address);
      retval = 1;
    }
  }
  if(retval == 0)
  {
    debug_print("GData plugin: Skipped received contact \"%s\" because it doesn't have an email address\n",
        gdata_gd_name_get_full_name(gdata_contacts_contact_get_name(contact)));
  }
  return retval;
}

static void free_contact(Contact *contact)
{
  g_free((gpointer)contact->full_name);
  g_free((gpointer)contact->family_name);
  g_free((gpointer)contact->given_name);
  g_free((gpointer)contact->address);
  g_free(contact);
}

static void clear_contacts_cache(void)
{
  GSList *walk;
  for(walk = contacts_cache.contacts; walk; walk = walk->next)
    free_contact(walk->data);
  g_slist_free(contacts_cache.contacts);
  contacts_cache.contacts = NULL;
}

static void cm_gdata_query_contacts_ready(GDataContactsService *service, GAsyncResult *res, gpointer data)
{
  GDataFeed *feed;
  GList *walk;
  GError *error = NULL;
  guint num_contacts = 0;
  guint num_contacts_added = 0;
	gchar *tmpstr1, *tmpstr2;

  feed = gdata_service_query_finish(GDATA_SERVICE(service), res, &error);
  cm_gdata_contacts_query_running = FALSE;
  if(error)
  {
    g_object_unref(feed);
    log_error(LOG_PROTOCOL, _("GData plugin: Error querying for contacts: %s\n"), error->message);
    g_error_free(error);
    return;
  }

  /* clear cache */
  clear_contacts_cache();

  /* Iterate through the returned contacts and fill the cache */
  for(walk = gdata_feed_get_entries(feed); walk; walk = walk->next) {
    num_contacts_added += add_gdata_contact_to_cache(GDATA_CONTACTS_CONTACT(walk->data));
    num_contacts++;
  }
  g_object_unref(feed);
  contacts_cache.contacts = g_slist_reverse(contacts_cache.contacts);
	/* i18n: First part of "Added X of Y contacts to cache" */
  tmpstr1 = g_strdup_printf(ngettext("Added %d of", "Added %d of", num_contacts_added), num_contacts_added);
	/* i18n: Second part of "Added X of Y contacts to cache" */
  tmpstr2 = g_strdup_printf(ngettext("1 contact to the cache", "%d contacts to the cache", num_contacts), num_contacts);
  log_message(LOG_PROTOCOL, "%s %s\n", tmpstr1, tmpstr2);
	g_free(tmpstr1);
	g_free(tmpstr2);
}

static void query_after_auth(GDataContactsService *service)
{
  GDataContactsQuery *query;

  log_message(LOG_PROTOCOL, _("GData plugin: Starting async contacts query\n"));

  query = gdata_contacts_query_new(NULL);
  gdata_contacts_query_set_group(query, contacts_group_id);
  gdata_query_set_max_results(GDATA_QUERY(query), cm_gdata_config.max_num_results);
  gdata_contacts_service_query_contacts_async(service, GDATA_QUERY(query), NULL, NULL, NULL,
#ifdef HAVE_GDATA_VERSION_0_9_1
  NULL,
#endif
  (GAsyncReadyCallback)cm_gdata_query_contacts_ready, NULL);

  g_object_unref(query);
}

#ifdef HAVE_GDATA_VERSION_0_9_1
static void cm_gdata_query_groups_ready(GDataContactsService *service, GAsyncResult *res, gpointer data)
{
  GDataFeed *feed;
  GList *walk;
  GError *error = NULL;

  feed = gdata_service_query_finish(GDATA_SERVICE(service), res, &error);
  if(error)
  {
    g_object_unref(feed);
    log_error(LOG_PROTOCOL, _("GData plugin: Error querying for groups: %s\n"), error->message);
    g_error_free(error);
    return;
  }

  /* Iterate through the returned groups and search for Contacts group id */
  for(walk = gdata_feed_get_entries(feed); walk; walk = walk->next) {
    const gchar *system_group_id;
    GDataContactsGroup *group = GDATA_CONTACTS_GROUP(walk->data);

    system_group_id = gdata_contacts_group_get_system_group_id(group);
    if(system_group_id && !strcmp(system_group_id, GDATA_CONTACTS_GROUP_CONTACTS)) {
      gchar *pos;
      const gchar *id;

      id = gdata_entry_get_id(GDATA_ENTRY(group));

      /* possibly replace projection "full" by "base" */
      pos = g_strrstr(id, "/full/");
      if(pos) {
        GString *str = g_string_new("\0");
        int off = pos-id;

        g_string_append_len(str, id, off);
        g_string_append(str, "/base/");
        g_string_append(str, id+off+strlen("/full/"));
        g_string_append_c(str, '\0');
        contacts_group_id = str->str;
        g_string_free(str, FALSE);
      }
      else
        contacts_group_id = g_strdup(id);
      break;
    }
  }
  g_object_unref(feed);

  log_message(LOG_PROTOCOL, _("GData plugin: Groups received\n"));

  query_after_auth(service);
}
#endif

#ifdef HAVE_GDATA_VERSION_0_9
static void query_for_contacts_group_id(GDataClientLoginAuthorizer *authorizer)
{
  GDataContactsService *service;
#ifdef HAVE_GDATA_VERSION_0_9_1

  log_message(LOG_PROTOCOL, _("GData plugin: Starting async groups query\n"));

  service = gdata_contacts_service_new(GDATA_AUTHORIZER(authorizer));
  gdata_contacts_service_query_groups_async(service, NULL, NULL, NULL, NULL, NULL,
      (GAsyncReadyCallback)cm_gdata_query_groups_ready, NULL);
#else
  service = gdata_contacts_service_new(GDATA_AUTHORIZER(authorizer));
  query_after_auth(service);
#endif
  g_object_unref(service);
}

static void cm_gdata_auth_ready(GDataClientLoginAuthorizer *authorizer, GAsyncResult *res, gpointer data)
{
  GError *error = NULL;

  if(gdata_client_login_authorizer_authenticate_finish(authorizer, res, &error) == FALSE)
  {
    log_error(LOG_PROTOCOL, _("GData plugin: Authentication error: %s\n"), error->message);
    g_error_free(error);
    cm_gdata_contacts_query_running = FALSE;
    return;
  }

  log_message(LOG_PROTOCOL, _("GData plugin: Authenticated\n"));

  if(!contacts_group_id)
  {
    query_for_contacts_group_id(authorizer);
  }
  else {
    GDataContactsService *service;
    service = gdata_contacts_service_new(GDATA_AUTHORIZER(authorizer));
    query_after_auth(service);
    g_object_unref(service);
  }
}
#else
static void cm_gdata_auth_ready(GDataContactsService *service, GAsyncResult *res, gpointer data)
{
  GError *error = NULL;

  if(!gdata_service_authenticate_finish(GDATA_SERVICE(service), res, &error))
  {
    log_error(LOG_PROTOCOL, _("GData plugin: Authentication error: %s\n"), error->message);
    g_error_free(error);
    cm_gdata_contacts_query_running = FALSE;
    return;
  }

  log_message(LOG_PROTOCOL, _("GData plugin: Authenticated\n"));

  query_after_auth(service);
}
#endif
static void query()
{

#ifdef HAVE_GDATA_VERSION_0_9
  GDataClientLoginAuthorizer *authorizer;
#else
  GDataContactsService *service;
#endif

  if(cm_gdata_contacts_query_running)
  {
    debug_print("GData plugin: Network query already in progress");
    return;
  }

  log_message(LOG_PROTOCOL, _("GData plugin: Starting async authentication\n"));

#ifdef HAVE_GDATA_VERSION_0_9
  authorizer = gdata_client_login_authorizer_new(CM_GDATA_CLIENT_ID, GDATA_TYPE_CONTACTS_SERVICE);
  gdata_client_login_authorizer_authenticate_async(authorizer, cm_gdata_config.username, cm_gdata_config.password, NULL, (GAsyncReadyCallback)cm_gdata_auth_ready, NULL);
  cm_gdata_contacts_query_running = TRUE;
#else
  service = gdata_contacts_service_new(CM_GDATA_CLIENT_ID);
  cm_gdata_contacts_query_running = TRUE;
  gdata_service_authenticate_async(GDATA_SERVICE(service), cm_gdata_config.username, cm_gdata_config.password, NULL,
      (GAsyncReadyCallback)cm_gdata_auth_ready, NULL);
#endif


#ifdef HAVE_GDATA_VERSION_0_9
  g_object_unref(authorizer);
#else
  g_object_unref(service);
#endif

}


static void add_contacts_to_list(GList **address_list, GSList *contacts)
{
  GSList *walk;

  for(walk = contacts; walk; walk = walk->next)
  {
    address_entry *ae;
    Contact *contact = walk->data;

    ae = g_new0(address_entry, 1);
    ae->name = g_strdup(contact->full_name);
    ae->address = g_strdup(contact->address);
    ae->grp_emails = NULL;

    *address_list = g_list_prepend(*address_list, ae);
    addr_compl_add_address1(ae->address, ae);

    if(contact->given_name && *(contact->given_name) != '\0')
      addr_compl_add_address1(contact->given_name, ae);

    if(contact->family_name && *(contact->family_name) != '\0')
      addr_compl_add_address1(contact->family_name, ae);
  }
}

void cm_gdata_add_contacts(GList **address_list)
{
  add_contacts_to_list(address_list, contacts_cache.contacts);
}

gboolean cm_gdata_update_contacts_cache(void)
{
  if(prefs_common.work_offline)
  {
    debug_print("GData plugin: Offline mode\n");
  }
  else if(!cm_gdata_config.username || *(cm_gdata_config.username) == '\0' || !cm_gdata_config.password)
  {
    /* noop if no credentials are given */
    debug_print("GData plugin: Empty username or password\n");
  }
  else
  {
    debug_print("GData plugin: Querying contacts");
    query();
  }
  return TRUE;
}

void cm_gdata_contacts_done(void)
{
  g_free(contacts_group_id);
  contacts_group_id = NULL;

  write_cache_to_file();
  if(contacts_cache.contacts && !claws_is_exiting())
    clear_contacts_cache();
}

void cm_gdata_load_contacts_cache_from_file(void)
{
  gchar *path;
  GNode *rootnode, *childnode, *contactnode;
  XMLNode *xmlnode;

  path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, GDATA_CONTACTS_FILENAME, NULL);
  if(!is_file_exist(path)) {
    g_free(path);
    return;
  }

  /* no merging; make sure the cache is empty (this should be a noop, but just to be safe...) */
  clear_contacts_cache();

  rootnode = xml_parse_file(path);
  g_free(path);
  if(!rootnode)
    return;
  xmlnode = rootnode->data;

  /* Check that root entry is "gdata" */
  if(strcmp2(xmlnode->tag->tag, "gdata") != 0) {
    g_warning("wrong gdata cache file\n");
    xml_free_tree(rootnode);
    return;
  }

  for(childnode = rootnode->children; childnode; childnode = childnode->next) {
    GList *attributes;
    xmlnode = childnode->data;

    if(strcmp2(xmlnode->tag->tag, "contacts") != 0)
      continue;

    for(contactnode = childnode->children; contactnode; contactnode = contactnode->next)
    {
      Contact *cached_contact;

      xmlnode = contactnode->data;

      cached_contact = g_new0(Contact, 1);
      /* Attributes of the branch nodes */
      for(attributes = xmlnode->tag->attr; attributes; attributes = attributes->next) {
        XMLAttr *attr = attributes->data;

        if(attr && attr->name && attr->value) {
          if(!strcmp2(attr->name, "full_name"))
            cached_contact->full_name = g_strdup(attr->value);
          else if(!strcmp2(attr->name, "given_name"))
            cached_contact->given_name = g_strdup(attr->value);
          else if(!strcmp2(attr->name, "family_name"))
            cached_contact->family_name = g_strdup(attr->value);
          else if(!strcmp2(attr->name, "address"))
            cached_contact->address = g_strdup(attr->value);
        }
      }
      contacts_cache.contacts = g_slist_prepend(contacts_cache.contacts, cached_contact);
      debug_print("Read contact from cache: %s\n", cached_contact->full_name);
    }
  }

  /* Free XML tree */
  xml_free_tree(rootnode);

  contacts_cache.contacts = g_slist_reverse(contacts_cache.contacts);
}

--- NEW FILE: gdata_plugin.h ---
/* GData plugin for Claws-Mail
 * Copyright (C) 2011 Holger Berndt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef GDATA_PLUGIN_H_
#define GDATA_PLUGIN_H_

void cm_gdata_update_contacts_update_timer(void);

#endif /* GDATA_PLUGIN_H_ */

--- NEW FILE: gdata_plugin.c ---
/* GData plugin for Claws-Mail
 * Copyright (C) 2011 Holger Berndt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */


#ifdef HAVE_CONFIG_H
#  include "config.h"
#  include "claws-features.h"
#endif

#include <glib.h>
#include <glib/gi18n.h>

#ifdef G_OS_UNIX
# include <libintl.h>
#endif

#include "common/plugin.h"
#include "common/version.h"
#include "common/utils.h"
#include "common/hooks.h"
#include "common/defs.h"
#include "common/prefs.h"
#include "main.h"
#include "mainwindow.h"
#include "addr_compl.h"

#include "cm_gdata_contacts.h"
#include "cm_gdata_prefs.h"

static guint hook_address_completion;
static guint hook_offline_switch;
static guint timer_query_contacts = 0;

static gboolean my_address_completion_build_list_hook(gpointer source, gpointer data)
{
  cm_gdata_add_contacts(source);
  return FALSE;
}

static gboolean my_offline_switch_hook(gpointer source, gpointer data)
{
  cm_gdata_update_contacts_cache();
  return FALSE;
}

static void cm_gdata_save_config(void)
{
  PrefFile *pfile;
  gchar *rcpath;

  debug_print("Saving GData plugin configuration...\n");

  rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
  pfile = prefs_write_open(rcpath);
  g_free(rcpath);
  if (!pfile || (prefs_set_block_label(pfile, "GDataPlugin") < 0))
    return;

  if (prefs_write_param(cm_gdata_param, pfile->fp) < 0) {
    debug_print("failed!\n");
    g_warning(_("\nGData Plugin: Failed to write plugin configuration to file\n"));
    prefs_file_close_revert(pfile);
    return;
  }
  if (fprintf(pfile->fp, "\n") < 0) {
    FILE_OP_ERROR(rcpath, "fprintf");
    prefs_file_close_revert(pfile);
  }
  else
    prefs_file_close(pfile);
  debug_print("done.\n");
}

void cm_gdata_update_contacts_update_timer(void)
{
  if(timer_query_contacts != 0)
    g_source_remove(timer_query_contacts);
  timer_query_contacts = g_timeout_add_seconds(cm_gdata_config.max_cache_age, (GSourceFunc)cm_gdata_update_contacts_cache, NULL);
}

gint plugin_init(gchar **error)
{
  gchar *rcpath;

  /* Version check */
  if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,7,1,55),
			   VERSION_NUMERIC, _("GData"), error))
    return -1;

  hook_address_completion = hooks_register_hook(ADDDRESS_COMPLETION_BUILD_ADDRESS_LIST_HOOKLIST,
      my_address_completion_build_list_hook, NULL);
  if(hook_address_completion == (guint) -1) {
    *error = g_strdup(_("Failed to register address completion hook in the GData plugin"));
    return -1;
  }

  hook_offline_switch = hooks_register_hook(OFFLINE_SWITCH_HOOKLIST, my_offline_switch_hook, NULL);
  if(hook_offline_switch == (guint) -1) {
    hooks_unregister_hook(ADDDRESS_COMPLETION_BUILD_ADDRESS_LIST_HOOKLIST, hook_address_completion);
    *error = g_strdup(_("Failed to register offline switch hook in the GData plugin"));
    return -1;
  }

  /* Configuration */
  prefs_set_default(cm_gdata_param);
  rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
  prefs_read_config(cm_gdata_param, "GDataPlugin", rcpath, NULL);
  g_free(rcpath);

  cm_gdata_prefs_init();

  /* contacts cache */
  cm_gdata_load_contacts_cache_from_file();
  cm_gdata_update_contacts_update_timer();
  cm_gdata_update_contacts_cache();

  debug_print("GData plugin loaded\n");

  return 0;
}

gboolean plugin_done(void)
{
  if(!claws_is_exiting()) {
    hooks_unregister_hook(ADDDRESS_COMPLETION_BUILD_ADDRESS_LIST_HOOKLIST, hook_address_completion);
    hooks_unregister_hook(OFFLINE_SWITCH_HOOKLIST, hook_offline_switch);
    g_source_remove(timer_query_contacts);
  }
  cm_gdata_prefs_done();
  cm_gdata_contacts_done();

  cm_gdata_save_config();

  debug_print("GData plugin unloaded\n");

  /* returning FALSE because dependant libraries may not be unload-safe. */
  return FALSE;
}

const gchar *plugin_name(void)
{
  return _("GData");
}

const gchar *plugin_desc(void)
{
  return _("This plugin provides access to the GData protocol "
	   "for Claws Mail.\n\n"
      "The GData protocol is an interface to Google services.\n"
      "Currently, the only implemented functionality is to include "
      "Google Contacts into the Tab-address completion.\n"
     "\nFeedback to <berndth at gmx.de> is welcome.");
}

const gchar *plugin_type(void)
{
  return "GTK2";
}

const gchar *plugin_licence(void)
{
  return "GPL3+";
}

const gchar *plugin_version(void)
{
  return VERSION;
}

struct PluginFeature *plugin_provides(void)
{
  static struct PluginFeature features[] =
    { {PLUGIN_UTILITY, N_("GData integration")},
      {PLUGIN_NOTHING, NULL}};
  return features;
}

--- NEW FILE: cm_gdata_contacts.h ---
/* GData plugin for Claws-Mail
 * Copyright (C) 2011 Holger Berndt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef CM_GDATA_CONTACTS_H
#define CM_GDATA_CONTACTS_H

#include <glib.h>

void cm_gdata_add_contacts(GList **address_list);
void cm_gdata_contacts_done(void);
gboolean cm_gdata_update_contacts_cache(void);
void cm_gdata_load_contacts_cache_from_file(void);

#endif /* CM_GDATA_CONTACTS_H */

--- placeholder.txt DELETED ---

--- NEW FILE: Makefile.am ---
plugindir = $(pkglibdir)/plugins

plugin_LTLIBRARIES = gdata_plugin.la

gdata_plugin_la_SOURCES = \
	gdata_plugin.c \
	gdata_plugin.h \
	cm_gdata_contacts.c \
	cm_gdata_contacts.h \
	cm_gdata_prefs.c \
	cm_gdata_prefs.h

gdata_plugin_la_LDFLAGS = \
	-avoid-version -module \
	$(GTK_LIBS) \
	$(GDATA_LIBS)


INCLUDES = \
	-I$(top_srcdir)/src \
	-I$(top_srcdir)/src/common \
	-I$(top_srcdir)/src/gtk

AM_CPPFLAGS = \
	-Wall \
	$(CLAWS_MAIL_CFLAGS) \
	$(GLIB_CFLAGS) \
	$(GTK_CFLAGS) \
	$(GDATA_CFLAGS) \
	-DLOCALEDIR=\""$(localedir)"\"

--- NEW FILE: cm_gdata_prefs.h ---
/* GData plugin for Claws-Mail
 * Copyright (C) 2011 Holger Berndt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef CM_GDATA_PREFS_H_
#define CM_GDATA_PREFS_H_

#include "prefs_gtk.h"

typedef struct {
  char *username;
  char *password;
  int max_num_results;
  int max_cache_age;
} CmGDataPrefs;

extern CmGDataPrefs cm_gdata_config;
extern PrefParam    cm_gdata_param[];

void cm_gdata_prefs_init(void);
void cm_gdata_prefs_done(void);

#endif /* CM_GDATA_PREFS_H_ */

--- NEW FILE: cm_gdata_prefs.c ---
/* GData plugin for Claws-Mail
 * Copyright (C) 2011 Holger Berndt
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */


#ifdef HAVE_CONFIG_H
#  include "config.h"
#  include "claws-features.h"
#endif

#include <glib.h>
#include <glib/gi18n.h>

#include "cm_gdata_prefs.h"
#include "gdata_plugin.h"
#include "cm_gdata_contacts.h"

#include "prefs_gtk.h"
#include "main.h"

#include <gtk/gtk.h>


typedef struct
{
  PrefsPage page;
  GtkWidget *entry_username;
  GtkWidget *entry_password;
  GtkWidget *spin_max_num_results;
  GtkWidget *spin_max_cache_age;
} CmGDataPage;

CmGDataPrefs cm_gdata_config;
CmGDataPage gdata_page;

PrefParam cm_gdata_param[] =
{
    {"username", NULL, &cm_gdata_config.username, P_STRING,
        &gdata_page.entry_username, prefs_set_data_from_entry, prefs_set_entry},

    {"password", NULL, &cm_gdata_config.password, P_PASSWORD,
        &gdata_page.entry_password, prefs_set_data_from_entry, prefs_set_entry},

    { "max_num_results", "1000", &cm_gdata_config.max_num_results, P_INT,
        &gdata_page.spin_max_num_results, prefs_set_data_from_spinbtn, prefs_set_spinbtn},

    { "max_cache_age", "300", &cm_gdata_config.max_cache_age, P_INT,
        &gdata_page.spin_max_cache_age, prefs_set_data_from_spinbtn, prefs_set_spinbtn},

    {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL }
};

static void gdata_create_prefs_page(PrefsPage *page, GtkWindow *window, gpointer data)
{
  GtkWidget *vbox;
  GtkWidget *frame;
  GtkWidget *spinner;
  GtkWidget *table;
  GtkWidget *label;
  GtkWidget *entry;

  vbox = gtk_vbox_new(FALSE, 0);

  /* auth frame */
  frame = gtk_frame_new(_("Authentication"));
  gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
  gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);

  /* username */
  table = gtk_table_new(2, 2, FALSE);
  label = gtk_label_new(_("Username:"));
  gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
  gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 4, 4);
  entry = gtk_entry_new();
  gtk_widget_set_size_request(entry, 250, -1);
  gtk_table_attach(GTK_TABLE(table), entry, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 4, 4);
  gdata_page.entry_username = entry;
  label = gtk_label_new(_("Password:"));
  gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
  gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 4, 4);
  entry = gtk_entry_new();
  gtk_widget_set_size_request(entry, 250, -1);
  gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
  gdata_page.entry_password = entry;
  gtk_table_attach(GTK_TABLE(table), entry, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 4, 4);
  gtk_container_add(GTK_CONTAINER(frame), table);

  table = gtk_table_new(2, 2, FALSE);
  gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
  label = gtk_label_new(_("Polling interval (seconds):"));
  gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 4, 4);
  gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
  spinner = gtk_spin_button_new_with_range(10, 10000, 10);
  gtk_table_attach(GTK_TABLE(table), spinner, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 4, 4);
  gdata_page.spin_max_cache_age = spinner;

  label = gtk_label_new(_("Maximum number of results:"));
  gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 4, 4);
  gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
  spinner = gtk_spin_button_new_with_range(0, G_MAXINT, 50);
  gtk_table_attach(GTK_TABLE(table), spinner, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 4, 4);
  gdata_page.spin_max_num_results = spinner;

  gtk_widget_show_all(vbox);
  page->widget = vbox;

  prefs_set_dialog(cm_gdata_param);
}

static void gdata_destroy_prefs_page(PrefsPage *page)
{
}

static void gdata_save_prefs(PrefsPage *page)
{
  int old_max_cache_age = cm_gdata_config.max_cache_age;

  if (!page->page_open)
    return;

  prefs_set_data_from_dialog(cm_gdata_param);

  cm_gdata_update_contacts_cache();
  if(old_max_cache_age != cm_gdata_config.max_cache_age)
    cm_gdata_update_contacts_update_timer();
}

void cm_gdata_prefs_init(void)
{
  static gchar *path[3];

  path[0] = _("Plugins");
  path[1] = _("GData");
  path[2] = NULL;

  gdata_page.page.path = path;
  gdata_page.page.create_widget = gdata_create_prefs_page;
  gdata_page.page.destroy_widget = gdata_destroy_prefs_page;
  gdata_page.page.save_page = gdata_save_prefs;
  prefs_gtk_register_page((PrefsPage*) &gdata_page);
}

void cm_gdata_prefs_done(void)
{
  if(!claws_is_exiting()) {
    prefs_gtk_unregister_page((PrefsPage*) &gdata_page);
  }
}



More information about the Commits mailing list