2007-08-21 [colin] 2.10.0cvs142
[claws.git] / src / ldapupdate.c
index e6f7269849a0e4702205c045f37bab1b8c9e8ba5..d8425c67c357ab7cea127572e5d6dcc8b0b5e99c 100644 (file)
@@ -4,7 +4,7 @@
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 
  */
 
 /*
  * Functions necessary to access LDAP servers.
  */
 
+/*
+ * Outstanding bugs
+ * 1) When adding a contact to an empty addressbook from the pop-up menu
+ * when right-clicking on an email address causes claws-mail to crash in
+ * addritem.c line 965. Severity: Show stopper. Solved in 2.9.2cvs17
+ * 2) Updating a contact gets lost if the user makes a new search on the
+ * same LdapServer. Severity: Medium. Solved in 2.9.2cvs17 (patch added to solve 1) also solved this bug)
+ * 3) After adding a new contact the displayName for the contact is empty
+ * until the user makes a reread from the LdapServer. Severity: minor.
+ * Solved in 2.9.2cvs24
+ */
+
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
 #endif
@@ -44,6 +56,7 @@
 #include "ldaputil.h"
 #include "utils.h"
 #include "adbookbase.h"
+#include "editaddress_other_attributes_ldap.h"
 
 /**
  * Structure to hold user defined attributes
@@ -113,6 +126,23 @@ AttrKeyValue *attrkeyvalue_create() {
        return buf;
 }
 
+/**
+ * Free created AttrKeyValue structure
+ * \param akv AttrKeyValue structure to free
+ */
+void attrkeyvalue_free(AttrKeyValue *akv) {
+       if (akv->key) {
+               g_free(akv->key);
+               akv->key = NULL;
+       }
+       if (akv->value) {
+               g_free(akv->value);
+               akv->value = NULL;
+       }
+       g_free(akv);
+       akv = NULL;
+}
+
 /**
  * Retrieve E-Mail address object for update.
  * \param item   ItemEmail to update.
@@ -174,17 +204,15 @@ gboolean ldapsvr_retrieve_item_person(ItemPerson *person, GHashTable *array) {
                node = g_list_next(node);
        }
        g_hash_table_insert(array, "mail", attr);
-/* Not implemented in this release.
        node = person->listAttrib;
        attr = NULL;
        while (node) {
-               AttrKeyValue *newAttr = ldapsvr_retrieve_attribute(node->data)
+               AttrKeyValue *newAttr = ldapsvr_retrieve_attribute(node->data);
                if (newAttr)
                        attr = g_list_append(attr, newAttr);
                node = g_list_next(node);
        }
        g_hash_table_insert(array, "attribute", attr);
-*/
        return TRUE;
 }
 
