Lots of bug fixes and read-only support for LDAP. No searching implemented though
authorMichael Rasmussen <mir@datanom.net>
Mon, 14 Nov 2011 22:06:44 +0000 (22:06 +0000)
committerMichael Rasmussen <mir@datanom.net>
Mon, 14 Nov 2011 22:06:44 +0000 (22:06 +0000)
14 files changed:
configure.ac
plugins/example/example-plugin.c
plugins/ldap/Makefile.am
plugins/ldap/ldap-plugin.c
plugins/xml/xml-plugin.c
src/Makefile.am
src/callbacks.c
src/contactwindow.c
src/mainwindow.c
src/plugin-loader.c
src/plugin-loader.h
src/plugin.h
src/utils.c
src/utils.h

index 5c1ff39319be0fc5bd9fba01aeff2c21c2dbfe3f..b581a6651da3b6d463b6b56de5edf326221a8557 100644 (file)
@@ -57,9 +57,9 @@ fi
 AC_SUBST(PLUGINDIR)
 AC_SUBST(BINDIR)
 
-GLIB_REQUIRED=2.10.0
-GOBJECT_REQUIRED=2.10.0
-GTK_REQUIRED=2.12.0
+GLIB_REQUIRED=2.16.0
+GOBJECT_REQUIRED=2.16.0
+GTK_REQUIRED=2.16.0
 
 AC_SUBST(GLIB_REQUIRED)
 AC_SUBST(GOBJECT_REQUIRED)
@@ -89,7 +89,6 @@ PKG_CHECK_MODULES([LIBXML], libxml-2.0)
 AC_SUBST(LIBXML_CFLAGS)
 AC_SUBST(LIBXML_LIBS)
 
