X-Git-Url: http://git.claws-mail.org/?p=claws.git;a=blobdiff_plain;f=src%2Fldapupdate.c;h=d8425c67c357ab7cea127572e5d6dcc8b0b5e99c;hp=f5c33b5b4b516f85b18fd5078c322df813a6aa45;hb=cc805ae1d452897db93307213e1068145f02954c;hpb=d49969f17779016988bd8f51c82b143647078629 diff --git a/src/ldapupdate.c b/src/ldapupdate.c index f5c33b5b4..d8425c67c 100644 --- a/src/ldapupdate.c +++ b/src/ldapupdate.c @@ -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, @@ -13,14 +13,26 @@ * 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 . + * */ /* * 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 * @@ -423,6 +475,7 @@ Rdn *ldapsvr_modify_dn(GHashTable *hash, gchar *dn) { else { /* We cannot remove dn */ g_free(compare); + rdn_free(rdn); return NULL; } } @@ -435,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; } @@ -489,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);*/ } /** @@ -511,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]); @@ -525,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 NULL 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 * @@ -543,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]); @@ -567,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; @@ -577,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 TRUE 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); + } } /** @@ -619,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); @@ -637,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); @@ -654,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"); @@ -669,8 +1068,7 @@ void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) { mailList = g_hash_table_lookup(contact , "mail"); if (mailList) { char **tmp; - mail = g_malloc(sizeof(*mail)); - tmp = g_malloc(sizeof(*tmp)); + tmp = g_malloc(sizeof(*tmp) * (g_list_length(mailList)+1)); mail = tmp; while (mailList) { EmailKeyValue *item = mailList->data; @@ -698,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); } @@ -727,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); @@ -755,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; } } @@ -773,22 +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)) { char **tmp; - mail = g_malloc(sizeof(*mail)); - tmp = g_malloc(sizeof(*tmp)); + tmp = g_malloc(sizeof(*tmp) * (g_list_length(mailList)+1)); mail = tmp; while (mailList) { EmailKeyValue *item = mailList->data; @@ -796,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); } @@ -846,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; } @@ -912,7 +1411,7 @@ void ldapsvr_update_book(LdapServer *server, ItemPerson *item) { } } else { - fprintf(stderr, "\t\tpid : ???\n"); + g_printerr("\t\tpid : ???\n"); } node = g_list_next(node); }