@@ -247,11 +275,14 @@ void ldapsvr_print_contacts_hashtable(gpointer key, gpointer data, gpointer fd)
  * \param list List of GHashTable
  */
 void ldapsvr_free_hashtable(GList *list) {
-       while (list) {
-               g_hash_table_destroy(list->data);
-               list = g_list_next(list);
+       GList *tmp = list;
+       while (tmp) {
+               g_hash_table_destroy(tmp->data);
+               tmp->data = NULL;
+               tmp = g_list_next(tmp);
        }
        g_list_free(list);
+       list = NULL;
 }
 
 /**
@@ -310,7 +341,7 @@ LDAP *ldapsvr_connect(LdapControl *ctl) {
                        rc = ldap_start_tls_s(ld, NULL, NULL);
                        
                        if (rc != LDAP_SUCCESS) {
-                               fprintf(stderr, "LDAP Error(tls): ldap_simple_bind_s: %s\n",
+                               g_printerr("LDAP Error(tls): ldap_simple_bind_s: %s\n",
                                        ldap_err2string(rc));
                                return NULL;
                        }
@@ -323,8 +354,8 @@ LDAP *ldapsvr_connect(LdapControl *ctl) {
                if (* ctl->bindDN != '\0') {
                        rc = claws_ldap_simple_bind_s(ld, ctl->bindDN, ctl->bindPass);
                        if (rc != LDAP_SUCCESS) {
-                               fprintf(stderr, "bindDN: %s, bindPass: %s\n", ctl->bindDN, ctl->bindPass);
-                               fprintf(stderr, "LDAP Error(bind): ldap_simple_bind_s: %s\n",
+                               g_printerr("bindDN: %s, bindPass: %s\n", ctl->bindDN, ctl->bindPass);
+                               g_printerr("LDAP Error(bind): ldap_simple_bind_s: %s\n",
                                        ldap_err2string(rc));
                                return NULL;
                        }
@@ -358,6 +389,27 @@ Rdn *rdn_create() {
        return buf;
 }
 
+/**
+ * Free a created Rdn structure
+ * \param rdn Structure to free
+ */
+void rdn_free(Rdn *rdn) {
+       if (rdn->attribute) {
+               g_free(rdn->attribute);
+               rdn->attribute = NULL;
+       }
+       if (rdn->value) {
+               g_free(rdn->value);
+               rdn->value = NULL;
+       }
+       if (rdn->new_dn) {
+               g_free(rdn->new_dn);
+               rdn->new_dn = NULL;
+       }
+       g_free(rdn);
+       rdn = NULL;
+}
+
 /**
  * update Rdn structure
  *
@@ -380,6 +432,8 @@ void update_rdn(Rdn *rdn, gchar *head, gchar *tail) {
 Rdn *ldapsvr_modify_dn(GHashTable *hash, gchar *dn) {
        Rdn *rdn;
        gchar *pos, *compare;
+       gchar *rest;
+       gchar *val;
        g_return_val_if_fail(hash != NULL || dn != NULL, NULL);
        
        pos = g_strstr_len(dn, strlen(dn), "=");
@@ -388,8 +442,8 @@ Rdn *ldapsvr_modify_dn(GHashTable *hash, gchar *dn) {
        if (!pos)
                return NULL;
        pos++;
-       gchar *rest = g_strstr_len(pos, strlen(pos), ",");
-       gchar *val = g_strndup(pos, rest - pos);
+       rest = g_strstr_len(pos, strlen(pos), ",");
+       val = g_strndup(pos, rest - pos);
        if (val == NULL) {
                if (compare)
                        g_free(compare);
@@ -421,6 +475,7 @@ Rdn *ldapsvr_modify_dn(GHashTable *hash, gchar *dn) {
                else {
                        /* We cannot remove dn */
                        g_free(compare);
+                       rdn_free(rdn);
                        return NULL;
                }
        }
@@ -433,9 +488,11 @@ Rdn *ldapsvr_modify_dn(GHashTable *hash, gchar *dn) {
                }
                else {
                        /* We cannot remove dn */
+                       rdn_free(rdn);
                        return NULL;
                }
        }
+       rdn_free(rdn);
        return NULL;
 }
 
@@ -487,10 +544,18 @@ void clean_up(LDAP *ld, LdapServer *server, GHashTable *contact) {
                if (displayName)
                        person->nickName = g_strdup(displayName);
        }
+       if (server->retVal != LDAPRC_SUCCESS) {
+               if (person) {
+                       ItemPerson *res = 
+                               addrcache_remove_person(server->addressCache, person);
+                       if (!res)
+                               g_critical(N_("ldapsvr_update_book: Could not clean cache\n"));
+                       else
+                               addritem_free_item_person(res);
+               }
+       }
        if (ld)
                ldapsvr_disconnect(ld);