-
 # Checks for header files.
 AC_CHECK_HEADER(gcrypt.h,
                [AC_CHECK_LIB(gcrypt, gcry_control,,
@@ -99,10 +98,13 @@ AC_CHECK_HEADER(gcrypt.h,
                [AC_MSG_ERROR([libgcrypt not found.
                               Get latest version here:
                               http://www.gnupg.org/download])])
-if test x"$ac_cv_lib_gcry_control" = "xyes"; then
-    GCRYPT_LIBS="-lgcrypt"
-    AC_SUBST(GCRYPT_LIBS)
-fi
+AM_PATH_LIBGCRYPT(1.1.43,,
+    AC_MSG_ERROR([[
+***
+*** libgcrypt was not found. You may want to get it from
+*** ftp://ftp.gnupg.org/pub/gcrypt/alpha/libgcrypt/
+***
+]]))
 
 # Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
@@ -175,7 +177,44 @@ AC_ARG_ENABLE(ldap-plugin,
        [ac_cv_enable_ldap_plugin=$enableval], [ac_cv_enable_ldap_plugin=no])
 if test x"$ac_cv_enable_ldap_plugin" = xyes; then
        AC_MSG_RESULT(yes)
-       PLUGINS="ldap $PLUGINS"
+       AC_CHECK_LIB(resolv, res_query, LDAP_LIBS="$LDAP_LIBS -lresolv")
+       AC_CHECK_LIB(socket, bind, LDAP_LIBS="$LDAP_LIBS -lsocket")
+       AC_CHECK_LIB(nsl, gethostbyaddr, LDAP_LIBS="$LDAP_LIBS -lnsl")
+       AC_CHECK_LIB(lber, ber_get_tag, LDAP_LIBS="$LDAP_LIBS -llber",,
+                                $LDAP_LIBS)
+       
+       AC_CHECK_HEADERS(ldap.h lber.h,
+                                        [ ac_cv_enable_ldap=yes ],
+                                        [ ac_cv_enable_ldap=no ])
+       
+       if test "$ac_cv_enable_ldap" = yes; then
+                       AC_CHECK_LIB(ldap, ldap_open,
+                                                [ ac_cv_enable_ldap=yes ],
+                                                [ ac_cv_enable_ldap=no ],
+                                                $LDAP_LIBS)
+       
+                       AC_CHECK_LIB(ldap, ldap_start_tls_s,
+                                                [ ac_cv_have_tls=yes ],
+                                                [ ac_cv_have_tls=no ])
+       fi
+       
+       if test "$ac_cv_enable_ldap" = yes; then
+               LDAP_LIBS="$LDAP_LIBS -lldap"
+               PLUGINS="ldap $PLUGINS"
+       else
+               AC_MSG_ERROR([Open LDAP is required. See http://www.openldap.org/])
+       fi
+       
+       if test "$ac_cv_have_tls" = yes; then
+               AC_MSG_CHECKING([Whether GNUTLS is present])
+               PKG_CHECK_MODULES([GNUTLS], gnutls, 
+                                                 AC_DEFINE(LDAP_TLS, [],
+                                                 [Is GNUTLS present on the system]))
+               AC_SUBST(GNUTLS_LIBS)
+               AC_SUBST(GNUTLS_CFLAGS)
+       fi
+       
+       AC_SUBST(LDAP_LIBS)
 else
        AC_MSG_RESULT(no)
 fi
index f339997d9afd76a9a0d8af8368a9db84ced3745b..846caa2bc9d9d1b5107ad19943247887f81230f6 100644 (file)
@@ -42,6 +42,8 @@
 
 #define NAME "Example plugin"
 
+/* Reference to self */
+static Plugin* self = NULL;
 /* See plugin.h */
 static PluginFeature* feature = NULL;
 /* Description */
@@ -62,6 +64,16 @@ static GSList* inactive_attribs = NULL;
 /* List of not active or inactive attributes this plugin supports*/
 static GSList* remaining_attribs = NULL;
 
+/**
+ * Remove newly open book from closed_books
+ * @param book AddressBook to remove
+ */
+static void closed_books_remove(AddressBook* book) {
+       GList* found = g_list_find_custom(closed_books, book, address_book_compare);
+       if (found)
+               closed_books = g_list_remove_link(closed_books, found);
+}
+
 /**
  * Free list of abooks.
  * @param error Pointer to memory where error is supposed to be saved
@@ -235,17 +247,20 @@ gboolean plugin_update_contact(
  * @return TRUE if success FALSE otherwise
  */
 gboolean plugin_abook_open(AddressBook* abook, gchar** error) {
+       closed_books_remove(abook);
        if (abook->dirty) {
                /* Open Address book */
                if (*error) {
                        show_message(NULL, GTK_UTIL_MESSAGE_WARNING, "%s", *error);
                        g_free(*error);
                        *error = NULL;
+                       closed_books = g_list_prepend(closed_books, abook);
                        return FALSE;
                }
                abook->dirty = FALSE;
        }
        abooks = g_list_prepend(abooks, abook);
+       abook->open = TRUE;
 
        return TRUE;    
 }
@@ -259,11 +274,15 @@ gboolean plugin_abook_open(AddressBook* abook, gchar** error) {
 gboolean plugin_abook_delete(AddressBook* abook, gchar** error) {
        if (! abook)
                return FALSE;
-               
-       plugin_abook_close(abook, error);
+       
+       if (abook->open)        
+               self->abook_close(abook, error);
+       /* Remove from closed books since deleting */
+       closed_books = g_list_remove(closed_books, abook);
        if (abook->URL) {
                /* Delete address book */
        }
+       
        return TRUE;    
 }
 
@@ -278,6 +297,9 @@ gboolean plugin_abook_close(AddressBook* abook, gchar** error) {
        abooks = g_list_remove(abooks, abook);
        address_book_contacts_free(abook);
        debug_print("List contains %d elements after\n", g_list_length(abooks));
+       closed_books = g_list_prepend(closed_books, abook);
+       abook->open = FALSE;
+       
        return TRUE;
 }
 
@@ -361,7 +383,8 @@ GList* plugin_closed_books_get(void) {
  * @param Pointer to memory where error is supposed to be saved
  * @return FALSE if success TRUE otherwise
  */
-gboolean plugin_init(gchar** error) {
+gboolean plugin_init(gpointer self_ref, gchar** error) {
+       self = (Plugin *) self_ref;
        if (*error != NULL) {
                g_free(*error);
                *error = NULL;
@@ -376,6 +399,8 @@ gboolean plugin_init(gchar** error) {
 gboolean plugin_done(void) {
        g_free(feature);
        
+       self = NULL;
+
        return TRUE;
 }
 
@@ -425,8 +450,8 @@ const gchar* plugin_version(void) {
  * Get functional type of plugin. Returned memory is owned by the plugin.
  * @return type
  */
-const gchar* plugin_type(void) {
-       return "DEMO";
+PluginType plugin_type(void) {
+       return PLUGIN_TYPE_SIMPLE;
 }
 
 /**
@@ -436,3 +461,29 @@ const gchar* plugin_type(void) {
 const gchar* plugin_license(void) {
        return "GPL3+";
 }
+
+/**
+ * Does the plugin needs credentials for address books
+ * @return bool
+ */
+gboolean plugin_need_credentials(void) {
+       return FALSE;
+}
+
+/**
+ * Get list of additional config
+ * @return NULL if no additional config is required, a list of
+ * ExtraConfig otherwise
+ */
+GSList* plugin_extra_config(void) {
+       return NULL;
+}
+
+/**
+ * Get file filter for this plugin
+ * @return filter or NULL. If returning NULL means data storage is
+ * URL based URI based otherwise
+ */
+const gchar* plugin_file_filter(void) {
+       return "demo";
+}
\ No newline at end of file
index f58da0e4359af6a8d8ed27293a9f77c41fb7e5d8..35be37d286f4e77d6d98f119edc6aa24068a31bd 100644 (file)
@@ -11,18 +11,23 @@ ldap_plugin_la_LDFLAGS = \
 INCLUDES = \
           -I${top_srcdir} \
           -I${top_srcdir}/src \
-          -I$(top_srcdir)/src/dbus \
           -I${top_builddir} \
+          -I$(top_srcdir)/src/dbus \
           @GLIB_CFLAGS@ \
-          @GTK_CFLAGS@
+          @GTK_CFLAGS@ \
+          @GNUTLS_CFLAGS@
 
 AM_CPPFLAGS = \
-               -DG_LOG_DOMAIN=\"Claws-Contacts\"
+               -DG_LOG_DOMAIN=\"Claws-Contacts\" \
+               $(LIBGCRYPT_CFLAGS)
 
 ldap_plugin_la_SOURCES = \
                ldap-plugin.c
                        
 ldap_plugin_la_LIBADD= \
                   @GLIB_LIBS@ \
-                  @GTK_LIBS@
+                  @GTK_LIBS@ \
+                  @LDAP_LIBS@ \
+                  @GNUTLS_LIBS@ \
+                  $(LIBGCRYPT_LIBS)    
 
index 3085308afd43f1b489df77c1c9084911834234ca..a9b2c55bc0effff7708e1bc94a2e4301a3ec01b7 100644 (file)
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <glib/gprintf.h>
+#include <ldap.h>
+#include <stdlib.h>
 #include "plugin.h"
 #include "gtk-utils.h"
 #include "utils.h"
 
 #define NAME "LDAP plugin"
+#define TIMEOUT 20
 
+typedef struct {
+       gboolean        ldaps;
+       gchar*          host;
+       gint            port;
+} Connection;
+
+typedef struct {
+       LDAP*           ldap;
+       GSList*         baseDN;
+       gchar*          search_base;
+       gint            max_entries;
+       gint            timeout;
+} Server;
+
+typedef struct {
+       gchar*                  id;
+       const Contact*  contact;
+       gboolean                intersection;
+       Connection*             connection;
+       Server*                 server;
+} AbookConnection;
+
+/* Reference to self */
+static Plugin* self = NULL;
 /* See plugin.h */
 static PluginFeature* feature = NULL;
 /* Description */
@@ -62,10 +89,848 @@ static GSList* inactive_attribs = NULL;
 /* List of not active or inactive attributes this plugin supports*/
 static GSList* remaining_attribs = NULL;
 
+/* List of extra config */
+static GSList* extra_config = NULL;
+
 static gchar self_home[] = "ldap";
 static gchar configrc[] = "ldaprc";
 static ConfigFile* config = NULL;
 
+static GSList* abook_connection = NULL;
+
+static const char *other_attributes[] = {
+    "telephoneNumber",
+    "description", /* comments */
+    "postalAddress",
+    "homePhone",
+    "homePostalAddress",
+    "mobile",
+    "ou",
+    "title",
+    "telexNumber",
+    "facsimileTelephoneNumber",
+    "street",
+    "postOfficeBox",
+    "postalCode",
+    "st", /* state or province */
+    "l", /* locality Name */
+    "departmentNumber",
+    "initials",
+    "labeledURI",
+    "pager",
+    "roomNumber",
+    NULL
+};
+
+static void deconnect(Server* server) {
+    if (server->ldap) {
+        ldap_unbind_ext(server->ldap, NULL, NULL);
+        server->ldap = NULL;
+    }
+    if (server->baseDN) {
+        gslist_free(&server->baseDN, g_free);
+    }
+       if (server->search_base) {
+               g_free(server->search_base);
+               server->search_base = NULL;
+       }
+}
+
+static void connect_info_free(Connection** connection) {
+       g_free((*connection)->host);
+       g_free(*connection);
+       *connection = NULL;
+}
+
+static AbookConnection* abook_connection_new() {
+       AbookConnection* ac = g_new0(AbookConnection, 1);
+       ac->id = NULL;
+       ac->connection = NULL;
+       ac->server = g_new0(Server, 1);
+       ac->server->timeout = TIMEOUT;
+       
+       return ac;
+}
+
+static void abook_connection_free(gpointer abook_connection) {
+       AbookConnection* abook_con = (AbookConnection *) abook_connection;
+       if (! abook_con)
+               return;
+               
+       if (abook_con->id) {
+               g_free(abook_con->id);
+               abook_con->id = NULL;
+       }
+       if (abook_con->connection) {
+               connect_info_free(&abook_con->connection);
+       }
+       if (abook_con->server) {
+               deconnect(abook_con->server);
+               g_free(abook_con->server);
+               abook_con->server = NULL;
+       }
+       g_free(abook_con);
+       abook_con = NULL;
+}
+
+static AbookConnection* get_abook_connection(AddressBook* abook) {
+       GSList* cur;
+       gchar* id;
+       AbookConnection* ac = NULL;
+       gboolean found = FALSE;
+        
+       gchar* plain = g_strconcat(abook->abook_name, abook->URL, NULL);
+       id = sha256(plain);
+       g_free(plain);
+       
+       for (cur = abook_connection; cur && !found; cur = g_slist_next(cur)) {
+               ac = (AbookConnection *) cur->data;
+               if (strcmp(ac->id, id) == 0)
+                       found = TRUE;
+               else
+                       ac = NULL;
+       }
+       g_free(id);
+       
+       return ac;
+}
+
+static AddressBook* find_addressbook(AbookConnection* ac) {
+       GList* cur;
+       gchar* id = NULL;
+       AddressBook* abook = NULL;
+       
+       for (cur = abooks; cur; cur = g_list_next(cur)) {
+               abook = (AddressBook *) cur->data;
+               gchar* plain = g_strconcat(abook->abook_name, abook->URL, NULL);
+               id = sha256(plain);
+               g_free(plain);
+               if (strcmp(ac->id, id) == 0) {
+                       g_free(id);
+                       return abook;
+               }
+       }
+       
+       g_free(id);
+       
+       return abook;
+}
+
+static Connection* split_url(AddressBook* abook, gchar** error) {
+       Connection* info = g_new0(Connection, 1);
+       gchar** cur;
+       gchar* tmp;
+       
+       gchar** parts = g_strsplit(abook->URL, ":", 0);
+       
+       if (*parts) {
+               cur = parts;
+               while (*cur && ! *error) {
+                       if (strcmp("ldaps", *cur) == 0)
+                               info->ldaps = TRUE;
+                       else if (strcmp("ldap", *cur) == 0)
+                               info->ldaps = FALSE;
+                       else if (atoi(*cur) > 0)
+                               info->port = atoi(*cur);
+                       else {
+                               if (info->host)
+                                       *error = g_strconcat(abook->URL, ": ", _("Wrong URL"), NULL);
+                               else {
+                                       if (g_str_has_prefix(*cur, "//")) {
+                                               tmp = g_strdup(*cur+2);
+                                       }
+                                       else
+                                               tmp = g_strdup(*cur);
+                                       info->host = g_strdup(tmp);
+                                       g_free(tmp);
+                               }
+                       }
+                       cur += 1;
+               }
+               g_strfreev(parts);
+       }
+       else
+               *error = g_strconcat(abook->URL, ": ", _("Wrong URL"), NULL);
+       
+       if (info->ldaps) {
+               if (info->port < 1)
+                       info->port = 636;
+       }
+       else {
+               if (info->port < 1)
+                       info->port = 389;
+       }
+       
+       return info;
+}
+
+static int ldap_bind(AbookConnection* abook_con) {
+       struct berval cred;
+       AddressBook* abook = find_addressbook(abook_con);
+       Server* server = abook_con->server;
+
+       if (abook && abook->password) {
+            cred.bv_val = abook->password;
+            cred.bv_len = strlen(abook->password);
+        }
+        else {
+            cred.bv_val = "";
+            cred.bv_len = 0;
+       }
+
+       debug_print("binding: DN->%s\n", (abook->username) ? abook->username : "null");
+       return ldap_sasl_bind_s(server->ldap, abook->username,
+                       LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
+}
+
+static gchar* get_uri(Connection* info) {
+       gchar *tmp, *uri;
+       
+       if (info->ldaps)
+               tmp = g_strconcat("ldaps://", info->host, NULL);
+       else
+               tmp = g_strconcat("ldap://", info->host, NULL);
+       if (info->port > 0)
+               uri = g_strdup_printf("%s:%d", tmp, info->port);
+       else
+               uri = g_strdup(tmp);
+               
+       g_free(tmp);
+       
+       return uri;
+}
+
+static void set_default_values(Server** server) {
+       GSList *config, *cur;
+       ExtraConfig* conf;
+       
+       config = self->extra_config();
+       for (cur = config; cur; cur = g_slist_next(cur)) {
+               conf = (ExtraConfig *) cur->data;
+               if (conf->label && strcasecmp("timeout (sec)", conf->label) == 0)
+                       (*server)->timeout = conf->default_value.spin_btn;
+               else if (conf->label && strcasecmp("search base", conf->label) == 0)
+                       (*server)->search_base = g_strdup(conf->default_value.entry);
+               else if (conf->label && strcasecmp("max entries", conf->label) == 0)
+                       (*server)->max_entries = conf->default_value.spin_btn;
+       }
+       
+       gslist_free(&config, extra_config_free);
+}
+
+static Server* get_server_config(AddressBook* abook) {
+       GSList* cur;
+       ExtraConfig* conf;
+       Server* server = g_new0(Server, 1);
+       
+       set_default_values(&server);
+       
+       for (cur = abook->extra_config; cur; cur = g_slist_next(cur)) {
+               conf = (ExtraConfig *) cur->data;
+               if (conf->label && strcasecmp("timeout (sec)", conf->label) == 0)
+                       server->timeout = conf->value.spin_btn;
+               else if (conf->label && strcasecmp("search base", conf->label) == 0)
+                       server->search_base = g_strdup(conf->value.entry);
+               else if (conf->label && strcasecmp("max entries", conf->label) == 0)
+                       server->max_entries = conf->value.spin_btn;
+       }
+       
+       return server;
+}
+       
+#define SEARCH_BASE ""
+#define TEST_FILTER "(objectClass=*)"
+#define TEST_ATTRIB "namingContexts"
+static void find_search_base(AbookConnection* abook_con, int* err, gchar** error) {
+    gchar* attribs[2];
+    struct timeval timeOut;
+    LDAPMessage* result = NULL;
+    LDAPMessage* entry;
+    BerElement *ber;
+    gchar *attribute;
+    struct berval **vals;
+    gint rc, i;
+    Server* server = abook_con->server;
+    
+    if (server->timeout > 0)
+       timeOut.tv_sec = server->timeout;        
+       else
+       timeOut.tv_sec = TIMEOUT;        
+    timeOut.tv_usec = 0;
+    server->baseDN = NULL;    
+    attribs[0] = TEST_ATTRIB;
+    attribs[1] = NULL;
+    rc = ldap_search_ext_s(server->ldap, SEARCH_BASE, LDAP_SCOPE_BASE,
+        TEST_FILTER, attribs, 0, NULL, NULL, &timeOut, 0, &result);
+
+    if (rc == LDAP_SUCCESS) {
+        /* Process entries */
+        for (entry = ldap_first_entry(server->ldap, result);
+                entry; entry = ldap_next_entry(server->ldap, entry)) {
+            /* Process attributes */
+            for (attribute = ldap_first_attribute(server->ldap, entry, &ber);
+                   attribute; attribute = ldap_next_attribute(server->ldap, entry, ber)) {
+                if (strcasecmp(attribute, TEST_ATTRIB) == 0) {
+                    vals = ldap_get_values_len(server->ldap, entry, attribute);
+                    if (vals) {
+                        for (i = 0; vals[i]; i++) {
+                            server->baseDN = g_slist_prepend(
+                                server->baseDN, g_strndup(vals[i]->bv_val,
+                                    vals[i]->bv_len));
+                        }
+                    }
+                    ldap_value_free_len(vals);
+                }
+                ldap_memfree(attribute);
+            }
+            if (ber) {
+                ber_free(ber, 0);
+            }
+            ber = NULL;
+        }
+    }
+    else {
+               if (error)
+               *error = g_strdup(ldap_err2string(rc));
+        debug_print("%s\n", ldap_err2string(rc));
+        *err = rc;
+    }
+    if (result)
+        ldap_msgfree(result);
+}
+
+static gboolean ldap_connect(AbookConnection* abook_con, gchar** error) {
+    struct timeval timeOut;
+    int version = LDAP_VERSION3;
+    int reqcert = LDAP_OPT_X_TLS_ALLOW;
+    int rc;
+    gchar* uri;
+    gboolean TLS = FALSE;
+    AddressBook* abook = find_addressbook(abook_con);
+    Server* server = NULL;
+    Connection* info = NULL;
+
+    if (! abook || ! abook->URL || strlen(abook->URL) < 0) {
+               *error = g_strdup(_("Missing URL"));
+        return TRUE;
+       }
+
+       abook_con->server = server = get_server_config(abook);
+       abook_con->connection = info = split_url(abook, error);
+       if (*error)
+               return TRUE;
+
+    if (server->ldap) {
+        ldap_unbind_ext(server->ldap, NULL, NULL);
+        server->ldap = NULL;
+    }
+
+    timeOut.tv_usec = 0;
+    if (server->timeout > 0)
+       timeOut.tv_sec = server->timeout;        
+       else
+       timeOut.tv_sec = TIMEOUT;        
+
+    rc = ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, &version);
+    if (rc != LDAP_OPT_SUCCESS) {
+        ldap_unbind_ext(server->ldap, NULL, NULL);
+        server->ldap = NULL;
+        *error = g_strdup(ldap_err2string(rc));
+        debug_print("%s\n", ldap_err2string(rc));
+        return TRUE;
+    }
+    ldap_set_option( NULL, LDAP_OPT_NETWORK_TIMEOUT, &timeOut);
+
+#ifdef LDAP_TLS
+    if (info->ldaps) {
+        debug_print("LDAP_OPT_X_TLS_ALLOW\n");
+        rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &reqcert);
+        if (rc != LDAP_OPT_SUCCESS) {
+            ldap_unbind_ext(server->ldap, NULL, NULL);
+            server->ldap = NULL;
+            *error = g_strdup(ldap_err2string(rc));
+            debug_print("%s\n", ldap_err2string(rc));
+            return TRUE;
+        }
+    }
+#else
+       if (info->ldaps) {
+               debug_print("LDAPS requested but SSL is unavailable. Trying LDAP\n");
+       }
+#endif
+
+       uri = get_uri(info);
+    rc = ldap_initialize(&server->ldap, uri);
+    g_free(uri);
+    if (rc != LDAP_SUCCESS) {
+        ldap_unbind_ext(server->ldap, NULL, NULL);
+        server->ldap = NULL;
+        *error = g_strdup(
+                _("initialize: LDAP session initialization failed."));
+        debug_print("initialize: LDAP session initialization failed\n");
+        return TRUE;
+    }
+
+#ifdef LDAP_TLS
+    if (! info->ldaps) {
+        rc = ldap_start_tls_s(server->ldap, NULL, NULL);
+        if (rc != LDAP_SUCCESS) {
+            debug_print("start_tls: %s\n", ldap_err2string(rc));
+        }
+        else
+               TLS = TRUE;
+    }
+#endif
+
+    /* anonymous? */
+    if (abook->username && strlen(abook->username) > 0) {
+        rc = ldap_bind(abook_con);
+        if (rc != LDAP_SUCCESS) {
+            ldap_unbind_ext(server->ldap, NULL, NULL);
+            server->ldap = NULL;
+            *error = g_strdup(ldap_err2string(rc));
+            debug_print("bind: %s\n", ldap_err2string(rc));
+            return TRUE;
+        }
+    }
+
+    debug_print("Connected to %s on port %d\n", info->host, info->port);
+    if (info->ldaps || TLS)
+        debug_print("Connected using %s\n", (info->ldaps) ? "SSL" : "TLS");
+
+       if (! server->search_base) {
+               find_search_base(abook_con, &rc, error);        
+               if (rc == LDAP_SUCCESS)
+                       server->search_base = g_strdup((gchar *) server->baseDN->data);
+               else {
+                       ldap_unbind_ext(server->ldap, NULL, NULL);
+                       server->ldap = NULL;
+                       *error = g_strdup_printf(ldap_err2string(rc));
+                       debug_print("Search base: %s\n", ldap_err2string(rc));
+                       return TRUE;
+               }
+       }
+    
+    return FALSE;
+}
+
+static void abooks_free() {
+       GList* cur;
+       
+       if (! abooks && ! closed_books)
+               return;
+               
+       for (cur = abooks; cur; cur = g_list_next(cur)) {
+               AddressBook* abook = (AddressBook *) cur->data;
+               address_book_free(&abook);
+       }
+       g_list_free(abooks);
+       abooks = NULL;
+
+       for (cur = closed_books; cur; cur = g_list_next(cur)) {
+               AddressBook* abook = (AddressBook *) cur->data;
+               address_book_free(&abook);
+       }
+       g_list_free(closed_books);
+       closed_books = NULL;
+}
+
+static void write_config_file() {
+       GList *cur;
+       gchar* error = NULL;
+       gchar* path = NULL;
+       
+       if (config) {
+               path = g_strdup(config->path);
+               plugin_config_free(&config);
+       }
+       else {
+               gchar* basedir = get_self_home();
+               path = g_strconcat(basedir, G_DIR_SEPARATOR_S, self_home,
+                                       G_DIR_SEPARATOR_S, configrc, NULL);
+               g_free(basedir);
+       }
+       config = plugin_config_new(path);
+       g_free(path);
+       config->comment = g_strconcat("Configuration file for ", NAME,
+               ".\nDo not change unless you KNOW what your are doing.", NULL);
+       config->configured_books->group = g_strdup("configured address books");
+
+       for (cur = abooks; cur; cur = g_list_next(cur)) {
+               AddressBook* book = (AddressBook *) cur->data;
+               config->configured_books->books =
+                       g_slist_prepend(config->configured_books->books, g_strdup(book->abook_name));
+       }
+
+       config->closed_books->group = g_strdup("closed address books");
+
+       for (cur = closed_books; cur; cur = g_list_next(cur)) {
+               AddressBook* book = (AddressBook *) cur->data;
+               config->closed_books->books =
+                       g_slist_prepend(config->closed_books->books, g_strdup(book->abook_name));
+       }
+       
+       plugin_config_set(config, &error);
+       if (error) {
+               show_message(NULL, GTK_UTIL_MESSAGE_ERROR, "%s", error);
+               g_free(error);
+       }
+}
+
+static const gchar* native2ldap(const gchar* attr) {
+       if (strcasecmp(attr, "email") == 0)
+               return "mail";
+       else if (strcasecmp(attr, "first-name") == 0)
+               return "givenName";
+       else if (strcasecmp(attr, "last-name") == 0)
+               return "sn";
+       else if (strcasecmp(attr, "nick-name") == 0)
+               return "displayName";
+       else if (strcasecmp(attr, "image") == 0)
+               return "jpegPhoto";
+       else
+               return attr;
+}
+
+static const gchar* ldap2native(const gchar* attr) {
+       if (strcasecmp(attr, "mail") == 0)
+               return "email";
+       else if (strcasecmp(attr, "givenName") == 0)
+               return "first-name";
+       else if (strcasecmp(attr, "sn") == 0)
+               return "last-name";
+       else if (strcasecmp(attr, "displayName") == 0)
+               return "nick-name";
+       else if (strcasecmp(attr, "jpegPhoto") == 0)
+               return "image";
+       else
+               return attr;
+}
+
+static gchar** attriblist2ldap() {
+       GHashTableIter iter;
+       gpointer key, value;
+       GSList *attr_list = NULL;
+       gchar** res;
+
+       g_hash_table_iter_init(&iter, attribs);
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               const gchar* attr  = native2ldap((gchar *) key);
+               attr_list = g_slist_prepend(attr_list, attr);
+       }
+       
+       res = gslist_to_array(attr_list, NULL);
+       gslist_free(&attr_list, NULL);
+       
+       return res;
+}
+
+static void set_attribs() {
+       const gchar** ptr = standard_attribs;
+       AttribDef* attrib_def;
+       
+       if (attribs) {
+               hash_table_free(&attribs);
+       }
+       attribs = hash_table_new();
+       while(ptr && *ptr) {
+               attrib_def = g_new0(AttribDef, 1);
+               attrib_def->type = ATTRIB_TYPE_STRING;
+               attrib_def->attrib_name = g_strdup(*ptr);
+               g_hash_table_replace(attribs, g_strdup(*ptr++), attrib_def);
+       }
+
+       ptr = other_attributes;
+       while(ptr && *ptr) {
+               attrib_def = g_new0(AttribDef, 1);
+               attrib_def->type = ATTRIB_TYPE_STRING;
+               attrib_def->attrib_name = g_strdup(*ptr);
+               g_hash_table_replace(attribs, g_strdup(*ptr++), attrib_def);
+       }       
+}
+
+static AddressBook* get_addressbook(const gchar* name) {
+       gchar **keys, **cur;
+       GSList* values = NULL;
+       gsize len;
+       GError* err = NULL;
+       gchar* value;
+       AddressBook* abook;
+       ExtraConfig* conf;
+       
+       abook = g_new0(AddressBook, 1);
+       if (g_key_file_has_group(config->key_file, name)) {
+               abook->abook_name = g_strdup(name);
+               abook->dirty = TRUE;
+               keys = g_key_file_get_keys(config->key_file, name, &len, &err);
+               for (cur = keys; *cur; cur += 1) {
+                       gslist_free(&values, g_free);
+                       if (config_get_value(config, name, *cur, &values)) {
+                               value = (gchar *) values->data;
+                               if (strcmp("url", *cur) == 0)
+                                       abook->URL = g_strdup(value);
+                               else if (strcmp("username", *cur) == 0)
+                                       abook->username = g_strdup(value);
+                               else if (strcmp("password", *cur) == 0)
+                                       abook->password = aes256_decrypt(value, TRUE);
+                               else {
+                                       conf = get_extra_config(self->extra_config(), *cur);
+                                       if (conf) {
+                                               switch (conf->type) {
+                                                       case PLUGIN_CONFIG_EXTRA_CHECKBOX:
+                                                               conf->value.check_btn =
+                                                                       (strcmp("true", value) == 0) ? TRUE : FALSE;
+                                                               break;
+                                                       case PLUGIN_CONFIG_EXTRA_ENTRY:
+                                                               conf->value.entry = g_strdup(value);
+                                                               break;
+                                                       case PLUGIN_CONFIG_EXTRA_SPINBUTTON:
+                                                               conf->value.spin_btn = atoi(value);
+                                                               break;
+                                               }
+                                               abook->extra_config =
+                                                       g_slist_prepend(abook->extra_config, conf);
+                                       }
+                               }
+                       }
+               }
+               if (values)
+                       gslist_free(&values, g_free);
+                g_strfreev(keys);
+       }
+       
+       return abook;
+}
+
+/**
+ * Remove addressbook from list
+ * @param list List to change
+ * @param book AddressBook to remove
+ * @return TRUE if item was removed
+ */
+static gboolean g_list_book_remove(GList** list, AddressBook* book) {
+       if (! list || !*list || ! book)
+               return FALSE;
+               
+       GList* found = g_list_find_custom(*list, book, address_book_compare);
+       if (found)
+               *list = g_list_remove_link(*list, found);
+       
+       return (found != NULL);
+}
+
+static gchar* filter_token_add(const gchar* token, const gchar* search) {
+    return g_strconcat("(", token, "=", search, ")", NULL);
+}
+
+#define FILTER "(objectClass=*)"
+//#define FILTER "(sn=Rasmussen)"
+static gchar* filter_build(Contact* contact, gboolean intersection) {
+       GString* filter;
+       //gchar* token;
+       
+       if (contact) {
+               //if (i > 0 && !(i % 2)) {
+               //    if (intersection)
+               //      filter = g_string_prepend(filter, "(&");
+               //    else
+               //      filter = g_string_prepend(filter, "(|");
+               //    filter = g_string_append(filter, token);
+               //    filter = g_string_append(filter, ")");
+               //}
+               //else
+               //      filter = g_string_append(filter, token);
+               //i++;
+       }
+       else {
+               filter = g_string_new(FILTER);
+       }
+       
+       return g_string_free(filter, FALSE);
+}
+
+static gchar* filter_build_standard() {
+       gchar** list = standard_attribs;
+       gchar* token;
+       GString* filter = g_string_new("");
+       gboolean multi = FALSE;
+
+       while(*list) {
+               token = filter_token_add(native2ldap(*list++), "*");
+               if (multi) {
+               filter = g_string_prepend(filter, "(|");
+                   filter = g_string_append(filter, token);
+                   filter = g_string_append(filter, ")");
+               }
+               else
+                       filter = g_string_append(filter, token);
+               multi = TRUE;
+               g_free(token);
+       }
+       
+       return g_string_free(filter, FALSE);
+}
+
+static GList* fetch_data(Server* server, LDAPMessage* res) {
+    LDAPMessage*    entry;
+    BerElement*     ber;
+    Contact*           contact;
+    Email*                     email;
+    gchar*          attribute;
+    struct berval** vals;
+    GList*                     result = NULL;
+    gchar*          value;
+    int                                i, count = 0;
+
+    /* Process entries */
+    for (entry = ldap_first_entry(server->ldap, res); entry;
+            entry = ldap_next_entry(server->ldap, entry), count++) {
+        debug_print("------------------------\n");
+        contact = contact_new();
+        value = ldap_get_dn(server->ldap, entry);
+        debug_print("Found: DN->%s\n", value);
+        g_free(value);
+        /* Process attributes */
+        for (attribute = ldap_first_attribute(server->ldap, entry, &ber);
+            attribute; attribute = ldap_next_attribute(server->ldap, entry, ber)) {
+            
+            vals = ldap_get_values_len(server->ldap, entry, attribute);
+            if (vals) {
+                /* We only handle one value per attribute except for mail */
+                if (strcasecmp(attribute, "mail") == 0) {
+                    for (i = 0; vals[i]; i++) {
+                                               email = g_new0(Email, 1);
+                        value = g_strndup(vals[i]->bv_val, vals[i]->bv_len);
+                                               email->email = g_strdup(value);
+                                               debug_print("Found (email): %s->%s\n", attribute, value);
+                        g_free(value);
+                        contact->emails = g_slist_prepend(contact->emails, email);
+                    }
+                }
+                else {
+                    value = g_strndup(vals[0]->bv_val, vals[0]->bv_len);
+                    debug_print("Found (other): %s->%s\n", attribute, value);
+                    const gchar* key = ldap2native(attribute);
+                                       AttribDef* attr = pack_data(ATTRIB_TYPE_STRING, key, value);
+                    g_hash_table_insert(contact->data, g_strdup(key), attr);
+                    g_free(value);
+                }
+            }
+            ldap_memfree(attribute);
+            ldap_value_free_len(vals);
+        }
+        if (ber) {
+            ber_free(ber, 0);
+        }
+        ber = NULL;
+        //contact_dump(contact, stderr);
+        result = g_list_prepend(result, contact);
+    }
+    if (count)
+       debug_print("------------------------\n");
+    debug_print("Found: %d contacts\n", count);
+       
+    return result;
+}
+
+static gboolean fetch_all(AddressBook* book,
+                                                 AbookConnection* ac,
+                                                 gchar** error) {
+       GSList* cur;
+       Server* server = ac->server;
+       gchar** ldap_attributes;
+    int rc = 0;
+    struct timeval timeOut;
+    LDAPMessage* res = NULL;
+    gchar* search_filter;
+       GString* filter;
+       gint max_size;
+       
+    if (server->timeout > 0)
+       timeOut.tv_sec = server->timeout;        
+       else
+       timeOut.tv_sec = TIMEOUT;
+       timeOut.tv_usec = 0;
+       max_size = server->max_entries;
+       ldap_attributes = attriblist2ldap();
+       search_filter = filter_build(ac->contact, ac->intersection);
+       filter = g_string_new("(&");
+       filter = g_string_append(filter, search_filter);
+       g_free(search_filter);
+       search_filter = filter_build_standard();
+       filter = g_string_append(filter, search_filter);
+       filter = g_string_append(filter, ")");
+       g_free(search_filter);
+       search_filter = g_string_free(filter, FALSE);
+       //debug_set_mode(TRUE);
+       debug_print("Filter: %s\n", search_filter);
+       //debug_set_mode(FALSE);
+       if (server->search_base) {
+        rc = ldap_search_ext_s(server->ldap, server->search_base,
+               LDAP_SCOPE_SUBTREE, search_filter, ldap_attributes,
+               0, NULL, NULL, &timeOut, max_size, &res);
+       }
+       else {
+           for (cur = server->baseDN; cur; cur = g_slist_next(cur)) {
+               rc = ldap_search_ext_s(server->ldap, (gchar *) cur->data,
+                       LDAP_SCOPE_SUBTREE, search_filter, ldap_attributes,
+                       0, NULL, NULL, &timeOut, max_size, &res);
+               if (rc == LDAP_SUCCESS) {
+                   GList* list = fetch_data(server, res);
+                   if (list) {
+                       book->contacts = g_list_concat(book->contacts, list);
+                       list = NULL;
+                               }
+               }
+               if (res)
+                   ldap_msgfree(res);
+           }
+       }
+       g_free(search_filter);
+       book->contacts = fetch_data(server, res);
+       //debug_set_mode(FALSE);
+       if (res)
+               ldap_msgfree(res);
+
+    g_strfreev(ldap_attributes);
+
+    if (rc) {
+               if (error)
+               *error = g_strdup(ldap_err2string(rc));
+               debug_print("bind: %s\n", ldap_err2string(rc));
+               return TRUE;
+    }
+    
+    return FALSE;
+}
+
+static void set_extra_config() {
+       ExtraConfig* ec;
+       
+       ec = g_new0(ExtraConfig, 1);
+       ec->type = PLUGIN_CONFIG_EXTRA_ENTRY;
+       ec->label = g_strdup(_("Search base"));
+       ec->tooltip = g_strdup(_("This specifies the name of the directory to be searched "
+                                        "on the server. Examples include:\ndc=claws-mail,dc=org\n"
+                                        "  ou=people,dc=domainname,dc=com\n"
+                                        "  o=Organization Name,c=Country."));
+    extra_config = g_slist_append(extra_config, ec);
+
+       ec = g_new0(ExtraConfig, 1);
+       ec->type = PLUGIN_CONFIG_EXTRA_SPINBUTTON;
+       ec->label = g_strdup(_("Timeout (Sec)"));
+       ec->tooltip = g_strdup(_("Timeout in seconds. 0 means wait infinite."));
+       ec->default_value.spin_btn = 20;
+    extra_config = g_slist_append(extra_config, ec);
+
+       ec = g_new0(ExtraConfig, 1);
+       ec->type = PLUGIN_CONFIG_EXTRA_SPINBUTTON;
+       ec->label = g_strdup(_("Max entries"));
+       ec->tooltip = g_strdup(_("Maximum number of contacts to receive. 0 means all."));
+       ec->default_value.spin_btn = 30;
+    extra_config = g_slist_append(extra_config, ec);
+}
+
 /**
  * Free list of abooks.
  * @param error Pointer to memory where error is supposed to be saved
@@ -100,6 +965,7 @@ GSList* plugin_attrib_list(void) {
  * @return GSList* list of AttributeDef
  */
 GSList* plugin_remaining_attribs(void) {
+       remaining_attribs = g_slist_append(remaining_attribs, NULL);
        return gslist_deep_copy(remaining_attribs, attrib_def_copy);
 }
 
@@ -118,8 +984,6 @@ GSList* plugin_inactive_attribs(void) {
  * naming supported attributes.
  */
 void plugin_attribs_set(GHashTable* attributes) {
-       hash_table_free(&attribs);
-       attribs = hash_table_copy(attributes);
 }
 
 /**
@@ -239,17 +1103,45 @@ gboolean plugin_update_contact(
  * @return TRUE if success FALSE otherwise
  */
 gboolean plugin_abook_open(AddressBook* abook, gchar** error) {
+       gboolean removed;
+       AbookConnection* ac = get_abook_connection(abook);
+       
+       if (! ac) {
+               ac = abook_connection_new();
+               gchar* plain = g_strconcat(abook->abook_name, abook->URL, NULL);
+               gchar* id = sha256(plain);
+               g_free(plain);
+               ac->id = g_strdup(id);
+               g_free(id);
+               abook_connection = g_slist_prepend(abook_connection, ac);
+       }
+
+       removed = g_list_book_remove(&closed_books, abook);
+       abooks = g_list_prepend(abooks, abook);
        if (abook->dirty) {
                /* Open Address book */
+               ldap_connect(ac, error);
                if (*error) {
                        show_message(NULL, GTK_UTIL_MESSAGE_WARNING, "%s", *error);
                        g_free(*error);
                        *error = NULL;
+                       if (removed)
+                               closed_books = g_list_prepend(closed_books, abook);
+                       g_list_book_remove(&abooks, abook);
+                       return FALSE;
+               }
+               if (fetch_all(abook, ac, error)) {
+                       show_message(NULL, GTK_UTIL_MESSAGE_WARNING, "%s", *error);
+                       g_free(*error);
+                       *error = NULL;
+                       if (removed)
+                               closed_books = g_list_prepend(closed_books, abook);
+                       g_list_book_remove(&abooks, abook);
                        return FALSE;
                }
                abook->dirty = FALSE;
        }
-       abooks = g_list_prepend(abooks, abook);
+       abook->open = TRUE;
 
        return TRUE;    
 }
@@ -263,11 +1155,21 @@ gboolean plugin_abook_open(AddressBook* abook, gchar** error) {
 gboolean plugin_abook_delete(AddressBook* abook, gchar** error) {
        if (! abook)
                return FALSE;
-               
-       plugin_abook_close(abook, error);
-       if (abook->URL) {
-               /* Delete address book */
+       
+       AbookConnection* ac = get_abook_connection(abook);
+       if (! ac) {
+               if (error)
+                       *error = g_strdup_printf(_("%s: Not found"), abook->abook_name);
+               return FALSE;
        }
+
+       if (abook->open)        
+               self->abook_close(abook, error);
+       /* Remove from closed books since deleting */
+       closed_books = g_list_remove(closed_books, abook);
+       abook_connection = g_slist_remove(abook_connection, ac);
+       abook_connection_free(ac);
+       ac = NULL;
        return TRUE;    
 }
 
@@ -278,10 +1180,21 @@ gboolean plugin_abook_delete(AddressBook* abook, gchar** error) {
  * @return TRUE if success FALSE otherwise
  */
 gboolean plugin_abook_close(AddressBook* abook, gchar** error) {
+       AbookConnection* ac = get_abook_connection(abook);
+       if (! ac) {
+               if (error)
+                       *error = g_strdup_printf(_("%s: Not found"), abook->abook_name);
+               return FALSE;
+       }
+
+       deconnect(ac->server);
        debug_print("List contains %d elements before\n", g_list_length(abooks));
        abooks = g_list_remove(abooks, abook);
        address_book_contacts_free(abook);
        debug_print("List contains %d elements after\n", g_list_length(abooks));
+       closed_books = g_list_prepend(closed_books, abook);
+       abook->open = FALSE;
+
        return TRUE;
 }
 
@@ -312,13 +1225,74 @@ GSList* plugin_addrbook_all_get() {
 gboolean plugin_abook_set_config(AddressBook* old,
                                                                 AddressBook* new,
                                                                 gchar** error) {
+       AddressBook* abook;
+       AbookConnection* ac;
+       GSList* cur;
+       
+       if (! config) {
+               if (error) {
+                       *error = g_strdup(_("Missing config file"));
+               }
+               return TRUE;
+       }
+       
+       ac = abook_connection_new();
        if (new) {
                /* Update config for existing address book */
+               self->abook_delete(old, error);
+               if (*error)
+                       return TRUE;
+               abook = new;
        }
        else {
                /* Write config for new address book */
+               abook = old;
        }
 
+       if (! abook->abook_name || strlen(abook->abook_name) < 1) {
+               if (error) {
+                       *error = g_strdup(_("Missing name for address book"));
+               }
+               abook_connection_free(ac);
+               ac = NULL;
+               return FALSE;
+       }
+       
+       g_key_file_set_string(config->key_file, abook->abook_name, "url", abook->URL);
+       g_key_file_set_string(config->key_file, abook->abook_name, "username", abook->username);
+       g_key_file_set_string(config->key_file, abook->abook_name,
+                       "password", aes256_encrypt(abook->password, TRUE));
+       
+       if (abook->extra_config) {
+               for (cur = abook->extra_config; cur; cur = g_slist_next(cur)) {
+                       ExtraConfig* conf = (ExtraConfig *) cur->data;
+                       switch (conf->type) {
+                               case PLUGIN_CONFIG_EXTRA_CHECKBOX:
+                                       g_key_file_set_boolean(config->key_file,
+                                               abook->abook_name, conf->label, conf->value.check_btn);
+                                       break;
+                               case PLUGIN_CONFIG_EXTRA_ENTRY:
+                                       g_key_file_set_string(config->key_file,
+                                               abook->abook_name, conf->label, conf->value.entry);
+                                       break;
+                               case PLUGIN_CONFIG_EXTRA_SPINBUTTON:
+                                       g_key_file_set_integer(config->key_file,
+                                               abook->abook_name, conf->label, conf->value.spin_btn);
+                                       break;
+                       }
+               }
+       }
+               
+       abook->dirty = TRUE;
+
+       gchar* plain = g_strconcat(abook->abook_name, abook->URL, NULL);
+       gchar* id = sha256(plain);
+       g_free(plain);
+       if (ac->id)
+               g_free(ac->id);
+       ac->id = g_strdup(id);
+       g_free(id);
+       
        return (*error) ? TRUE : FALSE;
 }
 
@@ -355,6 +1329,8 @@ GList* plugin_closed_books_get(void) {
        GList *cur, *list = NULL;
        
        for (cur = closed_books; cur; cur = g_list_next(cur)) {
+               /* No deep copy since list of contacts will always be NULL */
+               list = g_list_prepend(list, address_book_copy(cur->data, FALSE));
        }
        
        return list;
@@ -365,12 +1341,13 @@ GList* plugin_closed_books_get(void) {
  * @param Pointer to memory where error is supposed to be saved
  * @return FALSE if success TRUE otherwise
  */
-gboolean plugin_init(gchar** error) {
+gboolean plugin_init(gpointer self_ref, gchar** error) {
        gchar *basedir, *path;
-       GSList *list = NULL, *cur;
+       GSList *cur;
        ConfiguredBooks* cf_books;
        ClosedBooks* cl_books;
        
+       self = (Plugin *) self_ref;
        if (*error != NULL) {
                g_free(*error);
                *error = NULL;
@@ -383,7 +1360,9 @@ gboolean plugin_init(gchar** error) {
                basedir, G_DIR_SEPARATOR_S, self_home,
                G_DIR_SEPARATOR_S, configrc, NULL);
        
+       set_extra_config();
        config = plugin_config_new(path);
+       g_free(path);
        config_get(config, error);
        
        if (*error) {
@@ -391,51 +1370,34 @@ gboolean plugin_init(gchar** error) {
        }
        else {
                if (config) {
+                       set_attribs();
+                       if (debug_get_mode()) {
+                               hash_table_dump(attribs, stderr);
+                       }
+
                        cf_books = config->configured_books;
                        cl_books = config->closed_books;
                        
                        if (cf_books) {
-                               g_free(path);
-                               path = g_strconcat(basedir, G_DIR_SEPARATOR_S,
-                                       self_home, G_DIR_SEPARATOR_S, NULL);
                                for (cur = cf_books->books; cur; cur = g_slist_next(cur)) {
                                        gchar* book = (gchar *) cur->data;
-                                       if (! cl_books) {
-                                               AddressBook* abook = address_book_new();
-                                               abook->URL = g_strconcat(path, book, ".xml", NULL);
-                                               plugin_abook_open(abook, error);
-                                       }
+                                       AddressBook* abook = get_addressbook(book);
+                                       gboolean ok = self->abook_open(abook, error);
+                                       if (! ok)
+                                               closed_books = g_list_prepend(closed_books, abook);
                                }
                        }
-                       config_get_value(config, "supported attributes", "attributes", &list);                          
-                       attribs = hash_table_new();
-                       for (cur = list; cur; cur = g_slist_next(cur)) {
-                               gchar* key = (gchar *) cur->data;
-                               debug_print("Adding '%s' to attributes\n", key);
-                               AttribDef* attr = g_new0(AttribDef, 1);
-                               attr->attrib_name = g_strdup(key);
-                               attr->type = ATTRIB_TYPE_STRING;
-                               g_hash_table_replace(attribs, g_strdup(key), attr);
-                       }
-                       gslist_free(&list, g_free);
-
-                       config_get_value(config, "deactivated attributes", "attributes", &list);                                
-                       for (cur = list; cur; cur = g_slist_next(cur)) {
-                               gchar* key = (gchar *) cur->data;
-                               debug_print("Adding '%s' to deactivated attributes\n", key);
-                               AttribDef* attr = g_new0(AttribDef, 1);
-                               attr->attrib_name = g_strdup(key);
-                               attr->type = ATTRIB_TYPE_STRING;
-                               inactive_attribs = g_slist_prepend(inactive_attribs, attr);
+                       if (cl_books) {
+                               for (cur = cl_books->books; cur; cur = g_slist_next(cur)) {
+                                       gchar* book = (gchar *) cur->data;
+                                       AddressBook* abook = get_addressbook(book);
+                                       closed_books = g_list_prepend(closed_books, abook);
+                               }
                        }
-                       gslist_free(&list, g_free);
-
-                       //hash_table_dump(attribs, stderr);
                }
        }
        g_free(basedir);
-       g_free(path);
-
+       
        return FALSE;
 }
 
@@ -444,7 +1406,16 @@ gboolean plugin_init(gchar** error) {
  * @return TRUE if success FALSE otherwise
  */
 gboolean plugin_done(void) {
+       write_config_file();
+       abooks_free();
+       plugin_config_free(&config);
        g_free(feature);
+       hash_table_free(&attribs);
+       gslist_free(&remaining_attribs, NULL);
+       gslist_free(&abook_connection, abook_connection_free);
+       gslist_free(&extra_config, extra_config_free);
+
+       self = NULL;
        
        return TRUE;
 }
@@ -480,7 +1451,15 @@ const gchar* plugin_name(void) {
  * @return description
  */
 const gchar* plugin_desc(void) {
-       return _("This plugin provides LDAP support.");
+       return _("This plugin provides LDAP support.\n"
+                        "The plugin only supports LDAPv3 and the format\n"
+                        "for a URL is as follows:\n\n"
+                        "[(ldap|ldaps)://](FQDN|IP)[:port]\n\n"
+                        "If neither ldap nor ldaps is specified ldap is assumed.\n"
+                        "If port is not specified 389 is assumed for ldap\n"
+                        "and 636 is assumed for ldaps.\n"
+                        "If ldap is used for schema then TLS will be tried\n"
+                        "automatically before using plain text connection.");
 }
 
 /**
@@ -495,8 +1474,17 @@ const gchar* plugin_version(void) {
  * Get functional type of plugin. Returned memory is owned by the plugin.
  * @return type
  */
-const gchar* plugin_type(void) {
-       return "LDAP";
+PluginType plugin_type(void) {
+       return PLUGIN_TYPE_ADVANCED;
+}
+
+/**
+ * Get file filter for this plugin
+ * @return filter or NULL. If returning NULL means data storage is
+ * URL based URI based otherwise
+ */
+const gchar* plugin_file_filter(void) {
+       return NULL;
 }
 
 /**
@@ -507,3 +1495,19 @@ const gchar* plugin_license(void) {
        return "GPL3+";
 }
 
+/**
+ * Does the plugin needs credentials for address books
+ * @return bool
+ */
+gboolean plugin_need_credentials(void) {
+       return TRUE;
+}
+
+/**
+ * Get list of additional config
+ * @return NULL if no additional config is required, a list of
+ * ExtraConfig otherwise
+ */
+GSList* plugin_extra_config(void) {
+       return g_slist_reverse(gslist_deep_copy(extra_config, extra_config_copy));
+}
index e8d92ba21ad650c99d7c6d2200b7c9752b70f8b9..77ff69a68b3fae9280592de68f92cafd98b59a7b 100644 (file)
@@ -59,6 +59,7 @@ typedef struct {
        gint            equal;
 } Compare;
 
+static Plugin* self = NULL;
 static PluginFeature* feature = NULL;
 static const gchar subtype[] = "Claws-mail native addressbook";
 static GHashTable* attribs = NULL;
@@ -446,7 +447,7 @@ static void write_config_file(GSList** books, GSList** closed) {
        }
 
        cur = NULL;
-       cur_attribs = plugin_attrib_list();
+       cur_attribs = self->attrib_list();
        for (tmp = cur_attribs; tmp; tmp = g_slist_next(tmp)) {
                AttribDef* attrdef = (AttribDef *) tmp->data;
                cur = g_slist_prepend(cur, g_strdup(attrdef->attrib_name));
@@ -456,7 +457,7 @@ static void write_config_file(GSList** books, GSList** closed) {
        gslist_free(&cur, g_free);
 
        cur = NULL;
-       cur_attribs = plugin_inactive_attribs();
+       cur_attribs = self->inactive_attribs();
        for (tmp = cur_attribs; tmp; tmp = g_slist_next(tmp)) {
                AttribDef* attrdef = (AttribDef *) tmp->data;
                cur = g_slist_prepend(cur, g_strdup(attrdef->attrib_name));
@@ -804,12 +805,13 @@ static void closed_books_remove(AddressBook* book) {
                closed_books = g_list_remove_link(closed_books, found);
 }
 
-gboolean plugin_init(gchar** error) {
+gboolean plugin_init(gpointer self_ref, gchar** error) {
        gchar *basedir, *path;
        GSList *list = NULL, *cur, *found;
        ConfiguredBooks* cf_books;
        ClosedBooks* cl_books;
        
+       self = (Plugin *) self_ref;
        if (*error != NULL) {
                g_free(*error);
                *error = NULL;
@@ -824,7 +826,7 @@ gboolean plugin_init(gchar** error) {
                if (*error || ! attribs) {
                        return TRUE;
                }
-               plugin_abook_open(NULL, error);
+               self->abook_open(NULL, error);
        }
        else {
                basedir = get_self_home();
@@ -854,7 +856,9 @@ gboolean plugin_init(gchar** error) {
                                                if (! cl_books || ! found) {
                                                        AddressBook* abook = address_book_new();
                                                        abook->URL = g_strconcat(path, book, ".xml", NULL);
-                                                       plugin_abook_open(abook, error);
+                                                       gboolean ok = self->abook_open(abook, error);
+                                                       if (! ok)
+                                                               closed_books = g_list_prepend(closed_books, abook);
                                                }
                                        }
                                }
@@ -929,6 +933,8 @@ gboolean plugin_done(void) {
        old_abook_free_dir();
        gslist_free(&inactive_attribs, attrib_def_free);
        
+       self = NULL;
+
        return TRUE;
 }
 
@@ -964,7 +970,11 @@ const gchar* plugin_version(void) {
        return VERSION;
 }
 
-const gchar* plugin_type(void) {
+PluginType plugin_type(void) {
+       return PLUGIN_TYPE_SIMPLE;
+}
+
+const gchar* plugin_file_filter(void) {
        return "xml";
 }
 
@@ -1148,6 +1158,7 @@ gboolean plugin_abook_open(AddressBook* abook, gchar** error) {
                                show_message(NULL, GTK_UTIL_MESSAGE_WARNING, "%s", *error);
                                g_free(*error);
                                *error = NULL;
+                               closed_books = g_list_prepend(closed_books, abook);
                                return FALSE;
                        }
                        abook_set_next_uid(abook);
@@ -1155,6 +1166,7 @@ gboolean plugin_abook_open(AddressBook* abook, gchar** error) {
                }
                abooks = g_list_prepend(abooks, abook);
        }               
+       abook->open = TRUE;
 
        return TRUE;    
 }
@@ -1162,14 +1174,18 @@ gboolean plugin_abook_open(AddressBook* abook, gchar** error) {
 gboolean plugin_abook_delete(AddressBook* abook, gchar** error) {
        if (! abook)
                return FALSE;
-               
-       plugin_abook_close(abook, error);
+       
+       if (abook->open)
+               self->abook_close(abook, error);
+       /* Remove from closed books since deleting */
+       closed_books = g_list_remove(closed_books, abook);
        if (abook->URL) {
                if (g_unlink(abook->URL) < 0) {
                        *error = g_new0(gchar, 1024);
                        strerror_r(errno, *error, 1024);
                }
        }
+       
        return TRUE;    
 }
 
@@ -1179,6 +1195,8 @@ gboolean plugin_abook_close(AddressBook* abook, gchar** error) {
        address_book_contacts_free(abook);
        debug_print("List contains %d elements after\n", g_list_length(abooks));
        closed_books = g_list_prepend(closed_books, abook);
+       abook->open = FALSE;
+       
        return TRUE;
 }
 
@@ -1244,4 +1262,12 @@ GList* plugin_closed_books_get(void) {
        }
        
        return list;
-}
\ No newline at end of file
+}
+
+gboolean plugin_need_credentials(void) {
+       return FALSE;
+}
+
+GSList* plugin_extra_config(void) {
+       return NULL;
+}
index 5bc52a2bb7c7563b328f0d767084a9146f64feba..aa7a75436b28632c0efd5f0411cb12c3e96a67d1 100644 (file)
@@ -16,7 +16,8 @@ INCLUDES = \
 
 AM_CPPFLAGS = \
                -DG_LOG_DOMAIN=\"Claws-Contacts\" \
-               -DPLUGINDIR=\"@PLUGINDIR@\"
+               -DPLUGINDIR=\"@PLUGINDIR@\" \
+               $(LIBGCRYPT_CFLAGS)
 
 claws_contacts_SOURCES = \
                    claws-contacts.c \
@@ -48,7 +49,7 @@ claws_contactsinclude_HEADERS = \
 claws_contacts_LDADD= \
                   @GLIB_LIBS@ \
                   @GTK_LIBS@ \
-                  @GCRYPT_LIBS@ \
+                  $(LIBGCRYPT_LIBS) \
                   @LIBXML_LIBS@ \
                   ${top_builddir}/xmllib/libcontactxml.la \
                   ${top_builddir}/src/dbus/libdbus.la
index b1ec4c8e3581fcc74cb80ae5b4550e05d2ee8de8..7bc6649eb22ec4d949563ba7855eaafda5845bab 100644 (file)
@@ -185,7 +185,7 @@ static void select_addr_book_cb(GtkButton *button, gpointer data) {
        GtkFileFilter* filter;
        gchar* home;
        
-       file_filter = cur.plugin->filter();
+       file_filter = cur.plugin->file_filter();
        if (file_filter) {
                gchar* f = g_strconcat("*.", file_filter, NULL);
                filter = gtk_file_filter_new();
@@ -204,8 +204,10 @@ static void select_addr_book_cb(GtkButton *button, gpointer data) {
                gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
        gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
        home = cur.plugin->default_url(NULL);
-       gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), home);
-       g_free(home);
+       if (home) {
+               gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), home);
+               g_free(home);
+       }
        
        if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
                file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
@@ -669,7 +671,7 @@ void abook_edit_cb(GtkWidget* widget, gpointer data) {
        }
        else {
                show_message(win->window, GTK_UTIL_MESSAGE_INFO,
-               _("Please highlight desired address book for deletion"));
+               _("Please highlight desired address book to edit"));
        }
 }
 
@@ -851,7 +853,8 @@ void abook_new_cb(GtkWidget* widget, gpointer data) {
                plugin = plugin_get_plugin(plugin_name);
                if (plugin) {
                        if (address_book_edit(win->window, plugin, &book)) {
-                               if (g_file_test(book->URL, G_FILE_TEST_EXISTS)) {
+                               if (plugin->file_filter() && strcmp("xml", plugin->file_filter()) == 0 &&
+                                               g_file_test(book->URL, G_FILE_TEST_EXISTS)) {
                                        AddressBook* abook =
                                                        address_book_get(plugin, book->abook_name);
                                        if (abook) {
@@ -874,7 +877,8 @@ void abook_new_cb(GtkWidget* widget, gpointer data) {
                                                }
                                        }
                                }
-                               if (! addr_book_set_config(book, &error)) {
+                               if (! plugin->abook_set_config(book, NULL, &error)) {
+                               /*if (! addr_book_set_config(book, &error)) {*/
                                        if (plugin->abook_open(book, &error)) 
                                                update_abook_list(win);
                                }
@@ -990,19 +994,188 @@ void abook_list_cursor_changed_cb(GtkTreeView* tree_view, gpointer data) {
        }
 }
 
+static void add_advanced_page(GtkNotebook* notebook,
+                                                         Plugin* plugin,
+                                                         AddressBook* address_book) {
+       GSList *cur, *extra;
+       GtkWidget *widget, *hbox, *vbox, *label;
+       gboolean new;
+       
+       if (address_book->extra_config) {
+               extra = address_book->extra_config;
+               new = FALSE;
+       }
+       else {
+               extra = plugin->extra_config();
+               new = TRUE;
+       }
+
+       if (extra) {
+               vbox = gtk_vbox_new(FALSE, 0);
+               for (cur = extra; cur; cur = g_slist_next(cur)) {
+                       ExtraConfig* ec = (ExtraConfig *) cur->data;
+                       hbox = gtk_hbox_new(FALSE, 5);
+                       switch (ec->type) {
+                               case PLUGIN_CONFIG_EXTRA_CHECKBOX:
+                                       widget = gtk_check_button_new();
+                                       if (ec->label)
+                                               gtk_button_set_label(GTK_BUTTON(widget), ec->label);
+                                       if (ec->tooltip)
+                                               gtk_widget_set_tooltip_text(widget, ec->tooltip);
+                                       if (new)
+                                               gtk_toggle_button_set_active(
+                                                       GTK_TOGGLE_BUTTON(widget), ec->default_value.check_btn);
+                                       else
+                                               gtk_toggle_button_set_active(
+                                                       GTK_TOGGLE_BUTTON(widget), ec->value.check_btn);
+                                       label = gtk_label_new("");
+                                       break;
+                               case PLUGIN_CONFIG_EXTRA_ENTRY:
+                                       widget = gtk_entry_new();
+                                       if (ec->label)
+                                               label = gtk_label_new(ec->label);
+                                       else
+                                               label = gtk_label_new("");
+                                       if (ec->tooltip)
+                                               gtk_widget_set_tooltip_text(widget, ec->tooltip);
+                                       if (ec->value.entry)
+                                               gtk_entry_set_text(GTK_ENTRY(widget), ec->value.entry);
+                                       else if (ec->default_value.entry)
+                                               gtk_entry_set_text(GTK_ENTRY(widget), ec->default_value.entry);
+                                       break;
+                               case PLUGIN_CONFIG_EXTRA_SPINBUTTON:
+                                       widget = gtk_spin_button_new_with_range(0.0, 100.0, 1.0);
+                                       if (ec->label)
+                                               label = gtk_label_new(ec->label);
+                                       else
+                                               label = gtk_label_new("");
+                                       if (ec->tooltip)
+                                               gtk_widget_set_tooltip_text(widget, ec->tooltip);
+                                       if (new)
+                                               gtk_spin_button_set_value(
+                                                       GTK_SPIN_BUTTON(widget), ec->default_value.spin_btn);
+                                       else            
+                                               gtk_spin_button_set_value(
+                                                       GTK_SPIN_BUTTON(widget), ec->value.spin_btn);           
+                                       break;
+                       }
+                       gtk_widget_set_name(widget, ec->label);
+                       gtk_widget_set_size_request(label, 100, -1);
+                       gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+                       gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+                       if (ec->type == PLUGIN_CONFIG_EXTRA_SPINBUTTON)
+                               gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 2);
+                       else
+                               gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 2);
+                       gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
+               }
+               label = gtk_label_new(_("Advanced settings"));
+               gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label);
+               if (! address_book->extra_config)
+                       gslist_free(&extra, extra_config_free);
+       }
+}
+
+static void set_advanced_config(Plugin* plugin,
+                                                               AddressBook* address_book,
+                                                               GtkNotebook* notebook) {
+       GtkWidget *page, *widget;
+       GSList *config, *cur, *widgets = NULL, *ptr;
+       gboolean found;
+       
+       config = plugin->extra_config();
+       page = gtk_notebook_get_nth_page(notebook, 1);
+       
+       if (config && page) {
+               for (cur = config; cur; cur = g_slist_next(cur)) {
+                       ExtraConfig* ec = (ExtraConfig *) cur->data;
+                       gslist_free(&widgets, NULL);
+                       widgets = find_name(GTK_CONTAINER(notebook), ec->label);
+                       if (! widgets)
+                               continue;
+                       
+                       switch (ec->type) {
+                               case PLUGIN_CONFIG_EXTRA_CHECKBOX:
+                                       found = FALSE;
+                                       for (ptr = widgets; ptr && !found; ptr = g_slist_next(ptr)) {
+                                               widget = (GtkWidget *) widgets->data;
+                                               if (debug_get_mode()) {
+                                                       gchar* text = gtk_widget_get_tooltip_text(widget);
+                                                       debug_print("%s\n", (text) ? text: "(NULL)");
+                                                       g_free(text);
+                                               }
+                                               if (GTK_IS_CHECK_BUTTON(widget))
+                                                       found = TRUE;
+                                               else
+                                                       widget = NULL;
+                                       }
+                                       if (widget) {
+                                               ec->value.check_btn = 
+                                                       gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+                                       }
+                                       break;
+                               case PLUGIN_CONFIG_EXTRA_ENTRY:
+                                       found = FALSE;
+                                       for (ptr = widgets; ptr && !found; ptr = g_slist_next(ptr)) {
+                                               widget = (GtkWidget *) widgets->data;
+                                               if (debug_get_mode()) {
+                                                       gchar* text = gtk_widget_get_tooltip_text(widget);
+                                                       debug_print("%s\n", (text) ? text: "(NULL)");
+                                                       g_free(text);
+                                               }
+                                               if (GTK_IS_ENTRY(widget))
+                                                       found = TRUE;
+                                               else
+                                                       widget = NULL;
+                                       }
+                                       if (widget) {
+                                               g_free(ec->value.entry);
+                                               ec->value.entry = 
+                                                       gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
+                                       }
+                                       break;
+                               case PLUGIN_CONFIG_EXTRA_SPINBUTTON:
+                                       found = FALSE;
+                                       for (ptr = widgets; ptr && !found; ptr = g_slist_next(ptr)) {
+                                               widget = (GtkWidget *) widgets->data;
+                                               if (debug_get_mode()) {
+                                                       gchar* text = gtk_widget_get_tooltip_text(widget);
+                                                       debug_print("%s\n", (text) ? text: "(NULL)");
+                                                       g_free(text);
+                                               }
+                                               if (GTK_IS_SPIN_BUTTON(widget))
+                                                       found = TRUE;
+                                               else
+                                                       widget = NULL;
+                                       }
+                                       if (widget) {
+                                               ec->value.spin_btn = 
+                                                       gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+                                       }
+                                       break;
+                       }
+               }
+               address_book->extra_config = config;
+       }
+       else    
+               gslist_free(&config, extra_config_free);
+}
+
 #define GTK_ENTRIES 4
-gboolean address_book_edit(
-               GtkWidget* parent, Plugin* plugin, AddressBook** address_book) {
+gboolean address_book_edit(GtkWidget* parent,
+                                                  Plugin* plugin,
+                                                  AddressBook** address_book) {
        GtkWidget* dialog;
        GtkWidget* file_btn;
        AddressBook* book;
        GtkWidget* input[GTK_ENTRIES] = {NULL, NULL, NULL, NULL};
-       GtkWidget *hbox, *vbox, *label = NULL, *frame;
+       GtkWidget *hbox, *vbox, *label = NULL, *frame, *notebook;
        int i;
        gboolean response = FALSE;
        gboolean use_button = FALSE;
        struct DataContainer data;
        gboolean show_url = TRUE;
+       gchar* title = NULL;
        
        for (i = 0; i < GTK_ENTRIES; i++) {
                input[i] = gtk_entry_new();
@@ -1019,19 +1192,23 @@ gboolean address_book_edit(
                if (book->password)
                        gtk_entry_set_text(GTK_ENTRY(input[3]), book->password);
                gtk_entry_set_visibility(GTK_ENTRY(input[3]), FALSE);
+               title = g_strdup(_("Edit address book"));
        }
        else {
                book = *address_book = address_book_new();
-               show_url = FALSE;
+               title = g_strdup(_("New address book"));
+               /*if (! plugin->file_filter())
+                       show_url = FALSE;*/
        }
        
        dialog = gtk_dialog_new_with_buttons(
-                _("Edit address book")
+                title
                 GTK_WINDOW(parent),
                 GTK_DIALOG_DESTROY_WITH_PARENT,
                 GTK_STOCK_OK, GTK_RESPONSE_OK,
                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                 NULL);
+    g_free(title);
     gtk_widget_set_size_request(dialog, 360, -1);
        gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
        
@@ -1048,16 +1225,19 @@ gboolean address_book_edit(
                                if (show_url) { 
                                        label = gtk_label_new(_("URL"));
                                        gtk_widget_set_tooltip_text(
-                                               input[i], _("URL or path to this address book"));
-                                       file_btn = gtk_button_new_from_stock(GTK_STOCK_OPEN);
-                                       gtk_widget_set_tooltip_text(
-                                               file_btn, _("Open file dialog"));
-                                       data.plugin = plugin;
-                                       data.addressbook = address_book;
-                                       data.widget = input[i];
-                                       g_signal_connect(file_btn, "clicked",
-                                               G_CALLBACK(select_addr_book_cb), &data);
-                                       use_button = TRUE;
+                                               input[i], _("URL or path to this address book\n"
+                                                                       "Must conform to plugin requirements."));
+                                       if (plugin->file_filter()) {
+                                               file_btn = gtk_button_new_from_stock(GTK_STOCK_OPEN);
+                                               gtk_widget_set_tooltip_text(
+                                                       file_btn, _("Open file dialog"));
+                                               data.plugin = plugin;
+                                               data.addressbook = address_book;
+                                               data.widget = input[i];
+                                               g_signal_connect(file_btn, "clicked",
+                                                       G_CALLBACK(select_addr_book_cb), &data);
+                                               use_button = TRUE;
+                                       }
                                }
                                else {
                                        gtk_widget_destroy(input[i]);
@@ -1068,15 +1248,20 @@ gboolean address_book_edit(
                                label = gtk_label_new(_("Username"));
                                gtk_widget_set_tooltip_text(
                                        input[i], _("And optional username"));
+                               if (!plugin->need_credentials())
+                                       gtk_widget_set_sensitive(input[i], FALSE);
                                break;
                        case 3:
                                label = gtk_label_new(_("Password"));
                                gtk_widget_set_tooltip_text(
                                        input[i], _("And optional password"));
+                               gtk_entry_set_visibility(GTK_ENTRY(input[i]), FALSE);
+                               if (!plugin->need_credentials())
+                                       gtk_widget_set_sensitive(input[i], FALSE);
                                break;
                }
                if (input[i]) {
-                       gtk_widget_set_size_request(label, 60, -1);
+                       gtk_widget_set_size_request(label, 100, -1);
                        gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
                        gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
                        gtk_box_pack_start(GTK_BOX(hbox), input[i], TRUE, TRUE, 2);
@@ -1087,10 +1272,20 @@ gboolean address_book_edit(
                        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
                }
        }
-       frame = gtk_frame_new(_("Address book settings"));
-       gtk_container_add(GTK_CONTAINER(frame), vbox);
-       gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), frame);
-       gtk_widget_show_all(frame);
+       if (plugin->extra_config()) {
+               notebook = gtk_notebook_new();
+               label = gtk_label_new(_("Basic settings"));
+               gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label);
+               add_advanced_page(GTK_NOTEBOOK(notebook), plugin, book);
+               gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), notebook);
+               gtk_widget_show_all(notebook);
+       }
+       else {
+               frame = gtk_frame_new(_("Address book settings"));
+               gtk_container_add(GTK_CONTAINER(frame), vbox);
+               gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), frame);
+               gtk_widget_show_all(frame);
+       }
        dialog_set_focus(GTK_DIALOG(dialog), GTK_STOCK_CANCEL);
        
        gint result = gtk_dialog_run(GTK_DIALOG(dialog));
@@ -1119,6 +1314,10 @@ gboolean address_book_edit(
                        book->password = 
                                gtk_editable_get_chars(GTK_EDITABLE(input[3]), 0, -1);
                        response = TRUE;
+                       if (plugin->extra_config()) {
+                               //gslist_free(&book->extra_config, extra_config_free);
+                               set_advanced_config(plugin, book, GTK_NOTEBOOK(notebook));
+                       }
                        break;
                case GTK_RESPONSE_CANCEL:
                        break;
index 499e39678468b9a55ee78a3e93274be90790240e..1da9a436747d6e02c0135d9931870878a22f2d06 100644 (file)
@@ -895,6 +895,7 @@ static void contact_widget(ContactWindow* cw) {
        g_signal_connect(GTK_TREE_MODEL(list), "row-deleted",
                        G_CALLBACK(row_deleted_cb), cw);
        cw->email_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list));
+    g_object_unref(list);
     gtk_widget_set_tooltip_text(GTK_WIDGET(cw->email_list),
         _("Double-click, enter, or space on cell will activate edit mode\n"
           "Mouse-Left-click to drag and drop for reordering email list\n"
index a23651032f594b14462fc685e475d581030b303a..06f5365dfc2c86b41944fbb38e3cfe440642da9b 100644 (file)
@@ -360,6 +360,7 @@ static GtkWidget* create_view(MainWindow* mainwindow) {
 
     mainwindow->abook_list = 
         gtk_tree_view_new_with_model(GTK_TREE_MODEL(list));
+    g_object_unref(list);
     gtk_widget_set_name(mainwindow->abook_list, "abook_list");
     gtk_widget_set_tooltip_text(mainwindow->abook_list,
         _("Address book in BOLD is the default address book"));
@@ -412,6 +413,7 @@ static GtkWidget* create_view(MainWindow* mainwindow) {
 
     mainwindow->contact_list = 
         gtk_tree_view_new_with_model(GTK_TREE_MODEL(list));
+    g_object_unref(list);
     gtk_widget_set_tooltip_text(GTK_WIDGET(mainwindow->contact_list),
         _("Double-click, enter, or space on cell will activate edit mode"));
     gtk_tree_view_set_rules_hint(
index a3e4746318d9f6c5b746693f3fa779c54fd949c5..fc56494605553f076d61ef8264e9fa413290d592 100644 (file)
@@ -402,22 +402,7 @@ static void plugin_free(Plugin* plugin) {
        g_free(plugin);
 }
 
-static gchar* format_hash(const guchar* md_string) {
-    int len = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
-    int i;
-    gchar* hex = g_new0(gchar, 2 * len + 1);
-
-       for (i = 0; i < len; i++)
-        sprintf(hex + 2 * i, "%02x", md_string[i]);
-       
-       return hex;
-}
-
-#define key "claws-mail address book"
 static void compute_hash(Plugin* plugin) {
-    gcry_error_t err = 0;
-    gcry_md_hd_t digest = NULL;
-    guchar* md_string = NULL;
     gchar* cipher;
 
        gchar* plain = g_strconcat(
@@ -425,30 +410,12 @@ static void compute_hash(Plugin* plugin) {
                        plugin->desc(), " ",
                        plugin->version(), NULL);
 
-    err = gcry_md_open(
-                &digest, GCRY_MD_SHA256,
-                GCRY_MD_FLAG_SECURE | GCRY_MD_FLAG_HMAC);
-    if (err) {
-        g_printerr("%s\n", gcry_strerror(err));
-        goto done;
-    }
-    err = gcry_md_setkey(digest, key, strlen(key));
-    if (err) {
-        g_printerr("%s\n", gcry_strerror(err));
-        goto done;
-    }
-
-    gcry_md_write(digest, plain, strlen(plain));
-
-    md_string = gcry_md_read(digest, 0);
-    cipher = format_hash(md_string);
+       cipher = sha256(plain);
+    g_free(plain);
     debug_print("Computed hash: %s\n", cipher);
+
     plugin->id = g_strdup(cipher);
        g_free(cipher);
-       
-    done:
-    gcry_md_close(digest);
-    g_free(plain);
 }
 
 static Plugin* plugin_allready_loaded(Plugin* plugin, gchar** error) {
@@ -479,7 +446,7 @@ static Plugin* plugin_allready_loaded(Plugin* plugin, gchar** error) {
                if (found) {
                        g_free(found->error);
                        found->error = NULL;
-                       if (found->init(error)) {
+                       if (found->init(found, error)) {
                                if (*error)
                                        found->error = g_strdup(*error);
                                else
@@ -637,9 +604,9 @@ Plugin* plugin_load(const gchar* filename, gchar** error) {
                         get_contact, set_contact, delete_contact, search_contact, update_contact,
                         plugin_abook_open, plugin_abook_close, plugin_abook_delete,
                         plugin_addrbook_all_get, plugin_abook_set_config, plugin_type,
-                        plugin_url, plugin_attribs_set, plugin_commit_all,
+                        plugin_file_filter, plugin_url, plugin_attribs_set, plugin_commit_all,
                         plugin_remaining_attribs, plugin_inactive_attribs,
-                        plugin_closed_books_get;
+                        plugin_closed_books_get, plugin_need_credentials, plugin_extra_config;
 
        plugin = g_new0(Plugin, 1);
        if (plugin == NULL) {
@@ -659,6 +626,7 @@ Plugin* plugin_load(const gchar* filename, gchar** error) {
                !g_module_symbol(plugin->module, "plugin_desc", &plugin_desc) ||
                !g_module_symbol(plugin->module, "plugin_default_url", &plugin_url) ||
                !g_module_symbol(plugin->module, "plugin_type", &plugin_type) ||
+               !g_module_symbol(plugin->module, "plugin_file_filter", &plugin_file_filter) ||
                !g_module_symbol(plugin->module, "plugin_version", &plugin_version) ||
                !g_module_symbol(plugin->module, "plugin_provides", &plugin_provides) ||
                !g_module_symbol(plugin->module, "plugin_license", &plugin_license) ||
@@ -672,6 +640,8 @@ Plugin* plugin_load(const gchar* filename, gchar** error) {
                !g_module_symbol(plugin->module, "plugin_abook_set_config", &plugin_abook_set_config) ||
                !g_module_symbol(plugin->module, "plugin_commit_all", &plugin_commit_all) ||
                !g_module_symbol(plugin->module, "plugin_init", &plugin_init) ||
+               !g_module_symbol(plugin->module, "plugin_need_credentials", &plugin_need_credentials) ||
+               !g_module_symbol(plugin->module, "plugin_extra_config", &plugin_extra_config) ||
                !g_module_symbol(plugin->module, "plugin_closed_books_get", &plugin_closed_books_get) ||
                !g_module_symbol(plugin->module, "plugin_remaining_attribs", &plugin_remaining_attribs) ||
                !g_module_symbol(plugin->module, "plugin_inactive_attribs", &plugin_inactive_attribs)) {
@@ -728,7 +698,7 @@ Plugin* plugin_load(const gchar* filename, gchar** error) {
        }
        
        plugin->init = plugin_init;
-       plugin->filter = plugin_type;
+       plugin->file_filter = plugin_file_filter;
        plugin->reset = plugin_reset;
        plugin->name = plugin_name;
        plugin->desc = plugin_desc;
@@ -736,6 +706,8 @@ Plugin* plugin_load(const gchar* filename, gchar** error) {
        plugin->type = plugin_type;
        plugin->license = plugin_license;
        plugin->filename = g_strdup(filename);
+       plugin->need_credentials = plugin_need_credentials;
+       plugin->extra_config = plugin_extra_config;
        plugin->error = NULL;
        plugin->abook_open = plugin_abook_open;
        plugin->abook_close = plugin_abook_close;
@@ -773,7 +745,7 @@ Plugin* plugin_load(const gchar* filename, gchar** error) {
                }
        }
        else {
-               if (plugin->init(error)) {
+               if (plugin->init(plugin, error)) {
                        if (*error)
                                plugin->error = g_strdup(*error);
                        else
index 5e898351ca1969117f7f7d6c0f64009071be70a7..1d562770168f6d3984fe0b8b70dbd5732c63594c 100644 (file)
@@ -46,14 +46,16 @@ typedef struct {
        gchar* id;
        //gchar* addrbook_name;
        GModule *module;
-       gboolean (*init) (gchar** error);
+       gboolean (*init) (gpointer self, gchar** error);
        void (*reset) (gchar** error);
        const gchar* (*name) (void);
        const gchar* (*desc) (void);
        const gchar* (*version) (void);
-       const gchar* (*type) (void);
+       PluginType (*type) (void);
        const gchar* (*license) (void);
-       const gchar* (*filter) (void);
+       const gchar* (*file_filter) (void);
+       gboolean (*need_credentials) (void);
+       GSList* (*extra_config) (void);
        gchar* (*default_url) (const gchar* name);
        GSList* (*remaining_attribs) (void);
        GSList* (*inactive_attribs) (void);
index 60bca40f22f0cc27c7c02f857bae38ee89c9c702..b40bd2ca40a0f15431b521f8cb8f69f2f45ac636 100644 (file)
@@ -48,6 +48,50 @@ enum {
 #define IS_READ_WRITE(X) ((X & PLUGIN_READ_WRITE) == PLUGIN_READ_WRITE)
 #define HAS_ADV_SEARCH(X) ((X & PLUGIN_ADVANCED_SEARCH) == PLUGIN_ADVANCED_SEARCH)
 
+typedef enum {
+       PLUGIN_CONFIG_EXTRA_ENTRY,
+       PLUGIN_CONFIG_EXTRA_CHECKBOX,
+       PLUGIN_CONFIG_EXTRA_SPINBUTTON
+} ExtraConfigType;
+
+typedef struct {
+       ExtraConfigType type;
+       gchar*                  label;
+       gchar*                  tooltip;
+       union {
+               gchar*          entry;
+               gboolean        check_btn;
+               gint            spin_btn;
+       } value;
+       union {
+               gchar*          entry;
+               gboolean        check_btn;
+               gint            spin_btn;
+       } default_value;
+} ExtraConfig;
+
+typedef enum {
+       /*
+        * Address books configured with this plugin only requires
+        * the following input to function
+        * Name: Address book name (Name must be uniq within this plugin)
+        * (URI|URL): Reference to storage (URI => File in local file system,
+        * URL => TCP based reference). If 'plugin_file_filter' returns NULL
+        * then this plugin uses URL type storage reference.
+        * 
+        * Optional
+        * - Username
+        * - Password
+        */
+       PLUGIN_TYPE_SIMPLE,
+       /*
+        * Address books configured with this plugin requires additional
+        * input to function in which case the function 'plugin_extra_config'
+        * MUST return a GSList of ExtraConfig
+        */
+       PLUGIN_TYPE_ADVANCED
+} PluginType;
+
 typedef struct {
        guint                   support; /* One or more of the plugin features or'ed */
        const gchar*    subtype;
@@ -92,6 +136,8 @@ typedef struct {
        GList*          contacts; /* contacts is expected to be a list of Contact */
        gboolean        dirty;
        gulong          next_uid;
+       gboolean        open;
+       GSList*         extra_config; /* List of ExtraConfig */
 } AddressBook;
 
 typedef struct {
@@ -122,16 +168,20 @@ extern const gchar *standard_attribs[];
 
 
 /* Functions which must be implemented by any plugin */
-gboolean plugin_init(gchar** error);
+gboolean plugin_init(gpointer self, gchar** error);
 void plugin_reset(gchar** error);
 gboolean plugin_done(void);
 const PluginFeature* plugin_provides(void);
 const gchar* plugin_name(void);
 const gchar* plugin_desc(void);
 const gchar* plugin_version(void);
-const gchar* plugin_type(void);
+PluginType plugin_type(void);
+const gchar* plugin_file_filter(void);
 const gchar* plugin_license(void);
+gboolean plugin_need_credentials(void);
 gchar* plugin_default_url(const gchar* name);
+GSList* plugin_extra_config(void);
+
 /* 
  * Returning NULL means list of supported attributes are infinite
  * Returning an empty list means that no more supported attributes
index f74ea78e71e5cf786050763a2ca84eb8e2a9c6a8..8fb30730ba5b2ede4754083f0731f8625aa7699a 100644 (file)
@@ -44,6 +44,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <gcrypt.h>
 
 #include "utils.h"
 #include "gtk-utils.h"
@@ -66,6 +67,11 @@ typedef struct {
 } CompareSListData;
 */
 
+typedef struct {
+       size_t          size;
+       guchar*         block;
+} NormBlock;
+
 #define ERRBUFSIZ 255
 
 /* Forward declarations */
@@ -352,6 +358,9 @@ gboolean first_time(const gchar* subpath, const gchar* config, gchar** error) {
 void attrib_def_free(gpointer attrdef) {
        AttribDef* a = (AttribDef *) attrdef;
        
+       if (! a)
+               return;
+               
        if (a->attrib_name) {
                g_free(a->attrib_name);
                a->attrib_name = NULL;
@@ -415,24 +424,28 @@ void plugin_config_set(ConfigFile* config, gchar** error) {
        cl_books = config->closed_books;
        
        if (cf_books && cf_books->group) {
-               if (cf_books->books)
+               if (cf_books->books) {
                        list = gslist_to_array(cf_books->books, &num);
+                       g_key_file_set_string_list(config->key_file, cf_books->group,
+                               "books", (const gchar* const *) list, num);
+                       g_strfreev(list);
+               }
                else {
-                       list = g_new0(gchar *, 1);
+                       g_key_file_set_string(config->key_file, cf_books->group,
+                               "books", "");
                }
-               g_key_file_set_string_list(config->key_file, cf_books->group,
-                               "books", (const gchar* const *) list, num);
-               g_strfreev(list);
        }
        if (cl_books && cl_books->group) {
-               if (cl_books->books)
+               if (cl_books->books) {
                        list = gslist_to_array(cl_books->books, &num);
+                       g_key_file_set_string_list(config->key_file, cl_books->group,
+                               "books", (const gchar* const *) list, num);
+                       g_strfreev(list);
+               }
                else {
-                       list = g_new0(gchar *, 1);
+                       g_key_file_set_string(config->key_file, cl_books->group,
+                               "books", "");
                }
-               g_key_file_set_string_list(config->key_file, cl_books->group,
-                               "books", (const gchar* const *) list, num);
-               g_strfreev(list);
        }
 }
 
@@ -497,7 +510,7 @@ void config_get(ConfigFile* config, gchar** error) {
 }
 
 static void config_close(ConfigFile* config, gchar** error) {
-       gchar* data;
+       gchar *data, *dir;
        GError* err = NULL;
        FILE* fp;
        gsize len;
@@ -510,13 +523,30 @@ static void config_close(ConfigFile* config, gchar** error) {
                        g_clear_error(&err);
                }
                else {
-                       gchar* old = g_strconcat(config->path, ".bak", NULL);
-                       g_rename(config->path, old);
-                       g_free(old);
+                       dir = g_path_get_dirname(config->path);
+                       if (strcmp(".", dir) != 0) {
+                               if (!g_file_test(dir, G_FILE_TEST_EXISTS)) {
+                                       if (g_mkdir(dir, 0700)) {
+                                               *error = g_strconcat(dir, ": Could not create", NULL);
+                                               goto end;
+                                       }
+                               }
+                       }
+                       g_free(dir);
+                       if (g_file_test(config->path, G_FILE_TEST_EXISTS)) {
+                               gchar* old = g_strconcat(config->path, ".bak", NULL);
+                               g_rename(config->path, old);
+                               g_free(old);
+                       }
                        fp = g_fopen(config->path, "w");
-                       fwrite(data, len, 1, fp);
-                       fclose(fp);
+                       if (fp) {
+                               fwrite(data, len, 1, fp);
+                               fclose(fp);
+                       }
+                       else
+                               *error = g_strconcat(config->path, ": Could not create", NULL);
                }
+end:
                g_free(data);
                g_key_file_free(config->key_file);
                config->key_file = NULL;
@@ -536,9 +566,9 @@ static void config_open(ConfigFile* config, gchar** error) {
        if (g_file_test(config->path, G_FILE_TEST_EXISTS)) {
                g_key_file_load_from_file(
                        config->key_file, config->path, G_KEY_FILE_KEEP_COMMENTS, &err);
-               if (err) {
+               if (err && err->code > 1) {
                        config_close(config, error);
-                       g_free(error);
+                       g_free(*error);
                        if (error)
                                *error = g_strdup(err->message);
                        g_clear_error(&err);
@@ -564,7 +594,7 @@ gboolean config_get_value(ConfigFile* config, const gchar* group,
        }
        g_strfreev(str);
                        
-       return TRUE;
+       return (len > 0);
 }
 
 gboolean config_set_value(ConfigFile* config, const gchar* group,
@@ -585,13 +615,14 @@ gboolean config_set_value(ConfigFile* config, const gchar* group,
 }
 
 void plugin_config_free(ConfigFile** config_file) {
-       ConfigFile* config = *config_file;
+       ConfigFile* config;
        ConfiguredBooks* cf_books;
        ClosedBooks* cl_books;
        
-       if (config == NULL)
+       if (! config_file || ! *config_file)
                return;
        
+       config = *config_file;
        cf_books = config->configured_books;
        cl_books = config->closed_books;
 
@@ -629,7 +660,9 @@ gchar** gslist_to_array(const GSList* list, gint* len) {
        }
        gchar* str = g_string_free(buf, FALSE);
        gchar* tmp = str;
-       tmp[strlen(tmp) - 1] = 0;
+       if (strlen(str) > 0 && str[strlen(str) - 1] == '|') {
+               tmp[strlen(tmp) - 1] = 0;
+       }
        array = g_strsplit_set(tmp, "|", 0);
        g_free(str);
        
@@ -776,6 +809,9 @@ gboolean contact_compare_attrib(Contact* a, Contact* b, const AttribDef* compare
 void address_book_contacts_free(AddressBook* address_book) {
        GList* cur;
 
+       if (! address_book)
+               return;
+               
        for (cur = address_book->contacts; cur; cur = g_list_next(cur)) {
                Contact* c = (Contact *) cur->data;
                contact_free(c);
@@ -787,16 +823,18 @@ void address_book_contacts_free(AddressBook* address_book) {
 }
 
 void address_book_free(AddressBook** address_book) {
-       AddressBook* a = *address_book;
+       AddressBook* a;
        
-       if (! a)
+       if (! address_book || ! *address_book)
                return;
-               
+       
+       a  = *address_book;
        g_free(a->abook_name);
        g_free(a->URL);
        g_free(a->username);
        g_free(a->password);
        address_book_contacts_free(a);
+       gslist_free(&a->extra_config, extra_config_free);
        *address_book = NULL;
 }
 
@@ -854,6 +892,7 @@ AddressBook* address_book_copy(AddressBook* a, gboolean deep) {
        b->password = g_strdup(a->password);
        b->next_uid = a->next_uid;
        b->dirty = a->dirty;
+
        if (deep) {
                for (cur = a->contacts; cur; cur = g_list_next(cur)) {
                        b->contacts = g_list_prepend(
@@ -862,6 +901,9 @@ AddressBook* address_book_copy(AddressBook* a, gboolean deep) {
        }
        else
                b->contacts = g_list_copy(a->contacts);
+
+       b->extra_config = g_slist_reverse(gslist_deep_copy(
+               a->extra_config, extra_config_copy));
        
        debug_print("from: %d contacts to: %d contacts\n",
                g_list_length(a->contacts), g_list_length(b->contacts));
@@ -969,6 +1011,9 @@ void g_value_email_free(gpointer data) {
        GValue* email_member;
        guint i;
        
+       if (! email)
+               return;
+       
        for (i = 0; i < email->n_values; i++) {
                email_member = g_value_array_get_nth(email, i);
                g_value_unset(email_member);
@@ -1269,4 +1314,251 @@ gint gslist_get_index(GSList* list, gconstpointer data, GCompareFunc comp) {
                index = g_slist_index(list, data);
 
        return index;
+}
+
+#define KEY "This is abook I1"
+static gboolean aes_init(gcry_cipher_hd_t* digest) {
+    gcry_error_t err = 0;
+    const gchar iv[] = "AbC1234567890xYz";
+
+       err = gcry_cipher_open(digest, GCRY_CIPHER_AES256,
+               GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_SECURE | GCRY_CIPHER_CBC_MAC);
+    if (err) {
+        g_printerr("%s\n", gcry_strerror(err));
+       gcry_cipher_close(*digest);
+       return FALSE;
+    }
+
+       err = gcry_cipher_setkey(*digest, KEY, strlen(KEY));
+    if (err) {
+        g_printerr("%s\n", gcry_strerror(err));
+       gcry_cipher_close(*digest);
+       return FALSE;
+    }
+
+       err = gcry_cipher_setiv(*digest, iv, gcry_cipher_get_algo_blklen(GCRY_CIPHER_AES256));
+    if (err) {
+        g_printerr("%s\n", gcry_strerror(err));
+       gcry_cipher_close(*digest);
+       return FALSE;
+    }
+    
+    return TRUE;
+}
+
+static NormBlock* normalize(const gchar* text) {
+       size_t blk_size = gcry_cipher_get_algo_blklen(GCRY_CIPHER_AES256);
+       gint passes = strlen(text) / blk_size;
+       gint rest = strlen(text) % blk_size;
+       NormBlock* norm_block = g_new0(NormBlock, 1);
+       norm_block->size = (passes * blk_size) + (rest) ? blk_size : 0;
+       gint i;
+       
+       norm_block->block = g_new0(guchar, norm_block->size);
+       for (i = 0; i < strlen(text); i++) {
+               norm_block->block[i] = text[i];
+       }
+       
+       return norm_block;
+}
+
+gchar* aes256_encrypt(const gchar* plain, gboolean base64_enc) {
+    gcry_error_t err = 0;
+    gcry_cipher_hd_t digest = NULL;
+       guchar *cipher;
+       NormBlock* text;
+       gchar* base64 = NULL;
+       
+       if (! plain)
+               return NULL;
+
+       if (aes_init(&digest)) {
+               text = normalize(plain);
+               cipher = g_new0(guchar, text->size);
+               err = gcry_cipher_encrypt(digest, cipher, text->size, text->block, text->size);
+           if (err) {
+               g_printerr("%s\n", gcry_strerror(err));
+           }
+           else {
+                       if (base64_enc)
+                               base64 = g_base64_encode(cipher, text->size);
+                       else
+                               base64 = g_memdup(cipher, text->size);  
+               }
+               
+               g_free(cipher);
+               g_free(text->block);
+               g_free(text);
+               gcry_cipher_close(digest);
+       }
+       
+       return base64;
+}
+
+gchar* aes256_decrypt(const gchar* cipher_text, gboolean base64_enc) {
+    gcry_error_t err = 0;
+    gcry_cipher_hd_t digest = NULL;
+       size_t size;
+       guchar* cipher;
+       guchar* plain = NULL;
+
+       if (! cipher_text)
+               return NULL;
+
+       if (base64_enc)
+               cipher = g_base64_decode(cipher_text, &size);
+       else
+               cipher = (guchar *) g_strdup(cipher_text);
+
+       if (aes_init(&digest)) {
+               plain = g_new0(guchar, size + 1);
+               err = gcry_cipher_decrypt(digest, plain, size + 1, cipher, size);
+           if (err) {
+               g_printerr("%s\n", gcry_strerror(err));
+               g_free(plain);
+               plain = NULL;
+           }
+               
+               g_free(cipher);
+               gcry_cipher_close(digest);
+       }
+       
+       return (gchar *) plain;
+}
+
+static gchar* format_hash(const guchar* md_string) {
+    int len = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
+    int i;
+    gchar* hex = g_new0(gchar, 2 * len + 1);
+
+       for (i = 0; i < len; i++)
+        sprintf(hex + 2 * i, "%02x", md_string[i]);
+       
+       return hex;
+}
+
+#define key "claws-mail address book"
+gchar* sha256(const gchar* plain) {
+    gcry_error_t err = 0;
+    gcry_md_hd_t digest = NULL;
+    guchar* md_string = NULL;
+    gchar* cipher;
+
+       if (! plain)
+               return NULL;
+
+    err = gcry_md_open(
+                &digest, GCRY_MD_SHA256,
+                GCRY_MD_FLAG_SECURE | GCRY_MD_FLAG_HMAC);
+    if (err) {
+        g_printerr("%s\n", gcry_strerror(err));
+        gcry_md_close(digest);
+    }
+
+    err = gcry_md_setkey(digest, key, strlen(key));
+    if (err) {
+        g_printerr("%s\n", gcry_strerror(err));
+        gcry_md_close(digest);
+    }
+
+    gcry_md_write(digest, plain, strlen(plain));
+
+    md_string = gcry_md_read(digest, 0);
+    cipher = format_hash(md_string);
+    gcry_md_close(digest);
+    
+    return cipher;
+}
+
+void extra_config_free(gpointer data) {
+    ExtraConfig* ec;
+    
+    if (! data)
+        return;
+        
+    ec = (ExtraConfig *) data;
+    g_free(ec->label);
+    g_free(ec->tooltip);
+       if (ec->type == PLUGIN_CONFIG_EXTRA_ENTRY) {
+               g_free(ec->value.entry);
+               g_free(ec->default_value.entry);
+       }
+    g_free(ec);
+    ec = NULL;
+}
+
+gpointer extra_config_copy(gpointer data) {
+       ExtraConfig *a, *b;
+       
+       if (! data)
+               return NULL;
+               
+       a = (ExtraConfig *) data;
+       b = g_new0(ExtraConfig, 1);
+       
+       b->label = g_strdup(a->label);
+       b->tooltip = g_strdup(a->tooltip);
+       b->type = a->type;
+       if (a->type == PLUGIN_CONFIG_EXTRA_ENTRY) {
+               b->value.entry = g_strdup(a->value.entry);
+               b->default_value.entry = g_strdup(a->default_value.entry);
+       }
+       else {
+               b->value = a->value;
+               b->default_value = a->default_value;
+       }
+       
+       return b; 
+}
+
+typedef struct {
+       GSList* list;
+       const gchar* name;
+} GtkIteratorData;
+
+static void gtk_iterator(GtkWidget* widget, gpointer data) {
+       GtkIteratorData* iter_data = (GtkIteratorData *) data;
+       const gchar* name;
+       
+       if (GTK_IS_CONTAINER(widget))
+               gtk_container_foreach(GTK_CONTAINER(widget), gtk_iterator, iter_data);
+       name = gtk_widget_get_name(widget);
+       if (name && strcmp(name, iter_data->name) == 0)
+               iter_data->list = g_slist_prepend(iter_data->list, widget);
+}
+
+GSList* find_name(GtkContainer* container, const gchar* name) {
+       GSList* list = NULL;
+       GtkIteratorData* data;
+       
+       if (! container || ! name)
+               return list;
+       
+       data = g_new0(GtkIteratorData, 1);
+       data->name = name;
+       
+       gtk_container_foreach(container, gtk_iterator, data);
+       list = data->list;
+       g_free(data);
+       
+       return list;
+}
+
+ExtraConfig* get_extra_config(GSList* list, const gchar* name) {
+       GSList* cur;
+       ExtraConfig* conf = NULL;
+       gboolean found = FALSE;
+       
+       if (! list || ! name)
+               return conf;
+               
+       for (cur = list; cur && !found; cur = g_slist_next(cur)) {
+               conf = (ExtraConfig *) cur->data;
+               if (conf->label && strcmp(conf->label, name) == 0)
+                       found = TRUE;
+               else
+                       conf = NULL;
+       }
+       
+       return extra_config_copy(conf);
 }
\ No newline at end of file
index 443a3e0a702c75fe1283b8f45b98d13a6da4b5ef..4f383181d98fbfed267385bfb60630c0b40d008a 100644 (file)
@@ -125,6 +125,13 @@ gint gslist_get_index(GSList* list, gconstpointer data, GCompareFunc comp);
 gint gslist_compare_gchar(gconstpointer a, gconstpointer b);
 GPtrArray* g_value_email_new();
 void dbus_contact_print(DBusContact* contact, FILE* f);
+gchar* aes256_encrypt(const gchar* plain, gboolean base64_enc);
+gchar* aes256_decrypt(const gchar* cipher_text, gboolean base64_enc);
+gchar* sha256(const gchar* plain);
+void extra_config_free(gpointer data);
+gpointer extra_config_copy(gpointer data);
+ExtraConfig* get_extra_config(GSList* list, const gchar* name);
+GSList* find_name(GtkContainer* container, const gchar* name);
 
 G_END_DECLS