2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2009 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>
49 #include "ldapupdate.h"
52 #include "addrcache.h"
54 #include "ldapquery.h"
55 #include "ldapserver.h"
58 #include "adbookbase.h"
59 #include "editaddress_other_attributes_ldap.h"
62 * Structure to hold user defined attributes
65 typedef struct _AttrKeyValue AttrKeyValue;
66 struct _AttrKeyValue {
72 * Structure to hold contact information.
73 * Each addressbook will have 0..N contacts.
75 typedef struct _EmailKeyValue EmailKeyValue;
76 struct _EmailKeyValue {
83 * Structure to hold information about RDN.
85 typedef struct _Rdn Rdn;
93 * Retrieve address group item for update.
94 * \param group Group to print.
95 * \param array GHashTAble of item_group, or <i>NULL</i> if none created.
97 void ldapsvr_retrieve_item_group(ItemGroup *group, GHashTable *array) {
98 /* Not implemented in this release */
99 cm_return_if_fail(group != NULL);
103 * Create an initial EmailKeyValue structure
104 * \return empty structure
106 EmailKeyValue *emailkeyvalue_create() {
109 buf = g_new0(EmailKeyValue, 1);
117 * Create an initial AttrKeyValue structure
118 * \return empty structure
120 AttrKeyValue *attrkeyvalue_create() {
123 buf = g_new0(AttrKeyValue, 1);
130 * Free created AttrKeyValue structure
131 * \param akv AttrKeyValue structure to free
133 void attrkeyvalue_free(AttrKeyValue *akv) {
147 * Retrieve E-Mail address object for update.
148 * \param item ItemEmail to update.
149 * \return object, or <i>NULL</i> if none created.
151 EmailKeyValue *ldapsvr_retrieve_item_email(ItemEMail *item) {
152 EmailKeyValue *newItem;
153 cm_return_val_if_fail(item != NULL, NULL);
154 newItem = emailkeyvalue_create();
155 newItem->alias = g_strdup(ADDRITEM_NAME(item));
156 newItem->mail = g_strdup(item->address);
157 newItem->remarks = g_strdup(item->remarks);
162 * Retrieve user attribute object for update.
163 * \param item UserAttribute to update.
164 * \return object, or <i>NULL</i> if none created.
166 AttrKeyValue *ldapsvr_retrieve_attribute(UserAttribute *item) {
167 AttrKeyValue *newItem;
168 cm_return_val_if_fail(item != NULL, NULL);
169 newItem = attrkeyvalue_create();
170 newItem->key = g_strdup(item->name);
171 newItem->value = g_strdup(item->value);
176 * Retrieve person object for update.
177 * \param person ItemPerson to update.
178 * \param array GHashTable with user input.
179 * \return false if update is not needed, or true if update is needed.
181 gboolean ldapsvr_retrieve_item_person(ItemPerson *person, GHashTable *array) {
184 cm_return_val_if_fail(person != NULL, FALSE);
185 switch (person->status) {
186 case NONE: return FALSE;
187 case ADD_ENTRY: g_hash_table_insert(array, "status", "new"); break;
188 case UPDATE_ENTRY: g_hash_table_insert(array, "status", "update"); break;
189 case DELETE_ENTRY: g_hash_table_insert(array, "status", "delete"); break;
190 default: g_critical(_("ldapsvr_retrieve_item_person->Unknown status: %d"), person->status);
192 g_hash_table_insert(array, "uid", ADDRITEM_ID(person));
193 g_hash_table_insert(array, "cn", ADDRITEM_NAME(person));
194 g_hash_table_insert(array, "givenName", person->firstName);
195 g_hash_table_insert(array, "sn", person->lastName);
196 g_hash_table_insert(array, "nickName", person->nickName);
197 g_hash_table_insert(array, "dn", person->externalID);
198 g_hash_table_insert(array, "person", person);
199 node = person->listEMail;
202 EmailKeyValue *newEmail = ldapsvr_retrieve_item_email(node->data);
204 attr = g_list_append(attr, newEmail);
205 node = g_list_next(node);
207 g_hash_table_insert(array, "mail", attr);
208 node = person->listAttrib;
211 AttrKeyValue *newAttr = ldapsvr_retrieve_attribute(node->data);
213 attr = g_list_append(attr, newAttr);
214 node = g_list_next(node);
216 g_hash_table_insert(array, "attribute", attr);
221 * Print contents of contacts hashtable for debug.
222 * This function must be called with g_hash_table_foreach.
223 * \param key Key to process.
224 * \param data Data to process.
225 * \param fd Output stream.
227 void ldapsvr_print_contacts_hashtable(gpointer key, gpointer data, gpointer fd) {
228 gchar *keyName = (gchar *) key;
231 if (g_ascii_strcasecmp("mail", keyName) == 0) {
232 node = (GList *) data;
234 EmailKeyValue *item = node->data;
235 if (debug_get_mode()) {
236 debug_print("\t\talias = %s\n", item->alias?item->alias:"null");
237 debug_print("\t\tmail = %s\n", item->mail?item->mail:"null");
238 debug_print("\t\tremarks = %s\n", item->remarks?item->remarks:"null");
241 FILE *stream = (FILE *) fd;
242 fprintf(stream, "\t\talias = %s\n", item->alias?item->alias:"null");
243 fprintf(stream, "\t\tmail = %s\n", item->mail?item->mail:"null");
244 fprintf(stream, "\t\tremarks = %s\n", item->remarks?item->remarks:"null");
246 node = g_list_next(node);
249 else if (g_ascii_strcasecmp("attribute", keyName) == 0) {
250 node = (GList *) data;
252 AttrKeyValue *item = node->data;
253 if (debug_get_mode()) {
254 debug_print("\t\t%s = %s\n", item->key?item->key:"null",
255 item->value?item->value:"null");
258 FILE *stream = (FILE *) fd;
259 fprintf(stream, "\t\t%s = %s\n", item->key?item->key:"null",
260 item->value?item->value:"null");
262 node = g_list_next(node);
266 if (debug_get_mode())
267 debug_print("\t\t%s = %s\n", keyName?keyName:"null", data?(gchar *)data:"null");
269 FILE *stream = (FILE *) fd;
270 fprintf(stream, "\t\t%s = %s\n", keyName?keyName:"null", data?(gchar *)data:"null");
276 * Free list of changed contacts
278 * \param list List of GHashTable
280 void ldapsvr_free_hashtable(GList *list) {
283 g_hash_table_destroy(tmp->data);
285 tmp = g_list_next(tmp);
292 * Get person object from cache
294 * \param server Resource to LDAP
295 * \param uid PersonID in cache
296 * \return person object, or <i>NULL</i> if fail
298 ItemPerson *ldapsvr_get_contact(LdapServer *server, gchar *uid) {
300 cm_return_val_if_fail(server != NULL || uid != NULL, NULL);
301 aio = addrcache_get_object(server->addressCache, uid);
303 if(aio->type == ITEMTYPE_PERSON) {
304 return (ItemPerson *) aio;
311 * Create an initial Rdn structure
313 * \return empty structure
318 buf = g_new0(Rdn, 1);
319 buf->attribute = NULL;
326 * Free a created Rdn structure
327 * \param rdn Structure to free
329 void rdn_free(Rdn *rdn) {
330 if (rdn->attribute) {
331 g_free(rdn->attribute);
332 rdn->attribute = NULL;
347 * update Rdn structure
349 * \param rdn Rdn structure to update
350 * \param head Uniq part of dn
351 * \param tail Common part of dn
353 void update_rdn(Rdn *rdn, gchar *head, gchar *tail) {
354 rdn->value = g_strdup(head);
355 rdn->new_dn = g_strdup_printf("%s=%s%s", rdn->attribute, head, tail);
359 * Deside if dn needs to be changed
361 * \param hash GHashTable with user input.
362 * \param dn dn for current object
363 * \return Rdn structure
365 Rdn *ldapsvr_modify_dn(GHashTable *hash, gchar *dn) {
367 gchar *pos, *compare;
370 cm_return_val_if_fail(hash != NULL || dn != NULL, NULL);
372 pos = g_strstr_len(dn, strlen(dn), "=");
376 compare = g_strndup(dn, pos - dn);
379 rest = g_strstr_len(pos, strlen(pos), ",");
380 val = g_strndup(pos, rest - pos);
387 rdn->value = g_strdup(val);
388 rdn->attribute = g_strdup(compare);
390 if (strcmp("mail", rdn->attribute) == 0) {
391 GList *list = g_hash_table_lookup(hash, rdn->attribute);
393 EmailKeyValue *item = list->data;
394 compare = g_strdup((gchar *) item->mail);
395 if (strcmp(compare, rdn->value) == 0) {
396 update_rdn(rdn, compare, rest);
400 list = g_list_next(list);
402 /* if compare and rdn->attribute are equal then last email removed/empty */
403 if (strcmp(compare, rdn->attribute) != 0) {
404 /* RDN changed. Find new */
405 update_rdn(rdn, compare, rest);
410 /* We cannot remove dn */
417 compare = g_hash_table_lookup(hash, rdn->attribute);
418 /* if compare and rdn->attribute are equal then dn removed/empty */
419 if (strcmp(compare, rdn->attribute) != 0) {
420 update_rdn(rdn, compare, rest);
424 /* We cannot remove dn */
434 * This macro is borrowed from the Balsa project
435 * Creates a LDAPMod structure
437 * \param mods Empty LDAPMod structure
438 * \param modarr Array with values to insert
439 * \param op Operation to perform on LDAP
440 * \param attr Attribute to insert
441 * \param strv Empty array which is NULL terminated
442 * \param val Value for attribute
444 #define SETMOD(mods,modarr,op,attr,strv,val) \
445 do { (mods) = &(modarr); (modarr).mod_type=attr; (modarr).mod_op=op;\
446 (strv)[0]=(val); (modarr).mod_values=strv; \
450 * Creates a LDAPMod structure
452 * \param mods Empty LDAPMod structure
453 * \param modarr Array with values to insert
454 * \param op Operation to perform on LDAP
455 * \param attr Attribute to insert
456 * \param strv Array with values to insert. Must be NULL terminated
458 #define SETMODS(mods,modarr,op,attr,strv) \
459 do { (mods) = &(modarr); (modarr).mod_type=attr; \
460 (modarr).mod_op=op; (modarr).mod_values=strv; \
465 * Clean up, close LDAP connection, and refresh cache
467 * \param ld Resource to LDAP
468 * \param server AddressBook resource
469 * \param contact GHashTable with current changed object
471 void clean_up(LDAP *ld, LdapServer *server, GHashTable *contact) {
473 ldapsvr_get_contact(server, g_hash_table_lookup(contact , "uid"));
476 person->status = NONE;
477 displayName = g_hash_table_lookup(contact, "displayName");
479 person->nickName = g_strdup(displayName);
481 if (server->retVal != LDAPRC_SUCCESS) {
484 addrcache_remove_person(server->addressCache, person);
486 g_critical(N_("ldapsvr_update_book: Could not clean cache\n"));
488 addritem_free_item_person(res);
492 ldapsvr_disconnect(ld);
496 * Get cn attribute from dn
498 * \param dn Distinguesh Name for current object
499 * \return AttrKeyValue, or <i>NULL</i> if none created
501 AttrKeyValue *get_cn(gchar *dn) {
507 cm_return_val_if_fail(dn != NULL, NULL);
509 cn = attrkeyvalue_create();
510 start = g_strstr_len(dn, strlen(dn), "cn");
512 attrkeyvalue_free(cn);
515 end = g_strstr_len(start, strlen(start), ",");
516 item = g_strndup(start, end - start);
518 attrkeyvalue_free(cn);
521 key_value = g_strsplit(item, "=", 2);
522 cn->key = g_strdup(key_value[0]);
523 cn->value = g_strdup(key_value[1]);
524 g_strfreev(key_value);
530 * Get mail attribute from dn
532 * \param dn Distinguesh Name for current object
533 * \return AttrKeyValue, or <i>NULL</i> if none created
535 AttrKeyValue *get_mail(gchar *dn) {
541 cm_return_val_if_fail(dn != NULL, NULL);
543 mail = attrkeyvalue_create();
544 start = g_strstr_len(dn, strlen(dn), "mail");
546 attrkeyvalue_free(mail);
549 end = g_strstr_len(start, strlen(start), ",");
550 item = g_strndup(start, end - start);
552 attrkeyvalue_free(mail);
555 key_value = g_strsplit(item, "=", 2);
556 mail->key = g_strdup(key_value[0]);
557 mail->value = g_strdup(key_value[1]);
558 g_strfreev(key_value);
564 * Get ou or o attribute from dn
566 * \param dn Distinguesh Name for current object
567 * \return AttrKeyValue, or <i>NULL</i> if none created
569 AttrKeyValue *get_ou(gchar *dn) {
576 cm_return_val_if_fail(dn != NULL, NULL);
577 ou = attrkeyvalue_create();
578 start = g_strstr_len(dn, strlen(dn), ",o=");
580 start = g_strstr_len(dn, strlen(dn), ",ou=");
582 attrkeyvalue_free(ou);
586 end = g_strstr_len(start, strlen(start), ",");
587 item = g_strndup(start, end - start);
589 attrkeyvalue_free(ou);
592 key_value = g_strsplit(item, "=", 2);
593 ou->key = g_strdup(key_value[0]);
594 ou->value = g_strdup(key_value[1]);
595 g_strfreev(key_value);
601 * Print the contents of a LDAPMod structure for debuging purposes
603 * \param mods LDAPMod structure
605 void ldapsvr_print_ldapmod(LDAPMod *mods[]) {
609 cm_return_if_fail(mods != NULL);
610 g_printerr( "Type\n");
611 for (i = 0; NULL != mods[i]; i++) {
612 LDAPMod *mod = (LDAPMod *) mods[i];
614 switch (mod->mod_op) {
615 case LDAP_MOD_ADD: mod_op = g_strdup("ADD"); break;
616 case LDAP_MOD_REPLACE: mod_op = g_strdup("MODIFY"); break;
617 case LDAP_MOD_DELETE: mod_op = g_strdup("DELETE"); break;
618 default: mod_op = g_strdup("UNKNOWN");
620 g_printerr( "Operation: %s\tType:%s\nValues:\n", mod_op, mod->mod_type);
621 vals = mod->mod_vals.modv_strvals;
623 g_printerr( "\t%s\n", *vals++);
629 * Make a compare for every new value we want to store in the
630 * directory with the current values. Great tool for debugging
631 * against invalid syntax in attributes
633 * \param ld AddressBook resource
634 * \param dn dn for the entry
635 * \param cnt Number of attributes to compare
636 * \param mods LDAPMod structure
638 void ldapsvr_compare_attr(LDAP *ld, gchar *dn, gint cnt, LDAPMod *mods[]) {
641 #ifdef OPEN_LDAP_API_AT_LEAST_3000
647 cm_return_if_fail(ld != NULL || dn != NULL || cnt >= 0 || mods != NULL);
648 for (i = 0; i < cnt; i++) {
649 gchar *value = g_strdup(mods[i]->mod_vals.modv_strvals[0]);
650 if (!value || strcmp(value, "") == 0)
651 value = g_strdup("thisisonlyadummy");
653 #ifdef OPEN_LDAP_API_AT_LEAST_3000
656 val.bv_len = strlen(value);
658 rc = ldap_compare_ext_s(ld, dn, mods[i]->mod_type, &val, NULL, NULL);
662 /* This is deprecated as of OpenLDAP-2.3.0 */
663 rc = ldap_compare_s(ld, dn, mods[i]->mod_type, value);
667 g_printerr("ldap_compare for (%s:%s)\" failed[0x%x]: %s\n",
668 mods[i]->mod_type, value, rc, ldap_err2string(rc));
674 * compare attribute to LDAP in case of LDAP_INAPPROPRIATE_MATCHING
676 * \param ld AddressBook resource
677 * \param server Reference to server
678 * \param dn dn for the entry
679 * \param attr Attribute
680 * \param value New value
681 * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
683 int ldapsvr_compare_manual_attr(LDAP *ld, LdapServer *server, gchar *dn, char *attr, char *value) {
684 LDAPMessage *res, *e = NULL;
686 struct berval **vals;
694 cm_return_val_if_fail(ld != NULL || server != NULL || attr != NULL, -1);
695 ctl = server->control;
699 filter = g_strdup_printf("(&(mail=%s)(%s=*))", mail->value, attr);
700 attrkeyvalue_free(mail);
703 rc = ldap_search_ext_s(ld, ctl->baseDN, LDAP_SCOPE_ONELEVEL, filter, NULL, 0, NULL, NULL, NULL, 0, &res);
706 g_printerr("ldap_search for attr=%s\" failed[0x%x]: %s\n",attr, rc, ldap_err2string(rc));
710 e = ldap_first_entry(ld, res);
711 /* entry has this attribute */
713 attribute = ldap_first_attribute( ld, e, &ber );
716 if( ( vals = ldap_get_values_len( ld, e, attr ) ) != NULL ) {
717 for( i = 0; vals[i] != NULL; i++ ) {
718 debug_print("Compare: %s=%s\n", attr, vals[i]->bv_val);
719 /* attribute has same value */
720 if (strcmp(vals[i]->bv_val, value) == 0)
722 /* attribute has new value */
724 retVal = LDAP_MOD_REPLACE;
727 ldap_value_free_len(vals);
730 retVal = LDAP_MOD_DELETE;
735 ldap_memfree(attribute);
737 /* entry does not have this attribute */
739 /* Only add if this is a real attribute */
741 retVal = LDAP_MOD_ADD;
742 /* This is dummy value used to avoid ldap_compare error */
755 * Deside which kind of operation is required to handle
756 * updating the specified attribute
758 * \param ld AddressBook resource
759 * \param server Reference to server
760 * \param dn dn for the entry
761 * \param attr Attribute
762 * \param value New value
763 * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
765 int ldapsvr_deside_operation(LDAP *ld, LdapServer *server, char *dn, char *attr, char *value) {
767 gboolean dummy = FALSE;
769 #ifdef OPEN_LDAP_API_AT_LEAST_3000
775 cm_return_val_if_fail(ld != NULL || server != NULL || dn != NULL || attr != NULL, -1);
778 /* value containing empty string cause invalid syntax. A bug in
779 * the LDAP library? Therefore we add a dummy value
781 if (strcmp(value,"") == 0) {
782 value = g_strdup("thisisonlyadummy");
786 #ifdef OPEN_LDAP_API_AT_LEAST_3000
789 val.bv_len = strlen(value);
791 rc = ldap_compare_ext_s(ld, dn, attr, &val, NULL, NULL);
795 /* This is deprecated as of OpenLDAP-2.3.0 */
796 rc = ldap_compare_s(ld, dn, attr, value);
800 debug_print("ldap_compare for (%s:%s)\" error_code[0x%x]: %s\n",
801 attr, value, rc, ldap_err2string(rc));
803 case LDAP_COMPARE_FALSE:
805 return LDAP_MOD_DELETE;
807 return LDAP_MOD_REPLACE;
808 case LDAP_COMPARE_TRUE: return -1;
809 case LDAP_NO_SUCH_ATTRIBUTE: return LDAP_MOD_ADD;
810 /* LDAP_INAPPROPRIATE_MATCHING needs extensive testing because I
811 * am not aware off the condition causing this return value!
813 case LDAP_INAPPROPRIATE_MATCHING:
816 return ldapsvr_compare_manual_attr(ld, server, dn, attr, value);
817 case LDAP_UNDEFINED_TYPE: return -2;
818 case LDAP_INVALID_SYNTAX: return -2;
824 * Check if attribute is part of the current search criteria
826 * \param list Array containing attributes in the current search criteria
827 * \param attr Attribute to check
828 * \result <i>TRUE</i> if attribute is found in the current search criteria
830 gboolean ldapsvr_check_search_attributes(char **list, char *attr) {
832 if (strcmp(*list++, attr) == 0)
839 * Deside which other attributes needs updating
841 * \param ld LDAP resource
842 * \param server AddressBook resource
843 * \param dn dn for the entry
844 * \param contact GHashTable with information for the current contact
846 void ldapsvr_handle_other_attributes(LDAP *ld, LdapServer *server, char *dn, GHashTable *contact) {
848 gboolean CHECKED_ATTRIBUTE[ATTRIBUTE_SIZE + 1];
849 LDAPMod *mods[ATTRIBUTE_SIZE + 1];
850 LDAPMod modarr[ATTRIBUTE_SIZE];
852 char *attr[ATTRIBUTE_SIZE + 1][2];
855 cm_return_if_fail(server != NULL || dn != NULL || contact != NULL);
856 for (i = 0; i <= ATTRIBUTE_SIZE; i++) {
857 CHECKED_ATTRIBUTE[i] = FALSE;
858 attr[i][0] = attr[i][1] = NULL;
860 node = g_hash_table_lookup(contact , "attribute");
862 AttrKeyValue *item = node->data;
864 int index = get_attribute_index(item->key);
866 debug_print("Found other attribute: %s = %s\n",
867 item->key?item->key:"null", item->value?item->value:"null");
868 mod_op = ldapsvr_deside_operation(ld, server, dn, item->key, item->value);
869 /* Only consider attributes which we no how to handle.
870 * Set to TRUE in CHECKED_ATTRIBUTE array to indicate no further action
873 CHECKED_ATTRIBUTE[index] = TRUE;
874 node = g_list_next(node);
877 if (mod_op == LDAP_MOD_DELETE) {
878 /* Setting param to NULL instructs OpenLDAP to remove any
879 * value stored for this attribute and remove the attribute
880 * completely. Should multiple instances of an attribute be
881 * allowed in the future param is required to have the value
882 * store for the attribute which is going to be deleted
886 if (mod_op == LDAP_MOD_REPLACE && strcmp(item->value, "") == 0) {
887 /* Having an empty string is considered a syntax error in
888 * ldap. E.g attributes with empty strings are not allowed
889 * in which case we treate this as a request for deleting
892 mod_op = LDAP_MOD_DELETE;
895 if (mod_op == LDAP_MOD_ADD && strcmp(item->value, "") == 0) {
896 /* Adding an empty string is considered a syntax error in
897 * ldap. E.g attributes with empty strings are not allowed
898 * in which case we silently refuse to add this entry
902 SETMOD(mods[cnt], modarr[cnt], mod_op, g_strdup(item->key), attr[cnt], g_strdup(item->value));
904 CHECKED_ATTRIBUTE[index] = TRUE;
908 node = g_list_next(node);
910 char **attribs = ldapctl_full_attribute_array(server->control);
911 for (i = 0; i < ATTRIBUTE_SIZE; i++) {
912 /* Attributes which holds no information are to be removed */
913 if (CHECKED_ATTRIBUTE[i] == FALSE) {
914 /* Only consider those attributes which is currently part of the search criteria.
915 * If attributes are not part of the search criteria they would seem to hold
916 * no information since their values will not be populated in the GUI
918 if (!strcmp(ATTRIBUTE[i], "jpegPhoto")) {
919 debug_print("not updating jpegPhoto\n");
922 if (ldapsvr_check_search_attributes(attribs, (char *) ATTRIBUTE[i])) {
923 mod_op = ldapsvr_deside_operation(ld, server, dn, (char *) ATTRIBUTE[i], "");
924 if (mod_op == LDAP_MOD_DELETE) {
925 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_DELETE, g_strdup((char *) ATTRIBUTE[i]), attr[cnt], NULL);
931 ldapctl_free_attribute_array(attribs);
933 if (debug_get_mode())
934 ldapsvr_print_ldapmod(mods);
935 server->retVal = LDAPRC_SUCCESS;
936 rc = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
939 case LDAP_ALREADY_EXISTS:
940 server->retVal = LDAPRC_ALREADY_EXIST;
943 g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n", dn, rc, ldap_err2string(rc));
945 server->retVal = LDAPRC_STRONG_AUTH;
947 server->retVal = LDAPRC_NAMING_VIOLATION;
951 char **attribs = ldapctl_full_attribute_array(server->control);
952 for (i = 0; i < ATTRIBUTE_SIZE; i++) {
953 if (!strcmp(ATTRIBUTE[i], "jpegPhoto")) {
954 debug_print("not updating jpegPhoto\n");
957 if (ldapsvr_check_search_attributes(attribs, (char *) ATTRIBUTE[i])) {
958 if (CHECKED_ATTRIBUTE[i] == FALSE) {
959 AddrItemObject *aio = addrcache_get_object(server->addressCache, g_hash_table_lookup(contact , "uid"));
960 ItemPerson *person = (ItemPerson *) aio;
961 addritem_person_remove_attribute(person, (const gchar *) ATTRIBUTE[i]);
965 ldapctl_free_attribute_array(attribs);
970 * Add new contact to LDAP
972 * \param server AddressBook resource
973 * \param contact GHashTable with object to add
975 void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
976 gchar *email = NULL, *param = NULL;
978 LDAPMod *mods[MODSIZE];
981 char *cn[] = {NULL, NULL};
982 char *displayName[] = {NULL, NULL};
983 char *givenName[] = {NULL, NULL};
985 char *sn[] = {NULL, NULL};
986 char *org[] = {NULL, NULL};
987 char *obj[] = {/*"top",*/ "person", "organizationalPerson", "inetOrgPerson", NULL};
990 AttrKeyValue *ou, *commonName;
995 cm_return_if_fail(server != NULL || contact != NULL);
996 node = g_hash_table_lookup(contact , "mail");
998 EmailKeyValue *newEmail = node->data;
999 email = g_strdup(newEmail->mail);
1001 if (email == NULL) {
1002 server->retVal = LDAPRC_NODN;
1003 clean_up(ld, server, contact);
1006 base_dn = g_strdup_printf("mail=%s,%s",
1007 email, server->control->baseDN?server->control->baseDN:"null");
1010 ldapsvr_get_contact(server, g_hash_table_lookup(contact , "uid"));
1011 person->externalID = g_strdup(base_dn);
1012 debug_print("dn: %s\n", base_dn);
1013 ld = ldapsvr_connect(server->control);
1015 clean_up(ld, server, contact);
1016 debug_print("no ldap found\n");
1019 SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "objectClass", obj);
1021 ou = get_ou(base_dn);
1023 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, g_strdup(ou->key), org, g_strdup(ou->value));
1025 attrkeyvalue_free(ou);
1028 commonName = get_cn(base_dn);
1029 if (commonName == NULL) {
1030 param = g_hash_table_lookup(contact , "cn");
1032 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "cn", cn, param);
1035 clean_up(ld, server, contact);
1036 debug_print("no CN found\n");
1041 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, g_strdup(commonName->key), cn, g_strdup(commonName->value));
1043 param = g_hash_table_lookup(contact , "cn");
1044 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "displayName", displayName, param);
1045 g_hash_table_insert(contact, "displayName", param);
1046 attrkeyvalue_free(commonName);
1049 param = g_hash_table_lookup(contact , "givenName");
1051 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "givenName", givenName, param);
1054 mailList = g_hash_table_lookup(contact , "mail");
1057 tmp = g_malloc(sizeof(*tmp) * (g_list_length(mailList)+1));
1060 EmailKeyValue *item = mailList->data;
1061 *tmp++ = g_strdup((gchar *) item->mail);
1062 mailList = g_list_next(mailList);
1065 SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "mail", mail);
1068 param = g_hash_table_lookup(contact, "sn");
1070 param = g_strdup(N_("Some SN"));
1071 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "sn", sn, param);
1074 if (debug_get_mode()) {
1075 ldapsvr_print_ldapmod(mods);
1077 server->retVal = LDAPRC_SUCCESS;
1078 rc = ldap_add_ext_s(ld, base_dn, mods, NULL, NULL);
1081 case LDAP_ALREADY_EXISTS:
1082 server->retVal = LDAPRC_ALREADY_EXIST;
1085 g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",
1086 base_dn, rc, ldap_err2string(rc));
1088 server->retVal = LDAPRC_STRONG_AUTH;
1090 server->retVal = LDAPRC_NAMING_VIOLATION;
1093 ldapsvr_handle_other_attributes(ld, server, base_dn, contact);
1095 clean_up(ld, server, contact);
1099 * Update contact to LDAP
1101 * \param server AddressBook resource
1102 * \param contact GHashTable with object to update
1104 void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
1106 LDAPMod *mods[MODSIZE];
1110 Rdn *NoRemove = NULL;
1111 char *cn[] = {NULL, NULL};
1112 char *givenName[] = {NULL, NULL};
1114 char *sn[] = {NULL, NULL};
1118 cm_return_if_fail(server != NULL || contact != NULL);
1119 ld = ldapsvr_connect(server->control);
1121 clean_up(ld, server, contact);
1124 dn = g_hash_table_lookup(contact, "dn");
1127 clean_up(ld, server, contact);
1130 NoRemove = ldapsvr_modify_dn(contact, dn);
1132 /* We are trying to change RDN */
1133 gchar *newRdn = g_strdup_printf("%s=%s", NoRemove->attribute, NoRemove->value);
1135 #ifdef OPEN_LDAP_API_AT_LEAST_3000
1137 int rc = ldap_rename_s(ld, dn, newRdn, NULL, 1, NULL, NULL);
1141 /* This is deprecated as of OpenLDAP-2.3.0 */
1142 int rc = ldap_modrdn2_s(ld, dn, newRdn, 1);
1146 if(rc != LDAP_SUCCESS) {
1147 if (rc == LDAP_ALREADY_EXISTS) {
1148 /* We are messing with a contact with more than one listed email
1149 * address and the email we are changing is not the one used for dn
1151 /* It needs to be able to handle renaming errors to an already defined
1152 * dn. For now we just refuse the update. It will be caught later on as
1153 * a LDAPRC_NAMING_VIOLATION error.
1157 g_printerr("Current dn: %s\n", dn);
1158 g_printerr("new dn: %s\n", newRdn);
1159 g_printerr("LDAP Error(ldap_modrdn2_s) failed[0x%x]: %s\n", rc, ldap_err2string(rc));
1161 clean_up(ld, server, contact);
1166 ItemPerson *person = g_hash_table_lookup(contact, "person");
1168 dn = g_strdup(NoRemove->new_dn);
1169 g_hash_table_replace(contact, "dn", dn);
1171 g_free(person->externalID);
1172 person->externalID = dn;
1177 server->retVal = LDAPRC_NODN;
1178 clean_up(ld, server, contact);
1181 param = g_hash_table_lookup(contact , "cn");
1182 mod_op = ldapsvr_deside_operation(ld, server, dn, "displayName", param);
1183 if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("cn", NoRemove->attribute) != 0)) {
1184 if (mod_op == LDAP_MOD_DELETE) {
1185 /* Setting param to NULL instructs OpenLDAP to remove any
1186 * value stored for this attribute and remove the attribute
1187 * completely. Should multiple instances of an attribute be
1188 * allowed in the future param is required to have the value
1189 * store for the attribute which is going to be deleted
1193 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
1194 /* Having an empty string is considered a syntax error in
1195 * ldap. E.g attributes with empty strings are not allowed
1196 * in which case we treate this as a request for deleting
1199 mod_op = LDAP_MOD_DELETE;
1202 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
1203 /* Adding an empty string is considered a syntax error in
1204 * ldap. E.g attributes with empty strings are not allowed
1205 * in which case we silently refuse to add this entry
1209 SETMOD(mods[cnt], modarr[cnt], mod_op, "displayName", cn, param);
1211 g_hash_table_insert(contact, "displayName", param);
1214 param = g_hash_table_lookup(contact , "givenName");
1215 mod_op = ldapsvr_deside_operation(ld, server, dn, "givenName", param);
1216 if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("givenName", NoRemove->attribute) != 0)) {
1217 if (mod_op == LDAP_MOD_DELETE) {
1218 /* Setting param to NULL instructs OpenLDAP to remove any
1219 * value stored for this attribute and remove the attribute
1220 * completely. Should multiple instances of an attribute be
1221 * allowed in the future param is required to have the value
1222 * store for the attribute which is going to be deleted
1226 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
1227 /* Having an empty string is considered a syntax error in
1228 * ldap. E.g attributes with empty strings are not allowed
1229 * in which case we treate this as a request for deleting
1232 mod_op = LDAP_MOD_DELETE;
1235 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
1236 /* Adding an empty string is considered a syntax error in
1237 * ldap. E.g attributes with empty strings are not allowed
1238 * in which case we silently refuse to add this entry
1242 SETMOD(mods[cnt], modarr[cnt], mod_op, "givenName", givenName, param);
1246 mailList = g_hash_table_lookup(contact , "mail");
1248 debug_print("# of mail: %d\n", g_list_length(mailList));
1249 if (!(strcmp("mail", NoRemove->attribute) == 0 && g_list_length(mailList) == 1)) {
1251 tmp = g_malloc(sizeof(*tmp) * (g_list_length(mailList)+1));
1254 EmailKeyValue *item = mailList->data;
1255 *tmp++ = g_strdup((gchar *) item->mail);
1256 mailList = g_list_next(mailList);
1260 * At least one email address is required
1261 * in which case it will always be a replace
1263 SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "mail", mail);
1269 * an error condition since at least one email adress
1270 * is required. Should never occur though.
1273 param = g_hash_table_lookup(contact , "sn");
1274 mod_op = ldapsvr_deside_operation(ld, server, dn, "sn", param);
1275 if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("sn", NoRemove->attribute) != 0)) {
1276 if (mod_op == LDAP_MOD_DELETE) {
1277 /* Setting param to NULL instructs OpenLDAP to remove any
1278 * value stored for this attribute and remove the attribute
1279 * completely. Should multiple instances of an attribute be
1280 * allowed in the future param is required to have the value
1281 * store for the attribute which is going to be deleted
1285 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
1286 /* Having an empty string is considered a syntax error in
1287 * ldap. E.g attributes with empty strings are not allowed
1288 * in which case we treate this as a request for deleting
1291 mod_op = LDAP_MOD_DELETE;
1294 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
1295 /* Adding an empty string is considered a syntax error in
1296 * ldap. E.g attributes with empty strings are not allowed
1297 * in which case we silently refuse to add this entry
1301 SETMOD(mods[cnt], modarr[cnt], mod_op, "sn", sn, param);
1305 debug_print("newDN: %s\n", dn);
1308 server->retVal = LDAPRC_SUCCESS;
1312 rc = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
1314 g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",
1315 dn, rc, ldap_err2string(rc));
1316 server->retVal = LDAPRC_NAMING_VIOLATION;
1321 ldapsvr_handle_other_attributes(ld, server, dn, contact);
1322 /* If we do not make changes persistent at this point then changes
1323 * will be lost if the user makes new search on the same server since
1324 * changes are only present in Claws' internal cache. This issue has to
1325 * be solved in addressbook.c since this involves access to structures
1326 * which are only accessible in addressbook.c */
1327 clean_up(ld, server, contact);
1331 * Delete contact from LDAP
1333 * \param server AddressBook resource
1334 * \param contact GHashTable with object to delete
1336 void ldapsvr_delete_contact(LdapServer *server, GHashTable *contact) {
1341 cm_return_if_fail(server != NULL || contact != NULL);
1342 ld = ldapsvr_connect(server->control);
1344 clean_up(ld, server, contact);
1347 dn = g_hash_table_lookup(contact, "dn");
1349 clean_up(ld, server, contact);
1352 server->retVal = LDAPRC_SUCCESS;
1353 rc = ldap_delete_ext_s(ld, dn, NULL, NULL);
1355 g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",
1356 dn, rc, ldap_err2string(rc));
1357 server->retVal = LDAPRC_NODN;
1359 clean_up(ld, server, contact);
1363 * Update any changes to the server.
1365 * \param server AddressBook resource.
1366 * \param person ItemPerson holding user input.
1368 void ldapsvr_update_book(LdapServer *server, ItemPerson *item) {
1370 GHashTable *contact = NULL;
1371 GList *contacts = NULL, *head = NULL;
1373 cm_return_if_fail(server != NULL);
1374 debug_print("updating ldap addressbook\n");
1376 contact = g_hash_table_new(g_str_hash, g_str_equal);
1378 gboolean result = ldapsvr_retrieve_item_person(item, contact);
1379 debug_print("Found contact to update: %s\n", result? "Yes" : "No");
1381 if (debug_get_mode()) {
1382 addritem_print_item_person(item, stdout);
1384 contacts = g_list_append(contacts, contact);
1388 ItemFolder *folder = server->addressCache->rootFolder;
1389 node = folder->listFolder;
1392 AddrItemObject *aio = node->data;
1394 if (aio->type == ITEMTYPE_FOLDER) {
1395 ItemFolder *folder = (ItemFolder *) aio;
1396 GList *persons = folder->listPerson;
1398 AddrItemObject *aio = persons->data;
1400 if (aio->type == ITEMTYPE_PERSON) {
1401 ItemPerson *item = (ItemPerson *) aio;
1402 gboolean result = ldapsvr_retrieve_item_person(item, contact);
1403 debug_print("Found contact to update: %s\n", result? "Yes" : "No");
1405 if (debug_get_mode()) {
1406 gchar *uid = g_hash_table_lookup(contact, "uid");
1407 item = ldapsvr_get_contact(server, uid);
1408 addritem_print_item_person(item, stdout);
1410 contacts = g_list_append(contacts, contact);
1414 persons = g_list_next(persons);
1419 g_printerr("\t\tpid : ???\n");
1421 node = g_list_next(node);
1426 if (debug_get_mode()) {
1428 debug_print("Contacts which must be updated in LDAP:\n");
1430 debug_print("\tContact:\n");
1431 g_hash_table_foreach(contacts->data,
1432 ldapsvr_print_contacts_hashtable, stderr);
1433 contacts = g_list_next(contacts);
1436 if (contacts == NULL)
1440 contact = (GHashTable *) contacts->data;
1441 status = (gchar *) g_hash_table_lookup(contact, "status");
1443 status = g_strdup("NULL");
1444 if (g_ascii_strcasecmp(status, "new") == 0) {
1445 ldapsvr_add_contact(server, contact);
1447 else if (g_ascii_strcasecmp(status, "update") == 0) {
1448 ldapsvr_update_contact(server, contact);
1450 else if (g_ascii_strcasecmp(status, "delete") == 0) {
1451 ldapsvr_delete_contact(server, contact);
1454 g_critical(_("ldapsvr_update_book->Unknown status: %s\n"), status);
1455 contacts = g_list_next(contacts);
1457 ldapsvr_free_hashtable(head);
1460 #endif /* USE_LDAP */