-       ldapsvr_force_refresh(server);
-/*     ldapsvr_free_all_query(server);*/
 }
 
 /**
@@ -509,12 +574,16 @@ AttrKeyValue *get_cn(gchar *dn) {
        
        cn = attrkeyvalue_create();
        start = g_strstr_len(dn, strlen(dn), "cn");
-       if (start == NULL)
+       if (start == NULL) {
+               attrkeyvalue_free(cn);
                return NULL;
+       }
        end = g_strstr_len(start, strlen(start), ",");
        item = g_strndup(start, end - start);
-       if (item == NULL)
+       if (item == NULL) {
+               attrkeyvalue_free(cn);
                return NULL;
+       }
        key_value = g_strsplit(item, "=", 2);
        cn->key = g_strdup(key_value[0]);
        cn->value = g_strdup(key_value[1]);
@@ -523,6 +592,40 @@ AttrKeyValue *get_cn(gchar *dn) {
        return cn;
 }
 
+/**
+ * Get mail attribute from dn
+ *
+ * \param dn Distinguesh Name for current object
+ * \return AttrKeyValue, or <i>NULL</i> if none created
+ */
+AttrKeyValue *get_mail(gchar *dn) {
+       AttrKeyValue *mail;
+       gchar *start;
+       gchar *end;
+       gchar *item;
+       gchar **key_value;
+       g_return_val_if_fail(dn != NULL, NULL);
+       
+       mail = attrkeyvalue_create();
+       start = g_strstr_len(dn, strlen(dn), "mail");
+       if (start == NULL) {
+               attrkeyvalue_free(mail);
+               return NULL;
+       }
+       end = g_strstr_len(start, strlen(start), ",");
+       item = g_strndup(start, end - start);
+       if (item == NULL) {
+               attrkeyvalue_free(mail);
+               return NULL;
+       }
+       key_value = g_strsplit(item, "=", 2);
+       mail->key = g_strdup(key_value[0]);
+       mail->value = g_strdup(key_value[1]);
+       g_strfreev(key_value);
+       g_free(item);
+       return mail;
+}
+
 /**
  * Get ou or o attribute from dn
  *
@@ -541,13 +644,17 @@ AttrKeyValue *get_ou(gchar *dn) {
        start = g_strstr_len(dn, strlen(dn), ",o=");
        if (start == NULL)
                start = g_strstr_len(dn, strlen(dn), ",ou=");
-       if (start == NULL)
+       if (start == NULL) {
+               attrkeyvalue_free(ou);
                return NULL;
+       }
        start++;
        end = g_strstr_len(start, strlen(start), ",");
        item = g_strndup(start, end - start);
-       if (item == NULL)
+       if (item == NULL) {
+               attrkeyvalue_free(ou);
                return NULL;
+       }
        key_value = g_strsplit(item, "=", 2);
        ou->key = g_strdup(key_value[0]);
        ou->value = g_strdup(key_value[1]);
@@ -565,7 +672,8 @@ void ldapsvr_print_ldapmod(LDAPMod *mods[]) {
        gchar *mod_op;
        int i;
 
-       fprintf( stderr, "Type\n");
+       g_return_if_fail(mods != NULL);
+       g_printerr( "Type\n");
        for (i = 0; NULL != mods[i]; i++) {
                LDAPMod *mod = (LDAPMod *) mods[i];
                gchar **vals;
@@ -575,12 +683,302 @@ void ldapsvr_print_ldapmod(LDAPMod *mods[]) {
                        case LDAP_MOD_DELETE: mod_op = g_strdup("DELETE"); break;
                        default: mod_op = g_strdup("UNKNOWN");
                }
-               fprintf( stderr, "Operation: %s\tType:%s\nValues:\n", mod_op, mod->mod_type);
+               g_printerr( "Operation: %s\tType:%s\nValues:\n", mod_op, mod->mod_type);
                vals = mod->mod_vals.modv_strvals;
                while (*vals) {
-                       fprintf( stderr, "\t%s\n", *vals++);
+                       g_printerr( "\t%s\n", *vals++);
+               }
+       }
+}
+
+/**
+ * Make a compare for every new value we want to store in the
+ * directory with the current values. Great tool for debugging
+ * against invalid syntax in attributes
+ *
+ * \param ld AddressBook resource
+ * \param dn dn for the entry
+ * \param cnt Number of attributes to compare
+ * \param  mods LDAPMod structure
+ */
+void ldapsvr_compare_attr(LDAP *ld, gchar *dn, gint cnt, LDAPMod *mods[]) {
+       int i, rc;
+       
+       g_return_if_fail(ld != NULL || dn != NULL || cnt >= 0 || mods != NULL);
+       for (i = 0; i < cnt; i++) {
+               gchar *value = g_strdup(mods[i]->mod_vals.modv_strvals[0]);
+               if (!value || strcmp(value, "") == 0)
+                       value = g_strdup("thisisonlyadummy");
+               rc = ldap_compare_s(ld, dn, mods[i]->mod_type, value);
+               g_printerr("ldap_compare for (%s:%s)\" failed[0x%x]: %s\n",
+               mods[i]->mod_type, value, rc, ldap_err2string(rc));
+               g_free(value);
+       }
+}
+
+/**
+ * compare attribute to LDAP in case of LDAP_INAPPROPRIATE_MATCHING
+ *
+ * \param ld AddressBook resource
+ * \param server Reference to server
+ * \param dn dn for the entry
+ * \param attr Attribute
+ * \param value New value
+ * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
+ */
+int ldapsvr_compare_manual_attr(LDAP *ld, LdapServer *server, gchar *dn, char *attr, char *value) {
+       LDAPMessage *res, *e = NULL;
+       BerElement *ber;
+       struct berval **vals;
+       int rc;
+       LdapControl *ctl;
+       gchar *filter;
+       gchar *attribute;
+       int retVal = -2, i;
+       AttrKeyValue *mail;
+
+       g_return_val_if_fail(ld != NULL || server != NULL || attr != NULL, -1);
+       ctl = server->control;
+       mail = get_mail(dn);
+       if (! mail)
+               return -2;
+       filter = g_strdup_printf("(&(mail=%s)(%s=*))", mail->value, attr);
+       attrkeyvalue_free(mail);
+       if (ctl) {
+               rc = ldap_search_s(ld, ctl->baseDN, /*LDAP_SCOPE_SUBTREE*/LDAP_SCOPE_ONELEVEL, filter, NULL, 0, &res);
+               if (rc) {
+                       g_printerr("ldap_search for attr=%s\" failed[0x%x]: %s\n",attr, rc, ldap_err2string(rc));
+                       retVal = -2;
+               }
+               else {
+                       e = ldap_first_entry(ld, res);
+                       /* entry has this attribute */
+                       if (e) {
+                               attribute = ldap_first_attribute( ld, e, &ber );
+                               if (attribute) {
+                                       if (value) {
+                                               if( ( vals = ldap_get_values_len( ld, e, attr ) ) != NULL ) {
+                                                       for( i = 0; vals[i] != NULL; i++ ) {
+                                                               debug_print("Compare: %s=%s\n", attr, vals[i]->bv_val);
+                                                               /* attribute has same value */
+                                                               if (strcmp(vals[i]->bv_val, value) == 0)
+                                                                       retVal = -1;
+                                                               /* attribute has new value */
+                                                               else
+                                                                       retVal = LDAP_MOD_REPLACE;
+                                                       }
+                                               }
+                                               ldap_value_free_len(vals);
+                                       }
+                                       else
+                                               retVal = LDAP_MOD_DELETE;
+                               }
+                               if( ber != NULL ) {
+                                       ber_free( ber, 0 );
+                               }
+                               ldap_memfree(attribute);
+                       }
+                       /* entry does not have this attribute */
+                       else {
+                               /* Only add if this is a real attribute */
+                               if (value)
+                                       retVal = LDAP_MOD_ADD;
+                               /* This is dummy value used to avoid ldap_compare error */
+                               else
+                                       retVal = -1;
+                       }
+               }
+       }
+       else
+               retVal = -2;
+       g_free(filter);
+       return retVal;
+}
+
+/**
+ * Deside which kind of operation is required to handle
+ * updating the specified attribute
+ *
+ * \param ld AddressBook resource
+ * \param server Reference to server
+ * \param dn dn for the entry
+ * \param attr Attribute
+ * \param value New value
+ * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
+ */
+int ldapsvr_deside_operation(LDAP *ld, LdapServer *server, char *dn, char *attr, char *value) {
+       int rc;
+       gboolean dummy = FALSE;
+
+       g_return_val_if_fail(ld != NULL || server != NULL || dn != NULL || attr != NULL, -1);
+       if (value == NULL)
+               return -1;
+       /* value containing empty string cause invalid syntax. A bug in
+        * the LDAP library? Therefore we add a dummy value
+        */
+       if (strcmp(value,"") == 0) {
+               value = g_strdup("thisisonlyadummy");
+               dummy = TRUE;
+       }
+       rc = ldap_compare_s(ld, dn, attr, value);
+       debug_print("ldap_compare for (%s:%s)\" error_code[0x%x]: %s\n",
+               attr, value, rc, ldap_err2string(rc));
+       switch (rc) {
+               case LDAP_COMPARE_FALSE: 
+                       if (dummy)
+                               return LDAP_MOD_DELETE;
+                       else
+                               return LDAP_MOD_REPLACE;
+               case LDAP_COMPARE_TRUE: return -1;
+               case LDAP_NO_SUCH_ATTRIBUTE: return LDAP_MOD_ADD;
+               /* LDAP_INAPPROPRIATE_MATCHING needs extensive testing because I
+                * am not aware off the condition causing this return value!
+                */
+               case LDAP_INAPPROPRIATE_MATCHING:
+                       if (dummy)
+                               value = NULL;
+                       return ldapsvr_compare_manual_attr(ld, server, dn, attr, value);
+               case LDAP_UNDEFINED_TYPE: return -2;
+               case LDAP_INVALID_SYNTAX: return -2;
+               default: return -2;
+       }
+}
+
+/**
+ * Check if attribute is part of the current search criteria
+ *
+ * \param list Array containing attributes in the current search criteria
+ * \param attr Attribute to check
+ * \result <i>TRUE</i> if attribute is found in the current search criteria
+ */
+gboolean ldapsvr_check_search_attributes(char **list, char *attr) {
+       while (*list) {
+               if (strcmp(*list++, attr) == 0)
+                       return TRUE;
+       }
+       return FALSE;
+}
+
+/**
+ * Deside which other attributes needs updating
+ *
+ * \param ld LDAP resource
+ * \param server AddressBook resource
+ * \param dn dn for the entry
+ * \param contact GHashTable with information for the current contact
+ */
+void ldapsvr_handle_other_attributes(LDAP *ld, LdapServer *server, char *dn, GHashTable *contact) {
+       GList *node;
+       gboolean CHECKED_ATTRIBUTE[ATTRIBUTE_SIZE + 1];
+       LDAPMod *mods[ATTRIBUTE_SIZE + 1];
+       LDAPMod modarr[ATTRIBUTE_SIZE];
+       gint cnt = 0;
+       char *attr[ATTRIBUTE_SIZE + 1][2];
+       int mod_op, rc, i;
+
+       g_return_if_fail(server != NULL || dn != NULL || contact != NULL);
+       for (i = 0; i <= ATTRIBUTE_SIZE; i++) {
+               CHECKED_ATTRIBUTE[i] = FALSE;
+               attr[i][0] = attr[i][1] = NULL;
+       }
+       node = g_hash_table_lookup(contact , "attribute");
+       while (node) {
+               AttrKeyValue *item = node->data;
+               if (item) {
+                       int index = get_attribute_index(item->key);
+                       if (index >= 0) {
+                               debug_print("Found other attribute: %s = %s\n", item->key, item->value);
+                               mod_op = ldapsvr_deside_operation(ld, server, dn, item->key, item->value);
+                               /* Only consider attributes which we no how to handle.
+                                * Set to TRUE in CHECKED_ATTRIBUTE array to indicate no further action
+                                */
+                               if (mod_op < 0) {
+                                       CHECKED_ATTRIBUTE[index] = TRUE;
+                                       node = g_list_next(node);
+                                       continue;
+                               }
+                               if (mod_op == LDAP_MOD_DELETE) {
+                                       /* Setting param to NULL instructs OpenLDAP to remove any
+                                       * value stored for this attribute and remove the attribute
+                                       * completely. Should multiple instances of an attribute be
+                                       * allowed in the future param is required to have the value
+                                       * store for the attribute which is going to be deleted
+                                       */
+                                       item->value = NULL;
+                               }
+                               if (mod_op == LDAP_MOD_REPLACE && strcmp(item->value, "") == 0) {
+                                       /* Having an empty string is considered a syntax error in
+                                       * ldap. E.g attributes with empty strings are not allowed
+                                       * in which case we treate this as a request for deleting
+                                       * the attribute.
+                                       */
+                                       mod_op = LDAP_MOD_DELETE;
+                                       item->value = NULL;
+                               }
+                               if (mod_op == LDAP_MOD_ADD && strcmp(item->value, "") == 0) {
+                                       /* Adding an empty string is considered a syntax error in
+                                       * ldap. E.g attributes with empty strings are not allowed
+                                       * in which case we silently refuse to add this entry
+                                       */
+                               }
+                               else {
+                                       SETMOD(mods[cnt], modarr[cnt], mod_op, g_strdup(item->key), attr[cnt], g_strdup(item->value));
+                                       cnt++;
+                                       CHECKED_ATTRIBUTE[index] = TRUE;
+                               }
+                       }
+               }
+               node = g_list_next(node);
+       }
+       char **attribs = ldapctl_full_attribute_array(server->control);
+       for (i = 0; i < ATTRIBUTE_SIZE; i++) {
+               /* Attributes which holds no information are to be removed */
+               if (CHECKED_ATTRIBUTE[i] == FALSE) {
+                       /* Only consider those attributes which is currently part of the search criteria.
+                        * If attributes are not part of the search criteria they would seem to hold
+                        * no information since their values will not be populated in the GUI
+                        */
+                       if (ldapsvr_check_search_attributes(attribs, (char *) ATTRIBUTE[i])) {
+                               mod_op = ldapsvr_deside_operation(ld, server, dn, (char *) ATTRIBUTE[i], "");
+                               if (mod_op == LDAP_MOD_DELETE) {
+                                       SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_DELETE, g_strdup((char *) ATTRIBUTE[i]), attr[cnt], NULL);
+                                       cnt++;
+                               }
+                       }
                }
        }
