2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2012 Michael Rasmussen and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * Functions necessary to access LDAP servers.
26 * 1) When adding a contact to an empty addressbook from the pop-up menu
27 * when right-clicking on an email address causes claws-mail to crash in
28 * addritem.c line 965. Severity: Show stopper. Solved in 2.9.2cvs17
29 * 2) Updating a contact gets lost if the user makes a new search on the
30 * same LdapServer. Severity: Medium. Solved in 2.9.2cvs17 (patch added to solve 1) also solved this bug)
31 * 3) After adding a new contact the displayName for the contact is empty
32 * until the user makes a reread from the LdapServer. Severity: minor.
33 * Solved in 2.9.2cvs24
43 #include <glib/gi18n.h>
47 #include "ldapupdate.h"
50 #include "addrcache.h"
52 #include "ldapquery.h"
53 #include "ldapserver.h"
56 #include "adbookbase.h"
57 #include "editaddress_other_attributes_ldap.h"
60 * Structure to hold user defined attributes
63 typedef struct _AttrKeyValue AttrKeyValue;
64 struct _AttrKeyValue {
70 * Structure to hold contact information.
71 * Each addressbook will have 0..N contacts.
73 typedef struct _EmailKeyValue EmailKeyValue;
74 struct _EmailKeyValue {
81 * Structure to hold information about RDN.
83 typedef struct _Rdn Rdn;
91 * Retrieve address group item for update.
92 * \param group Group to print.
93 * \param array GHashTAble of item_group, or <i>NULL</i> if none created.
95 void ldapsvr_retrieve_item_group(ItemGroup *group, GHashTable *array) {
96 /* Not implemented in this release */
97 cm_return_if_fail(group != NULL);
101 * Create an initial EmailKeyValue structure
102 * \return empty structure
104 EmailKeyValue *emailkeyvalue_create() {
107 buf = g_new0(EmailKeyValue, 1);
115 * Create an initial AttrKeyValue structure
116 * \return empty structure
118 AttrKeyValue *attrkeyvalue_create() {
121 buf = g_new0(AttrKeyValue, 1);
128 * Free created AttrKeyValue structure
129 * \param akv AttrKeyValue structure to free
131 void attrkeyvalue_free(AttrKeyValue *akv) {
145 * Retrieve E-Mail address object for update.
146 * \param item ItemEmail to update.
147 * \return object, or <i>NULL</i> if none created.
149 EmailKeyValue *ldapsvr_retrieve_item_email(ItemEMail *item) {
150 EmailKeyValue *newItem;
151 cm_return_val_if_fail(item != NULL, NULL);
152 newItem = emailkeyvalue_create();
153 newItem->alias = g_strdup(ADDRITEM_NAME(item));
154 newItem->mail = g_strdup(item->address);
155 newItem->remarks = g_strdup(item->remarks);
160 * Retrieve user attribute object for update.
161 * \param item UserAttribute to update.
162 * \return object, or <i>NULL</i> if none created.
164 AttrKeyValue *ldapsvr_retrieve_attribute(UserAttribute *item) {
165 AttrKeyValue *newItem;
166 cm_return_val_if_fail(item != NULL, NULL);
167 newItem = attrkeyvalue_create();
168 newItem->key = g_strdup(item->name);
169 newItem->value = g_strdup(item->value);
174 * Retrieve person object for update.
175 * \param person ItemPerson to update.
176 * \param array GHashTable with user input.
177 * \return false if update is not needed, or true if update is needed.
179 gboolean ldapsvr_retrieve_item_person(ItemPerson *person, GHashTable *array) {
182 cm_return_val_if_fail(person != NULL, FALSE);
183 switch (person->status) {
184 case NONE: return FALSE;
185 case ADD_ENTRY: g_hash_table_insert(array, "status", "new"); break;
186 case UPDATE_ENTRY: g_hash_table_insert(array, "status", "update"); break;
187 case DELETE_ENTRY: g_hash_table_insert(array, "status", "delete"); break;
188 default: g_critical(_("ldapsvr_retrieve_item_person->Unknown status: %d"), person->status);
190 g_hash_table_insert(array, "uid", ADDRITEM_ID(person));
191 g_hash_table_insert(array, "cn", ADDRITEM_NAME(person));
192 g_hash_table_insert(array, "givenName", person->firstName);
193 g_hash_table_insert(array, "sn", person->lastName);
194 g_hash_table_insert(array, "nickName", person->nickName);
195 g_hash_table_insert(array, "dn", person->externalID);
196 g_hash_table_insert(array, "person", person);
197 node = person->listEMail;
200 EmailKeyValue *newEmail = ldapsvr_retrieve_item_email(node->data);
202 attr = g_list_append(attr, newEmail);
203 node = g_list_next(node);
205 g_hash_table_insert(array, "mail", attr);
206 node = person->listAttrib;
209 AttrKeyValue *newAttr = ldapsvr_retrieve_attribute(node->data);
211 attr = g_list_append(attr, newAttr);
212 node = g_list_next(node);
214 g_hash_table_insert(array, "attribute", attr);
219 * Print contents of contacts hashtable for debug.
220 * This function must be called with g_hash_table_foreach.
221 * \param key Key to process.
222 * \param data Data to process.
223 * \param fd Output stream.
225 void ldapsvr_print_contacts_hashtable(gpointer key, gpointer data, gpointer fd) {
226 gchar *keyName = (gchar *) key;
229 if (g_ascii_strcasecmp("mail", keyName) == 0) {
230 node = (GList *) data;
232 EmailKeyValue *item = node->data;
233 if (debug_get_mode()) {
234 debug_print("\t\talias = %s\n", item->alias?item->alias:"null");
235 debug_print("\t\tmail = %s\n", item->mail?item->mail:"null");
236 debug_print("\t\tremarks = %s\n", item->remarks?item->remarks:"null");
239 FILE *stream = (FILE *) fd;
240 fprintf(stream, "\t\talias = %s\n", item->alias?item->alias:"null");
241 fprintf(stream, "\t\tmail = %s\n", item->mail?item->mail:"null");
242 fprintf(stream, "\t\tremarks = %s\n", item->remarks?item->remarks:"null");
244 node = g_list_next(node);
247 else if (g_ascii_strcasecmp("attribute", keyName) == 0) {
248 node = (GList *) data;
250 AttrKeyValue *item = node->data;
251 if (debug_get_mode()) {
252 debug_print("\t\t%s = %s\n", item->key?item->key:"null",
253 item->value?item->value:"null");
256 FILE *stream = (FILE *) fd;
257 fprintf(stream, "\t\t%s = %s\n", item->key?item->key:"null",
258 item->value?item->value:"null");
260 node = g_list_next(node);
264 if (debug_get_mode())
265 debug_print("\t\t%s = %s\n", keyName?keyName:"null", data?(gchar *)data:"null");
267 FILE *stream = (FILE *) fd;
268 fprintf(stream, "\t\t%s = %s\n", keyName?keyName:"null", data?(gchar *)data:"null");
274 * Free list of changed contacts
276 * \param list List of GHashTable
278 void ldapsvr_free_hashtable(GList *list) {
281 g_hash_table_destroy(tmp->data);
283 tmp = g_list_next(tmp);
290 * Get person object from cache
292 * \param server Resource to LDAP
293 * \param uid PersonID in cache
294 * \return person object, or <i>NULL</i> if fail
296 ItemPerson *ldapsvr_get_contact(LdapServer *server, gchar *uid) {
298 cm_return_val_if_fail(server != NULL || uid != NULL, NULL);
299 aio = addrcache_get_object(server->addressCache, uid);
301 if(aio->type == ITEMTYPE_PERSON) {
302 return (ItemPerson *) aio;
309 * Create an initial Rdn structure
311 * \return empty structure
316 buf = g_new0(Rdn, 1);
317 buf->attribute = NULL;
324 * Free a created Rdn structure
325 * \param rdn Structure to free
327 void rdn_free(Rdn *rdn) {
328 if (rdn->attribute) {
329 g_free(rdn->attribute);
330 rdn->attribute = NULL;
345 * update Rdn structure
347 * \param rdn Rdn structure to update
348 * \param head Uniq part of dn
349 * \param tail Common part of dn
351 void update_rdn(Rdn *rdn, gchar *head, gchar *tail) {
352 rdn->value = g_strdup(head);
353 rdn->new_dn = g_strdup_printf("%s=%s%s", rdn->attribute, head, tail);
357 * Deside if dn needs to be changed
359 * \param hash GHashTable with user input.
360 * \param dn dn for current object
361 * \return Rdn structure
363 Rdn *ldapsvr_modify_dn(GHashTable *hash, gchar *dn) {
365 gchar *pos, *compare;
368 cm_return_val_if_fail(hash != NULL || dn != NULL, NULL);
370 pos = g_strstr_len(dn, strlen(dn), "=");
374 compare = g_strndup(dn, pos - dn);
377 rest = g_strstr_len(pos, strlen(pos), ",");
378 val = g_strndup(pos, rest - pos);
386 rdn->attribute = compare;
388 if (strcmp("mail", rdn->attribute) == 0) {
389 GList *list = g_hash_table_lookup(hash, rdn->attribute);
391 EmailKeyValue *item = list->data;
392 compare = (gchar *) item->mail;
393 if (strcmp(compare, rdn->value) == 0) {
394 update_rdn(rdn, compare, rest);
397 list = g_list_next(list);
399 /* if compare and rdn->attribute are equal then last email removed/empty */
400 if (strcmp(compare, rdn->attribute) != 0) {
401 /* RDN changed. Find new */
402 update_rdn(rdn, compare, rest);
406 /* We cannot remove dn */
412 compare = g_hash_table_lookup(hash, rdn->attribute);
413 /* if compare and rdn->attribute are equal then dn removed/empty */
414 if (strcmp(compare, rdn->attribute) != 0) {
415 update_rdn(rdn, compare, rest);
419 /* We cannot remove dn */
429 * This macro is borrowed from the Balsa project
430 * Creates a LDAPMod structure
432 * \param mods Empty LDAPMod structure
433 * \param modarr Array with values to insert
434 * \param op Operation to perform on LDAP
435 * \param attr Attribute to insert
436 * \param strv Empty array which is NULL terminated
437 * \param val Value for attribute
439 #define SETMOD(mods,modarr,op,attr,strv,val) \
440 do { (mods) = &(modarr); (modarr).mod_type=attr; (modarr).mod_op=op;\
441 (strv)[0]=(val); (modarr).mod_values=strv; \
445 * Creates a LDAPMod structure
447 * \param mods Empty LDAPMod structure
448 * \param modarr Array with values to insert
449 * \param op Operation to perform on LDAP
450 * \param attr Attribute to insert
451 * \param strv Array with values to insert. Must be NULL terminated
453 #define SETMODS(mods,modarr,op,attr,strv) \
454 do { (mods) = &(modarr); (modarr).mod_type=attr; \
455 (modarr).mod_op=op; (modarr).mod_values=strv; \
460 * Clean up, close LDAP connection, and refresh cache
462 * \param ld Resource to LDAP
463 * \param server AddressBook resource
464 * \param contact GHashTable with current changed object
466 void clean_up(LDAP *ld, LdapServer *server, GHashTable *contact) {
468 ldapsvr_get_contact(server, g_hash_table_lookup(contact , "uid"));
471 person->status = NONE;
472 displayName = g_hash_table_lookup(contact, "displayName");
474 person->nickName = g_strdup(displayName);
476 if (server->retVal != LDAPRC_SUCCESS) {
479 addrcache_remove_person(server->addressCache, person);
481 g_critical(N_("ldapsvr_update_book: Could not clean cache\n"));
483 addritem_free_item_person(res);
487 ldapsvr_disconnect(ld);
491 * Get cn attribute from dn
493 * \param dn Distinguesh Name for current object
494 * \return AttrKeyValue, or <i>NULL</i> if none created
496 AttrKeyValue *get_cn(gchar *dn) {
502 cm_return_val_if_fail(dn != NULL, NULL);
504 cn = attrkeyvalue_create();
505 start = g_strstr_len(dn, strlen(dn), "cn");
507 attrkeyvalue_free(cn);
510 end = g_strstr_len(start, strlen(start), ",");
511 item = g_strndup(start, end - start);
513 attrkeyvalue_free(cn);
516 key_value = g_strsplit(item, "=", 2);
517 cn->key = g_strdup(key_value[0]);
518 cn->value = g_strdup(key_value[1]);
519 g_strfreev(key_value);
525 * Get mail attribute from dn
527 * \param dn Distinguesh Name for current object
528 * \return AttrKeyValue, or <i>NULL</i> if none created
530 AttrKeyValue *get_mail(gchar *dn) {
536 cm_return_val_if_fail(dn != NULL, NULL);
538 mail = attrkeyvalue_create();
539 start = g_strstr_len(dn, strlen(dn), "mail");
541 attrkeyvalue_free(mail);
544 end = g_strstr_len(start, strlen(start), ",");
545 item = g_strndup(start, end - start);
547 attrkeyvalue_free(mail);
550 key_value = g_strsplit(item, "=", 2);
551 mail->key = g_strdup(key_value[0]);
552 mail->value = g_strdup(key_value[1]);
553 g_strfreev(key_value);
559 * Get ou or o attribute from dn
561 * \param dn Distinguesh Name for current object
562 * \return AttrKeyValue, or <i>NULL</i> if none created
564 AttrKeyValue *get_ou(gchar *dn) {
571 cm_return_val_if_fail(dn != NULL, NULL);
572 ou = attrkeyvalue_create();
573 start = g_strstr_len(dn, strlen(dn), ",o=");
575 start = g_strstr_len(dn, strlen(dn), ",ou=");
577 attrkeyvalue_free(ou);
581 end = g_strstr_len(start, strlen(start), ",");
582 item = g_strndup(start, end - start);
584 attrkeyvalue_free(ou);
587 key_value = g_strsplit(item, "=", 2);
588 ou->key = g_strdup(key_value[0]);
589 ou->value = g_strdup(key_value[1]);
590 g_strfreev(key_value);
596 * Print the contents of a LDAPMod structure for debuging purposes
598 * \param mods LDAPMod structure
600 void ldapsvr_print_ldapmod(LDAPMod *mods[]) {
604 cm_return_if_fail(mods != NULL);
605 g_printerr( "Type\n");
606 for (i = 0; NULL != mods[i]; i++) {
607 LDAPMod *mod = (LDAPMod *) mods[i];
609 switch (mod->mod_op) {
610 case LDAP_MOD_ADD: mod_op = g_strdup("ADD"); break;
611 case LDAP_MOD_REPLACE: mod_op = g_strdup("MODIFY"); break;
612 case LDAP_MOD_DELETE: mod_op = g_strdup("DELETE"); break;
613 default: mod_op = g_strdup("UNKNOWN");
615 g_printerr( "Operation: %s\tType:%s\nValues:\n", mod_op, mod->mod_type);
616 vals = mod->mod_vals.modv_strvals;
618 g_printerr( "\t%s\n", *vals++);
624 * Make a compare for every new value we want to store in the
625 * directory with the current values. Great tool for debugging
626 * against invalid syntax in attributes
628 * \param ld AddressBook resource
629 * \param dn dn for the entry
630 * \param cnt Number of attributes to compare
631 * \param mods LDAPMod structure
633 void ldapsvr_compare_attr(LDAP *ld, gchar *dn, gint cnt, LDAPMod *mods[]) {
636 #ifdef OPEN_LDAP_API_AT_LEAST_3000
642 cm_return_if_fail(ld != NULL || dn != NULL || cnt >= 0 || mods != NULL);
643 for (i = 0; i < cnt; i++) {
644 gchar *value = g_strdup(mods[i]->mod_vals.modv_strvals[0]);
645 if (!value || strcmp(value, "") == 0)
646 value = g_strdup("thisisonlyadummy");
648 #ifdef OPEN_LDAP_API_AT_LEAST_3000
651 val.bv_len = strlen(value);
653 rc = ldap_compare_ext_s(ld, dn, mods[i]->mod_type, &val, NULL, NULL);
657 /* This is deprecated as of OpenLDAP-2.3.0 */
658 rc = ldap_compare_s(ld, dn, mods[i]->mod_type, value);
662 g_printerr("ldap_compare for (%s:%s)\" failed[0x%x]: %s\n",
663 mods[i]->mod_type, value, rc, ldaputil_get_error(ld));
669 * compare attribute to LDAP in case of LDAP_INAPPROPRIATE_MATCHING
671 * \param ld AddressBook resource
672 * \param server Reference to server
673 * \param dn dn for the entry
674 * \param attr Attribute
675 * \param value New value
676 * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
678 int ldapsvr_compare_manual_attr(LDAP *ld, LdapServer *server, gchar *dn, char *attr, char *value) {
679 LDAPMessage *res, *e = NULL;
681 struct berval **vals;
689 cm_return_val_if_fail(ld != NULL || server != NULL || attr != NULL, -1);
690 ctl = server->control;
694 filter = g_strdup_printf("(&(mail=%s)(%s=*))", mail->value, attr);
695 attrkeyvalue_free(mail);
698 rc = ldap_search_ext_s(ld, ctl->baseDN, LDAP_SCOPE_ONELEVEL, filter, NULL, 0, NULL, NULL, NULL, 0, &res);
701 g_printerr("ldap_search for attr=%s\" failed[0x%x]: %s\n",attr, rc, ldaputil_get_error(ld));
705 e = ldap_first_entry(ld, res);
706 /* entry has this attribute */
708 attribute = ldap_first_attribute( ld, e, &ber );
711 if( ( vals = ldap_get_values_len( ld, e, attr ) ) != NULL ) {
712 for( i = 0; vals[i] != NULL; i++ ) {
713 debug_print("Compare: %s=%s\n", attr, vals[i]->bv_val);
714 /* attribute has same value */
715 if (strcmp(vals[i]->bv_val, value) == 0)
717 /* attribute has new value */
719 retVal = LDAP_MOD_REPLACE;
722 ldap_value_free_len(vals);
725 retVal = LDAP_MOD_DELETE;
730 ldap_memfree(attribute);
732 /* entry does not have this attribute */
734 /* Only add if this is a real attribute */
736 retVal = LDAP_MOD_ADD;
737 /* This is dummy value used to avoid ldap_compare error */
750 * Deside which kind of operation is required to handle
751 * updating the specified attribute
753 * \param ld AddressBook resource
754 * \param server Reference to server
755 * \param dn dn for the entry
756 * \param attr Attribute
757 * \param value New value
758 * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
760 int ldapsvr_deside_operation(LDAP *ld, LdapServer *server, char *dn, char *attr, char *value) {
762 gboolean dummy = FALSE;
764 #ifdef OPEN_LDAP_API_AT_LEAST_3000
770 cm_return_val_if_fail(ld != NULL || server != NULL || dn != NULL || attr != NULL, -1);
773 /* value containing empty string cause invalid syntax. A bug in
774 * the LDAP library? Therefore we add a dummy value
776 if (strcmp(value,"") == 0) {
777 value = g_strdup("thisisonlyadummy");
781 #ifdef OPEN_LDAP_API_AT_LEAST_3000
784 val.bv_len = strlen(value);
786 rc = ldap_compare_ext_s(ld, dn, attr, &val, NULL, NULL);
790 /* This is deprecated as of OpenLDAP-2.3.0 */
791 rc = ldap_compare_s(ld, dn, attr, value);
795 debug_print("ldap_compare for (%s:%s)\" error_code[0x%x]: %s\n",
796 attr, value, rc, ldaputil_get_error(ld));
798 case LDAP_COMPARE_FALSE:
800 return LDAP_MOD_DELETE;
802 return LDAP_MOD_REPLACE;
803 case LDAP_COMPARE_TRUE: return -1;
804 case LDAP_NO_SUCH_ATTRIBUTE: return LDAP_MOD_ADD;
805 /* LDAP_INAPPROPRIATE_MATCHING needs extensive testing because I
806 * am not aware off the condition causing this return value!
808 case LDAP_INAPPROPRIATE_MATCHING:
811 return ldapsvr_compare_manual_attr(ld, server, dn, attr, value);
812 case LDAP_UNDEFINED_TYPE: return -2;
813 case LDAP_INVALID_SYNTAX: return -2;
819 * Check if attribute is part of the current search criteria
821 * \param list Array containing attributes in the current search criteria
822 * \param attr Attribute to check
823 * \result <i>TRUE</i> if attribute is found in the current search criteria
825 gboolean ldapsvr_check_search_attributes(char **list, char *attr) {
827 if (strcmp(*list++, attr) == 0)
834 * Deside which other attributes needs updating
836 * \param ld LDAP resource
837 * \param server AddressBook resource
838 * \param dn dn for the entry
839 * \param contact GHashTable with information for the current contact
841 void ldapsvr_handle_other_attributes(LDAP *ld, LdapServer *server, char *dn, GHashTable *contact) {
843 gboolean CHECKED_ATTRIBUTE[ATTRIBUTE_SIZE + 1];
844 LDAPMod *mods[ATTRIBUTE_SIZE + 1];
845 LDAPMod modarr[ATTRIBUTE_SIZE];
847 char *attr[ATTRIBUTE_SIZE + 1][2];
850 cm_return_if_fail(server != NULL || dn != NULL || contact != NULL);
851 for (i = 0; i <= ATTRIBUTE_SIZE; i++) {
852 CHECKED_ATTRIBUTE[i] = FALSE;
853 attr[i][0] = attr[i][1] = NULL;
855 node = g_hash_table_lookup(contact , "attribute");
857 AttrKeyValue *item = node->data;
859 int index = get_attribute_index(item->key);
861 debug_print("Found other attribute: %s = %s\n",
862 item->key?item->key:"null", item->value?item->value:"null");
863 mod_op = ldapsvr_deside_operation(ld, server, dn, item->key, item->value);
864 /* Only consider attributes which we no how to handle.
865 * Set to TRUE in CHECKED_ATTRIBUTE array to indicate no further action
868 CHECKED_ATTRIBUTE[index] = TRUE;
869 node = g_list_next(node);
872 if (mod_op == LDAP_MOD_DELETE) {
873 /* Setting param to NULL instructs OpenLDAP to remove any
874 * value stored for this attribute and remove the attribute
875 * completely. Should multiple instances of an attribute be
876 * allowed in the future param is required to have the value
877 * store for the attribute which is going to be deleted
881 if (mod_op == LDAP_MOD_REPLACE && strcmp(item->value, "") == 0) {
882 /* Having an empty string is considered a syntax error in
883 * ldap. E.g attributes with empty strings are not allowed
884 * in which case we treate this as a request for deleting
887 mod_op = LDAP_MOD_DELETE;
890 if (mod_op == LDAP_MOD_ADD && strcmp(item->value, "") == 0) {
891 /* Adding an empty string is considered a syntax error in
892 * ldap. E.g attributes with empty strings are not allowed
893 * in which case we silently refuse to add this entry
897 SETMOD(mods[cnt], modarr[cnt], mod_op, g_strdup(item->key), attr[cnt], g_strdup(item->value));
899 CHECKED_ATTRIBUTE[index] = TRUE;
903 node = g_list_next(node);
905 char **attribs = ldapctl_full_attribute_array(server->control);
906 for (i = 0; i < ATTRIBUTE_SIZE; i++) {
907 /* Attributes which holds no information are to be removed */
908 if (CHECKED_ATTRIBUTE[i] == FALSE) {
909 /* Only consider those attributes which is currently part of the search criteria.
910 * If attributes are not part of the search criteria they would seem to hold
911 * no information since their values will not be populated in the GUI
913 if (!strcmp(ATTRIBUTE[i], "jpegPhoto")) {
914 debug_print("not updating jpegPhoto\n");
917 if (ldapsvr_check_search_attributes(attribs, (char *) ATTRIBUTE[i])) {
918 mod_op = ldapsvr_deside_operation(ld, server, dn, (char *) ATTRIBUTE[i], "");
919 if (mod_op == LDAP_MOD_DELETE) {
920 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_DELETE, g_strdup((char *) ATTRIBUTE[i]), attr[cnt], NULL);
926 ldapctl_free_attribute_array(attribs);
928 if (debug_get_mode())
929 ldapsvr_print_ldapmod(mods);
930 server->retVal = LDAPRC_SUCCESS;
931 rc = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
934 case LDAP_ALREADY_EXISTS:
935 server->retVal = LDAPRC_ALREADY_EXIST;
938 g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n", dn, rc, ldaputil_get_error(ld));
940 server->retVal = LDAPRC_STRONG_AUTH;
942 server->retVal = LDAPRC_NAMING_VIOLATION;
946 char **attribs = ldapctl_full_attribute_array(server->control);
947 for (i = 0; i < ATTRIBUTE_SIZE; i++) {
948 if (!strcmp(ATTRIBUTE[i], "jpegPhoto")) {
949 debug_print("not updating jpegPhoto\n");
952 if (ldapsvr_check_search_attributes(attribs, (char *) ATTRIBUTE[i])) {
953 if (CHECKED_ATTRIBUTE[i] == FALSE) {
954 AddrItemObject *aio = addrcache_get_object(server->addressCache, g_hash_table_lookup(contact , "uid"));
955 ItemPerson *person = (ItemPerson *) aio;
956 addritem_person_remove_attribute(person, (const gchar *) ATTRIBUTE[i]);
960 ldapctl_free_attribute_array(attribs);
965 * Add new contact to LDAP
967 * \param server AddressBook resource
968 * \param contact GHashTable with object to add
970 void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
971 gchar *email = NULL, *param = NULL;
973 LDAPMod *mods[MODSIZE];
976 char *cn[] = {NULL, NULL};
977 char *displayName[] = {NULL, NULL};
978 char *givenName[] = {NULL, NULL};
980 char *sn[] = {NULL, NULL};
981 char *org[] = {NULL, NULL};
982 char *obj[] = {/*"top",*/ "person", "organizationalPerson", "inetOrgPerson", NULL};
985 AttrKeyValue *ou, *commonName;
990 cm_return_if_fail(server != NULL || contact != NULL);
991 node = g_hash_table_lookup(contact , "mail");
993 EmailKeyValue *newEmail = node->data;
994 email = g_strdup(newEmail->mail);
997 server->retVal = LDAPRC_NODN;
998 clean_up(ld, server, contact);
1001 base_dn = g_strdup_printf("mail=%s,%s",
1002 email, server->control->baseDN?server->control->baseDN:"null");
1005 ldapsvr_get_contact(server, g_hash_table_lookup(contact , "uid"));
1006 person->externalID = g_strdup(base_dn);
1007 debug_print("dn: %s\n", base_dn);
1008 ld = ldapsvr_connect(server->control);
1010 clean_up(ld, server, contact);
1011 debug_print("no ldap found\n");
1014 SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "objectClass", obj);
1016 ou = get_ou(base_dn);
1018 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, g_strdup(ou->key), org, g_strdup(ou->value));
1020 attrkeyvalue_free(ou);
1023 commonName = get_cn(base_dn);
1024 if (commonName == NULL) {
1025 param = g_hash_table_lookup(contact , "cn");
1027 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "cn", cn, param);
1030 clean_up(ld, server, contact);
1031 debug_print("no CN found\n");
1036 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, g_strdup(commonName->key), cn, g_strdup(commonName->value));
1038 param = g_hash_table_lookup(contact , "cn");
1039 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "displayName", displayName, param);
1040 g_hash_table_insert(contact, "displayName", param);
1041 attrkeyvalue_free(commonName);
1044 param = g_hash_table_lookup(contact , "givenName");
1046 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "givenName", givenName, param);
1049 mailList = g_hash_table_lookup(contact , "mail");
1052 tmp = g_malloc(sizeof(*tmp) * (g_list_length(mailList)+1));
1055 EmailKeyValue *item = mailList->data;
1056 *tmp++ = g_strdup((gchar *) item->mail);
1057 mailList = g_list_next(mailList);
1060 SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "mail", mail);
1063 param = g_hash_table_lookup(contact, "sn");
1065 param = g_strdup(N_("Some SN"));
1066 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "sn", sn, param);
1069 if (debug_get_mode()) {
1070 ldapsvr_print_ldapmod(mods);
1072 server->retVal = LDAPRC_SUCCESS;
1073 rc = ldap_add_ext_s(ld, base_dn, mods, NULL, NULL);
1076 case LDAP_ALREADY_EXISTS:
1077 server->retVal = LDAPRC_ALREADY_EXIST;
1080 g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",
1081 base_dn, rc, ldaputil_get_error(ld));
1083 server->retVal = LDAPRC_STRONG_AUTH;
1085 server->retVal = LDAPRC_NAMING_VIOLATION;
1088 ldapsvr_handle_other_attributes(ld, server, base_dn, contact);
1090 clean_up(ld, server, contact);
1094 * Update contact to LDAP
1096 * \param server AddressBook resource
1097 * \param contact GHashTable with object to update
1099 void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
1101 LDAPMod *mods[MODSIZE];
1105 Rdn *NoRemove = NULL;
1106 char *cn[] = {NULL, NULL};
1107 char *givenName[] = {NULL, NULL};
1109 char *sn[] = {NULL, NULL};
1113 cm_return_if_fail(server != NULL || contact != NULL);
1114 ld = ldapsvr_connect(server->control);
1116 clean_up(ld, server, contact);
1119 dn = g_hash_table_lookup(contact, "dn");
1122 clean_up(ld, server, contact);
1125 NoRemove = ldapsvr_modify_dn(contact, dn);
1127 /* We are trying to change RDN */
1128 gchar *newRdn = g_strdup_printf("%s=%s", NoRemove->attribute, NoRemove->value);
1130 #ifdef OPEN_LDAP_API_AT_LEAST_3000
1132 int rc = ldap_rename_s(ld, dn, newRdn, NULL, 1, NULL, NULL);
1136 /* This is deprecated as of OpenLDAP-2.3.0 */
1137 int rc = ldap_modrdn2_s(ld, dn, newRdn, 1);
1141 if(rc != LDAP_SUCCESS) {
1142 if (rc == LDAP_ALREADY_EXISTS) {
1143 /* We are messing with a contact with more than one listed email
1144 * address and the email we are changing is not the one used for dn
1146 /* It needs to be able to handle renaming errors to an already defined
1147 * dn. For now we just refuse the update. It will be caught later on as
1148 * a LDAPRC_NAMING_VIOLATION error.
1152 g_printerr("Current dn: %s\n", dn);
1153 g_printerr("new dn: %s\n", newRdn);
1154 g_printerr("LDAP Error(ldap_modrdn2_s) failed[0x%x]: %s\n", rc, ldaputil_get_error(ld));
1156 clean_up(ld, server, contact);
1161 ItemPerson *person = g_hash_table_lookup(contact, "person");
1163 dn = g_strdup(NoRemove->new_dn);
1164 g_hash_table_replace(contact, "dn", dn);
1166 g_free(person->externalID);
1167 person->externalID = dn;
1172 server->retVal = LDAPRC_NODN;
1173 clean_up(ld, server, contact);
1176 param = g_hash_table_lookup(contact , "cn");
1177 mod_op = ldapsvr_deside_operation(ld, server, dn, "displayName", param);
1178 if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("cn", NoRemove->attribute) != 0)) {
1179 if (mod_op == LDAP_MOD_DELETE) {
1180 /* Setting param to NULL instructs OpenLDAP to remove any
1181 * value stored for this attribute and remove the attribute
1182 * completely. Should multiple instances of an attribute be
1183 * allowed in the future param is required to have the value
1184 * store for the attribute which is going to be deleted
1188 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
1189 /* Having an empty string is considered a syntax error in
1190 * ldap. E.g attributes with empty strings are not allowed
1191 * in which case we treate this as a request for deleting
1194 mod_op = LDAP_MOD_DELETE;
1197 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
1198 /* Adding an empty string is considered a syntax error in
1199 * ldap. E.g attributes with empty strings are not allowed
1200 * in which case we silently refuse to add this entry
1204 SETMOD(mods[cnt], modarr[cnt], mod_op, "displayName", cn, param);
1206 g_hash_table_insert(contact, "displayName", param);
1209 param = g_hash_table_lookup(contact , "givenName");
1210 mod_op = ldapsvr_deside_operation(ld, server, dn, "givenName", param);
1211 if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("givenName", NoRemove->attribute) != 0)) {
1212 if (mod_op == LDAP_MOD_DELETE) {
1213 /* Setting param to NULL instructs OpenLDAP to remove any
1214 * value stored for this attribute and remove the attribute
1215 * completely. Should multiple instances of an attribute be
1216 * allowed in the future param is required to have the value
1217 * store for the attribute which is going to be deleted
1221 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
1222 /* Having an empty string is considered a syntax error in
1223 * ldap. E.g attributes with empty strings are not allowed
1224 * in which case we treate this as a request for deleting
1227 mod_op = LDAP_MOD_DELETE;
1230 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
1231 /* Adding an empty string is considered a syntax error in
1232 * ldap. E.g attributes with empty strings are not allowed
1233 * in which case we silently refuse to add this entry
1237 SETMOD(mods[cnt], modarr[cnt], mod_op, "givenName", givenName, param);
1241 mailList = g_hash_table_lookup(contact , "mail");
1243 debug_print("# of mail: %d\n", g_list_length(mailList));
1244 if (!(strcmp("mail", NoRemove->attribute) == 0 && g_list_length(mailList) == 1)) {
1246 tmp = g_malloc(sizeof(*tmp) * (g_list_length(mailList)+1));
1249 EmailKeyValue *item = mailList->data;
1250 *tmp++ = g_strdup((gchar *) item->mail);
1251 mailList = g_list_next(mailList);
1255 * At least one email address is required
1256 * in which case it will always be a replace
1258 SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "mail", mail);
1264 * an error condition since at least one email adress
1265 * is required. Should never occur though.
1268 param = g_hash_table_lookup(contact , "sn");
1269 mod_op = ldapsvr_deside_operation(ld, server, dn, "sn", param);
1270 if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("sn", NoRemove->attribute) != 0)) {
1271 if (mod_op == LDAP_MOD_DELETE) {
1272 /* Setting param to NULL instructs OpenLDAP to remove any
1273 * value stored for this attribute and remove the attribute
1274 * completely. Should multiple instances of an attribute be
1275 * allowed in the future param is required to have the value
1276 * store for the attribute which is going to be deleted
1280 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
1281 /* Having an empty string is considered a syntax error in
1282 * ldap. E.g attributes with empty strings are not allowed
1283 * in which case we treate this as a request for deleting
1286 mod_op = LDAP_MOD_DELETE;
1289 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
1290 /* Adding an empty string is considered a syntax error in
1291 * ldap. E.g attributes with empty strings are not allowed
1292 * in which case we silently refuse to add this entry
1296 SETMOD(mods[cnt], modarr[cnt], mod_op, "sn", sn, param);
1300 debug_print("newDN: %s\n", dn);
1303 server->retVal = LDAPRC_SUCCESS;
1307 rc = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
1309 g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",
1310 dn, rc, ldaputil_get_error(ld));
1311 server->retVal = LDAPRC_NAMING_VIOLATION;
1316 ldapsvr_handle_other_attributes(ld, server, dn, contact);
1317 /* If we do not make changes persistent at this point then changes
1318 * will be lost if the user makes new search on the same server since
1319 * changes are only present in Claws' internal cache. This issue has to
1320 * be solved in addressbook.c since this involves access to structures
1321 * which are only accessible in addressbook.c */
1322 clean_up(ld, server, contact);
1326 * Delete contact from LDAP
1328 * \param server AddressBook resource
1329 * \param contact GHashTable with object to delete
1331 void ldapsvr_delete_contact(LdapServer *server, GHashTable *contact) {
1336 cm_return_if_fail(server != NULL || contact != NULL);
1337 ld = ldapsvr_connect(server->control);
1339 clean_up(ld, server, contact);
1342 dn = g_hash_table_lookup(contact, "dn");
1344 clean_up(ld, server, contact);
1347 server->retVal = LDAPRC_SUCCESS;
1348 rc = ldap_delete_ext_s(ld, dn, NULL, NULL);
1350 g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",
1351 dn, rc, ldaputil_get_error(ld));
1352 server->retVal = LDAPRC_NODN;
1354 clean_up(ld, server, contact);
1358 * Update any changes to the server.
1360 * \param server AddressBook resource.
1361 * \param person ItemPerson holding user input.
1363 void ldapsvr_update_book(LdapServer *server, ItemPerson *item) {
1365 GHashTable *contact = NULL;
1366 GList *contacts = NULL, *head = NULL;
1368 cm_return_if_fail(server != NULL);
1369 debug_print("updating ldap addressbook\n");
1371 contact = g_hash_table_new(g_str_hash, g_str_equal);
1373 gboolean result = ldapsvr_retrieve_item_person(item, contact);
1374 debug_print("Found contact to update: %s\n", result? "Yes" : "No");
1376 if (debug_get_mode()) {
1377 addritem_print_item_person(item, stdout);
1379 contacts = g_list_append(contacts, contact);
1383 ItemFolder *folder = server->addressCache->rootFolder;
1384 node = folder->listFolder;
1387 AddrItemObject *aio = node->data;
1389 if (aio->type == ITEMTYPE_FOLDER) {
1390 ItemFolder *folder = (ItemFolder *) aio;
1391 GList *persons = folder->listPerson;
1393 AddrItemObject *aio = persons->data;
1395 if (aio->type == ITEMTYPE_PERSON) {
1396 ItemPerson *item = (ItemPerson *) aio;
1397 gboolean result = ldapsvr_retrieve_item_person(item, contact);
1398 debug_print("Found contact to update: %s\n", result? "Yes" : "No");
1400 if (debug_get_mode()) {
1401 gchar *uid = g_hash_table_lookup(contact, "uid");
1402 item = ldapsvr_get_contact(server, uid);
1403 addritem_print_item_person(item, stdout);
1405 contacts = g_list_append(contacts, contact);
1409 persons = g_list_next(persons);
1414 g_printerr("\t\tpid : ???\n");
1416 node = g_list_next(node);
1421 if (debug_get_mode()) {
1423 debug_print("Contacts which must be updated in LDAP:\n");
1425 debug_print("\tContact:\n");
1426 g_hash_table_foreach(contacts->data,
1427 ldapsvr_print_contacts_hashtable, stderr);
1428 contacts = g_list_next(contacts);
1431 if (contacts == NULL)
1435 contact = (GHashTable *) contacts->data;
1436 status = (gchar *) g_hash_table_lookup(contact, "status");
1438 status = g_strdup("NULL");
1439 if (g_ascii_strcasecmp(status, "new") == 0) {
1440 ldapsvr_add_contact(server, contact);
1442 else if (g_ascii_strcasecmp(status, "update") == 0) {
1443 ldapsvr_update_contact(server, contact);
1445 else if (g_ascii_strcasecmp(status, "delete") == 0) {
1446 ldapsvr_delete_contact(server, contact);
1449 g_critical(_("ldapsvr_update_book->Unknown status: %s\n"), status);
1450 contacts = g_list_next(contacts);
1452 ldapsvr_free_hashtable(head);
1455 #endif /* USE_LDAP */