+       ldapctl_free_attribute_array(attribs);
+       mods[cnt] = NULL;
+       if (debug_get_mode())
+               ldapsvr_print_ldapmod(mods);
+       server->retVal = LDAPRC_SUCCESS;
+       rc = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
+       if (rc) {
+               switch (rc) {
+                       case LDAP_ALREADY_EXISTS: 
+                               server->retVal = LDAPRC_ALREADY_EXIST;
+                               break;
+                       default:
+                               g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",dn, rc, ldap_err2string(rc));
+                               if (rc == 0x8)
+                                       server->retVal = LDAPRC_STRONG_AUTH;
+                               else
+                                       server->retVal = LDAPRC_NAMING_VIOLATION;
+               }
+       }
+       else {
+               char **attribs = ldapctl_full_attribute_array(server->control);
+               for (i = 0; i < ATTRIBUTE_SIZE; i++) {
+                       if (ldapsvr_check_search_attributes(attribs, (char *) ATTRIBUTE[i])) {
+                               if (CHECKED_ATTRIBUTE[i] == FALSE) {
+                                       AddrItemObject *aio = addrcache_get_object(server->addressCache, g_hash_table_lookup(contact , "uid"));
+                                       ItemPerson *person = (ItemPerson *) aio;
+                                       addritem_person_remove_attribute(person, (const gchar *) ATTRIBUTE[i]);
+                               }
+                       }
+               }
+               ldapctl_free_attribute_array(attribs);
+       }
 }
 
 /**
@@ -617,6 +1015,7 @@ void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
        }
        if (email == NULL) {
                server->retVal = LDAPRC_NODN;
+               clean_up(ld, server, contact);
                return;
        }
        base_dn = g_strdup_printf("mail=%s,%s", email, server->control->baseDN);
@@ -635,8 +1034,9 @@ void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
        cnt++;
        ou = get_ou(base_dn);
        if (ou != NULL) {
-               SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, ou->key, org, ou->value);
+               SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, g_strdup(ou->key), org, g_strdup(ou->value));
                cnt++;
+               attrkeyvalue_free(ou);
        }
        
        commonName = get_cn(base_dn);
@@ -652,11 +1052,12 @@ void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
                }
        }
        else {
-               SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, commonName->key, cn, commonName->value);
+               SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, g_strdup(commonName->key), cn, g_strdup(commonName->value));
                cnt++;
                param = g_hash_table_lookup(contact , "cn");
                SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "displayName", displayName, param);
                g_hash_table_insert(contact, "displayName", param);
+               attrkeyvalue_free(commonName);
        }
        cnt++;
        param = g_hash_table_lookup(contact , "givenName");
@@ -666,8 +1067,8 @@ void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
        }
        mailList = g_hash_table_lookup(contact , "mail");
        if (mailList) {
-               mail = g_malloc(sizeof(*mail));
-               char **tmp = g_malloc(sizeof(*tmp));
+               char **tmp;
+               tmp = g_malloc(sizeof(*tmp) * (g_list_length(mailList)+1));
                mail = tmp;
                while (mailList) {
                        EmailKeyValue *item = mailList->data;
@@ -695,13 +1096,14 @@ void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
                                server->retVal = LDAPRC_ALREADY_EXIST;
                                break;
                        default:
-                               fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",base_dn, rc, ldap_err2string(rc));
+                               g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",base_dn, rc, ldap_err2string(rc));
                                if (rc == 0x8)
                                        server->retVal = LDAPRC_STRONG_AUTH;
                                else
                                        server->retVal = LDAPRC_NAMING_VIOLATION;
                }
        }
+       ldapsvr_handle_other_attributes(ld, server, base_dn, contact);
        g_free(base_dn);
        clean_up(ld, server, contact);
 }
@@ -724,6 +1126,7 @@ void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
        char **mail = NULL;
        char *sn[] = {NULL, NULL};
        GList *mailList;
+       int mod_op;
 
        g_return_if_fail(server != NULL || contact != NULL);
        ld = ldapsvr_connect(server->control);
@@ -752,10 +1155,11 @@ void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
                                 */
                        }
                        else {
-                               fprintf(stderr, "Current dn: %s\n", dn);
-                               fprintf(stderr, "new dn: %s\n", newRdn);
-                               fprintf(stderr, "LDAP Error(ldap_modrdn2_s) failed[0x%x]: %s\n", rc, ldap_err2string(rc));
+                               g_printerr("Current dn: %s\n", dn);
+                               g_printerr("new dn: %s\n", newRdn);
+                               g_printerr("LDAP Error(ldap_modrdn2_s) failed[0x%x]: %s\n", rc, ldap_err2string(rc));
                                g_free(newRdn);
+                               clean_up(ld, server, contact);
                                return;
                        }
                }
@@ -770,21 +1174,76 @@ void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
                return;
        }
        param = g_hash_table_lookup(contact , "cn");
-       if (param && (strcmp(param, NoRemove->value) != 0 && strcmp("cn", NoRemove->attribute) != 0)) {
-               SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "displayName", cn, param);
-               cnt++;
-               g_hash_table_insert(contact, "displayName", param);
+       mod_op = ldapsvr_deside_operation(ld, server, dn, "displayName", param);
+       if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("cn", NoRemove->attribute) != 0)) {
+               if (mod_op == LDAP_MOD_DELETE) {
+                       /* Setting param to NULL instructs OpenLDAP to remove any
+                        * value stored for this attribute and remove the attribute
+                        * completely. Should multiple instances of an attribute be
+                        * allowed in the future param is required to have the value
+                        * store for the attribute which is going to be deleted
+                        */
+                       param = NULL;
+               }
+               if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
+                       /* Having an empty string is considered a syntax error in
+                        * ldap. E.g attributes with empty strings are not allowed
+                        * in which case we treate this as a request for deleting
+                        * the attribute.
+                        */
+                       mod_op = LDAP_MOD_DELETE;
+                       param = NULL;
+               }
+               if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
+                       /* Adding an empty string is considered a syntax error in
+                        * ldap. E.g attributes with empty strings are not allowed
+                        * in which case we silently refuse to add this entry
+                        */
+               }
+               else {
+                       SETMOD(mods[cnt], modarr[cnt], mod_op, "displayName", cn, param);
+                       cnt++;
+                       g_hash_table_insert(contact, "displayName", param);
+               }
        }
        param = g_hash_table_lookup(contact , "givenName");
-       if (param && (strcmp(param, NoRemove->value) != 0 && strcmp("givenName", NoRemove->attribute) != 0)) {
-               SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "givenName", givenName, param);
-               cnt++;
+       mod_op = ldapsvr_deside_operation(ld, server, dn, "givenName", param);
+       if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("givenName", NoRemove->attribute) != 0)) {
+               if (mod_op == LDAP_MOD_DELETE) {
+                       /* Setting param to NULL instructs OpenLDAP to remove any
+                        * value stored for this attribute and remove the attribute
+                        * completely. Should multiple instances of an attribute be
+                        * allowed in the future param is required to have the value
+                        * store for the attribute which is going to be deleted
+                        */
+                       param = NULL;
+               }
+               if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
+                       /* Having an empty string is considered a syntax error in
+                        * ldap. E.g attributes with empty strings are not allowed
+                        * in which case we treate this as a request for deleting
+                        * the attribute.
+                        */
+                       mod_op = LDAP_MOD_DELETE;
+                       param = NULL;
+               }
+               if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
+                       /* Adding an empty string is considered a syntax error in
+                        * ldap. E.g attributes with empty strings are not allowed
+                        * in which case we silently refuse to add this entry
+                        */
+               }
+               else {
+                       SETMOD(mods[cnt], modarr[cnt], mod_op, "givenName", givenName, param);
+                       cnt++;
+               }
        }
        mailList = g_hash_table_lookup(contact , "mail");
        if (mailList) {
+               debug_print("# of mail: %d\n", g_list_length(mailList));
                if (!(strcmp("mail", NoRemove->attribute) == 0 && g_list_length(mailList) == 1)) {
-                       mail = g_malloc(sizeof(*mail));
-                       char **tmp = g_malloc(sizeof(*tmp));
+                       char **tmp;
+                       tmp = g_malloc(sizeof(*tmp) * (g_list_length(mailList)+1));
                        mail = tmp;
                        while (mailList) {
                                EmailKeyValue *item = mailList->data;
@@ -792,31 +1251,74 @@ void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
                                mailList = g_list_next(mailList);
                        }
                        *tmp = NULL;
+                       /*
+                        * At least one email address is required
+                        * in which case it will always be a replace
+                        */
                        SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "mail", mail);
                        cnt++;
                }
        }
+       else {
+               /*
+                * an error condition since at least one email adress
+                * is required. Should never occur though.
+                */
+       }
        param = g_hash_table_lookup(contact , "sn");
-       if (param && (strcmp(param, NoRemove->value) != 0 && strcmp("sn", NoRemove->attribute) != 0)) {
-               SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "sn", sn, param);
-               cnt++;
+       mod_op = ldapsvr_deside_operation(ld, server, dn, "sn", param);
+       if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("sn", NoRemove->attribute) != 0)) {
+               if (mod_op == LDAP_MOD_DELETE) {
+                       /* Setting param to NULL instructs OpenLDAP to remove any
+                        * value stored for this attribute and remove the attribute
+                        * completely. Should multiple instances of an attribute be
+                        * allowed in the future param is required to have the value
+                        * store for the attribute which is going to be deleted
+                        */
+                       param = NULL;
+               }
+               if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
+                       /* Having an empty string is considered a syntax error in
+                        * ldap. E.g attributes with empty strings are not allowed
+                        * in which case we treate this as a request for deleting
+                        * the attribute.
+                        */
+                       mod_op = LDAP_MOD_DELETE;
+                       param = NULL;
+               }
+               if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
+                       /* Adding an empty string is considered a syntax error in
+                        * ldap. E.g attributes with empty strings are not allowed
+                        * in which case we silently refuse to add this entry
+                        */
+               }
+               else {
+                       SETMOD(mods[cnt], modarr[cnt], mod_op, "sn", sn, param);
+                       cnt++;
+               }
        }
        debug_print("newDN: %s\n", dn);
        if (NoRemove)
-               g_free(NoRemove);
+               rdn_free(NoRemove);
        server->retVal = LDAPRC_SUCCESS;
        if (cnt > 0) {
                int rc;
                mods[cnt] = NULL;
                rc = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
                if (rc) {
-                       fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",
+                       g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",
                     dn, rc, ldap_err2string(rc));
                        server->retVal = LDAPRC_NAMING_VIOLATION;
                }
                if (mail)
                        g_free(mail);
        }
+       ldapsvr_handle_other_attributes(ld, server, dn, contact);
+       /* If we do not make changes persistent at this point then changes
+        * will be lost if the user makes new search on the same server since
+        * changes are only present in Claws' internal cache. This issue has to
+        * be solved in addressbook.c since this involves access to structures
+        * which are only accessible in addressbook.c */
        clean_up(ld, server, contact);
 }
 
@@ -842,9 +1344,10 @@ void ldapsvr_delete_contact(LdapServer *server, GHashTable *contact) {
                clean_up(ld, server, contact);
                return;
        }
+       server->retVal = LDAPRC_SUCCESS;
        rc = ldap_delete_ext_s(ld, dn, NULL, NULL);
        if (rc) {
-               fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",
+               g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",
                                dn, rc, ldap_err2string(rc));
                server->retVal = LDAPRC_NODN;
        }
@@ -879,9 +1382,6 @@ void ldapsvr_update_book(LdapServer *server, ItemPerson *item) {
        else {
                ItemFolder *folder = server->addressCache->rootFolder;
                node = folder->listFolder;
-               if (node)
-                       node = g_list_copy(node);
-               node = g_list_prepend(node, server->addressCache->rootFolder);
                if (node) {
                        while (node) {
                                AddrItemObject *aio = node->data;
@@ -911,11 +1411,10 @@ void ldapsvr_update_book(LdapServer *server, ItemPerson *item) {
                                        }
                                }
                                else {
-                                       fprintf(stderr, "\t\tpid : ???\n");
+                                       g_printerr("\t\tpid : ???\n");
                                }
                                node = g_list_next(node);
                        }
-                       g_list_free(node);
                }
        }
        head = contacts;