2.10.0 unleashed
[claws.git] / src / ldapupdate.c
blobeca943eb2722759a76641ef22e1ce24a24f7e7d8
1 /*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2007 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 2 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, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * Functions necessary to access LDAP servers.
25 * Outstanding bugs
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
36 #ifdef HAVE_CONFIG_H
37 # include "config.h"
38 #endif
40 #ifdef USE_LDAP
42 #include <glib.h>
43 #include <glib/gi18n.h>
44 #include <sys/time.h>
45 #include <string.h>
46 #include <ldap.h>
47 #include <lber.h>
49 #include "ldapupdate.h"
50 #include "mgutils.h"
51 #include "addritem.h"
52 #include "addrcache.h"
53 #include "ldapctrl.h"
54 #include "ldapquery.h"
55 #include "ldapserver.h"
56 #include "ldaputil.h"
57 #include "utils.h"
58 #include "adbookbase.h"
60 /**
61 * Structure to hold user defined attributes
62 * from contacts
64 typedef struct _AttrKeyValue AttrKeyValue;
65 struct _AttrKeyValue {
66 gchar *key;
67 gchar *value;
70 /**
71 * Structure to hold contact information.
72 * Each addressbook will have 0..N contacts.
74 typedef struct _EmailKeyValue EmailKeyValue;
75 struct _EmailKeyValue {
76 gchar *mail;
77 gchar *alias;
78 gchar *remarks;
81 /**
82 * Structure to hold information about RDN.
84 typedef struct _Rdn Rdn;
85 struct _Rdn {
86 gchar *attribute;
87 gchar *value;
88 gchar *new_dn;
91 /**
92 * Retrieve address group item for update.
93 * \param group Group to print.
94 * \param array GHashTAble of item_group, or <i>NULL</i> if none created.
96 void ldapsvr_retrieve_item_group(ItemGroup *group, GHashTable *array) {
97 /* Not implemented in this release */
98 g_return_if_fail(group != NULL);
102 * Create an initial EmailKeyValue structure
103 * \return empty structure
105 EmailKeyValue *emailkeyvalue_create() {
106 EmailKeyValue *buf;
108 buf = g_new0(EmailKeyValue, 1);
109 buf->alias = NULL;
110 buf->mail = NULL;
111 buf->remarks = NULL;
112 return buf;
116 * Create an initial AttrKeyValue structure
117 * \return empty structure
119 AttrKeyValue *attrkeyvalue_create() {
120 AttrKeyValue *buf;
122 buf = g_new0(AttrKeyValue, 1);
123 buf->key = NULL;
124 buf->value = NULL;
125 return buf;
129 * Retrieve E-Mail address object for update.
130 * \param item ItemEmail to update.
131 * \return object, or <i>NULL</i> if none created.
133 EmailKeyValue *ldapsvr_retrieve_item_email(ItemEMail *item) {
134 EmailKeyValue *newItem;
135 g_return_val_if_fail(item != NULL, NULL);
136 newItem = emailkeyvalue_create();
137 newItem->alias = g_strdup(ADDRITEM_NAME(item));
138 newItem->mail = g_strdup(item->address);
139 newItem->remarks = g_strdup(item->remarks);
140 return newItem;
144 * Retrieve user attribute object for update.
145 * \param item UserAttribute to update.
146 * \return object, or <i>NULL</i> if none created.
148 AttrKeyValue *ldapsvr_retrieve_attribute(UserAttribute *item) {
149 AttrKeyValue *newItem;
150 g_return_val_if_fail(item != NULL, NULL);
151 newItem = attrkeyvalue_create();
152 newItem->key = g_strdup(item->name);
153 newItem->value = g_strdup(item->value);
154 return newItem;
158 * Retrieve person object for update.
159 * \param person ItemPerson to update.
160 * \param array GHashTable with user input.
161 * \return false if update is not needed, or true if update is needed.
163 gboolean ldapsvr_retrieve_item_person(ItemPerson *person, GHashTable *array) {
164 GList *node, *attr;
166 g_return_val_if_fail(person != NULL, FALSE);
167 switch (person->status) {
168 case NONE: return FALSE;
169 case ADD_ENTRY: g_hash_table_insert(array, "status", "new"); break;
170 case UPDATE_ENTRY: g_hash_table_insert(array, "status", "update"); break;
171 case DELETE_ENTRY: g_hash_table_insert(array, "status", "delete"); break;
172 default: g_critical(_("ldapsvr_retrieve_item_person->Unknown status: %d"), person->status);
174 g_hash_table_insert(array, "uid", ADDRITEM_ID(person));
175 g_hash_table_insert(array, "cn", ADDRITEM_NAME(person));
176 g_hash_table_insert(array, "givenName", person->firstName);
177 g_hash_table_insert(array, "sn", person->lastName);
178 g_hash_table_insert(array, "nickName", person->nickName);
179 g_hash_table_insert(array, "dn", person->externalID);
180 node = person->listEMail;
181 attr = NULL;
182 while (node) {
183 EmailKeyValue *newEmail = ldapsvr_retrieve_item_email(node->data);
184 if (newEmail)
185 attr = g_list_append(attr, newEmail);
186 node = g_list_next(node);
188 g_hash_table_insert(array, "mail", attr);
189 /* Not implemented in this release.
190 node = person->listAttrib;
191 attr = NULL;
192 while (node) {
193 AttrKeyValue *newAttr = ldapsvr_retrieve_attribute(node->data)
194 if (newAttr)
195 attr = g_list_append(attr, newAttr);
196 node = g_list_next(node);
198 g_hash_table_insert(array, "attribute", attr);
200 return TRUE;
204 * Print contents of contacts hashtable for debug.
205 * This function must be called with g_hash_table_foreach.
206 * \param key Key to process.
207 * \param data Data to process.
208 * \param fd Output stream.
210 void ldapsvr_print_contacts_hashtable(gpointer key, gpointer data, gpointer fd) {
211 gchar *keyName = (gchar *) key;
212 GList *node;
214 if (g_ascii_strcasecmp("mail", keyName) == 0) {
215 node = (GList *) data;
216 while (node) {
217 EmailKeyValue *item = node->data;
218 if (debug_get_mode()) {
219 debug_print("\t\talias = %s\n", item->alias);
220 debug_print("\t\tmail = %s\n", item->mail);
221 debug_print("\t\tremarks = %s\n", item->remarks);
223 else if (fd) {
224 FILE *stream = (FILE *) fd;
225 fprintf(stream, "\t\talias = %s\n", item->alias);
226 fprintf(stream, "\t\tmail = %s\n", item->mail);
227 fprintf(stream, "\t\tremarks = %s\n", item->remarks);
229 node = g_list_next(node);
232 else if (g_ascii_strcasecmp("attribute", keyName) == 0) {
233 node = (GList *) data;
234 while (node) {
235 AttrKeyValue *item = node->data;
236 if (debug_get_mode()) {
237 debug_print("\t\t%s = %s\n", item->key, item->value);
239 else if (fd) {
240 FILE *stream = (FILE *) fd;
241 fprintf(stream, "\t\t%s = %s\n", item->key, item->value);
243 node = g_list_next(node);
246 else {
247 if (debug_get_mode())
248 debug_print("\t\t%s = %s\n", keyName, (gchar *) data);
249 else if (fd) {
250 FILE *stream = (FILE *) fd;
251 fprintf(stream, "\t\t%s = %s\n", keyName, (gchar *) data);
257 * Free list of changed contacts
259 * \param list List of GHashTable
261 void ldapsvr_free_hashtable(GList *list) {
262 while (list) {
263 g_hash_table_destroy(list->data);
264 list = g_list_next(list);
266 g_list_free(list);
270 * Get person object from cache
272 * \param server Resource to LDAP
273 * \param uid PersonID in cache
274 * \return person object, or <i>NULL</i> if fail
276 ItemPerson *ldapsvr_get_contact(LdapServer *server, gchar *uid) {
277 AddrItemObject *aio;
278 g_return_val_if_fail(server != NULL || uid != NULL, NULL);
279 aio = addrcache_get_object(server->addressCache, uid);
280 if (aio) {
281 if(aio->type == ITEMTYPE_PERSON) {
282 return (ItemPerson *) aio;
285 return NULL;
289 * Connect to LDAP server.
290 * \param ctl Control object to process.
291 * \return LDAP Resource to LDAP.
293 LDAP *ldapsvr_connect(LdapControl *ctl) {
294 LDAP *ld = NULL;
295 gint rc;
296 gint version;
297 gchar *uri = NULL;
299 g_return_val_if_fail(ctl != NULL, NULL);
301 uri = g_strdup_printf("ldap%s://%s:%d",
302 ctl->enableSSL?"s":"",
303 ctl->hostName, ctl->port);
304 ldap_initialize(&ld, uri);
305 g_free(uri);
307 if (ld == NULL)
308 return NULL;
310 debug_print("connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port);
312 #ifdef USE_LDAP_TLS
313 /* Handle TLS */
314 version = LDAP_VERSION3;
315 rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
316 if (rc == LDAP_OPT_SUCCESS) {
317 ctl->version = LDAP_VERSION3;
320 if (ctl->version == LDAP_VERSION3) {
321 if (ctl->enableTLS && !ctl->enableSSL) {
322 rc = ldap_start_tls_s(ld, NULL, NULL);
324 if (rc != LDAP_SUCCESS) {
325 fprintf(stderr, "LDAP Error(tls): ldap_simple_bind_s: %s\n",
326 ldap_err2string(rc));
327 return NULL;
331 #endif
333 /* Bind to the server, if required */
334 if (ctl->bindDN) {
335 if (* ctl->bindDN != '\0') {
336 rc = claws_ldap_simple_bind_s(ld, ctl->bindDN, ctl->bindPass);
337 if (rc != LDAP_SUCCESS) {
338 fprintf(stderr, "bindDN: %s, bindPass: %s\n", ctl->bindDN, ctl->bindPass);
339 fprintf(stderr, "LDAP Error(bind): ldap_simple_bind_s: %s\n",
340 ldap_err2string(rc));
341 return NULL;
345 return ld;
349 * Disconnect to LDAP server.
350 * \param ld Resource to LDAP.
352 void ldapsvr_disconnect(LDAP *ld) {
353 /* Disconnect */
354 g_return_if_fail(ld != NULL);
355 ldap_unbind_ext(ld, NULL, NULL);
359 * Create an initial Rdn structure
361 * \return empty structure
363 Rdn *rdn_create() {
364 Rdn *buf;
366 buf = g_new0(Rdn, 1);
367 buf->attribute = NULL;
368 buf->value = NULL;
369 buf->new_dn = NULL;
370 return buf;
374 * update Rdn structure
376 * \param rdn Rdn structure to update
377 * \param head Uniq part of dn
378 * \param tail Common part of dn
380 void update_rdn(Rdn *rdn, gchar *head, gchar *tail) {
381 rdn->value = g_strdup(head);
382 rdn->new_dn = g_strdup_printf("mail=%s%s", head, tail);
386 * Deside if dn needs to be changed
388 * \param hash GHashTable with user input.
389 * \param dn dn for current object
390 * \return Rdn structure
392 Rdn *ldapsvr_modify_dn(GHashTable *hash, gchar *dn) {
393 Rdn *rdn;
394 gchar *pos, *compare;
395 gchar *rest;
396 gchar *val;
397 g_return_val_if_fail(hash != NULL || dn != NULL, NULL);
399 pos = g_strstr_len(dn, strlen(dn), "=");
400 compare = g_strndup(dn, pos - dn);
402 if (!pos)
403 return NULL;
404 pos++;
405 rest = g_strstr_len(pos, strlen(pos), ",");
406 val = g_strndup(pos, rest - pos);
407 if (val == NULL) {
408 if (compare)
409 g_free(compare);
410 return NULL;
412 rdn = rdn_create();
413 rdn->value = g_strdup(val);
414 rdn->attribute = g_strdup(compare);
415 g_free(val);
416 if (strcmp("mail", rdn->attribute) == 0) {
417 GList *list = g_hash_table_lookup(hash, rdn->attribute);
418 while (list) {
419 EmailKeyValue *item = list->data;
420 compare = g_strdup((gchar *) item->mail);
421 if (strcmp(compare, rdn->value) == 0) {
422 update_rdn(rdn, compare, rest);
423 g_free(compare);
424 return rdn;
426 list = g_list_next(list);
428 /* if compare and rdn->attribute are equal then last email removed/empty */
429 if (strcmp(compare, rdn->attribute) != 0) {
430 /* RDN changed. Find new */
431 update_rdn(rdn, compare, rest);
432 g_free(compare);
433 return rdn;
435 else {
436 /* We cannot remove dn */
437 g_free(compare);
438 return NULL;
441 else {
442 compare = g_hash_table_lookup(hash, rdn->attribute);
443 /* if compare and rdn->attribute are equal then dn removed/empty */
444 if (strcmp(compare, rdn->attribute) != 0) {
445 update_rdn(rdn, compare, rest);
446 return rdn;
448 else {
449 /* We cannot remove dn */
450 return NULL;
453 return NULL;
457 * This macro is borrowed from the Balsa project
458 * Creates a LDAPMod structure
460 * \param mods Empty LDAPMod structure
461 * \param modarr Array with values to insert
462 * \param op Operation to perform on LDAP
463 * \param attr Attribute to insert
464 * \param strv Empty array which is NULL terminated
465 * \param val Value for attribute
467 #define SETMOD(mods,modarr,op,attr,strv,val) \
468 do { (mods) = &(modarr); (modarr).mod_type=attr; (modarr).mod_op=op;\
469 (strv)[0]=(val); (modarr).mod_values=strv; \
470 } while(0)
473 * Creates a LDAPMod structure
475 * \param mods Empty LDAPMod structure
476 * \param modarr Array with values to insert
477 * \param op Operation to perform on LDAP
478 * \param attr Attribute to insert
479 * \param strv Array with values to insert. Must be NULL terminated
481 #define SETMODS(mods,modarr,op,attr,strv) \
482 do { (mods) = &(modarr); (modarr).mod_type=attr; \
483 (modarr).mod_op=op; (modarr).mod_values=strv; \
484 } while(0)
485 #define MODSIZE 10
488 * Clean up, close LDAP connection, and refresh cache
490 * \param ld Resource to LDAP
491 * \param server AddressBook resource
492 * \param contact GHashTable with current changed object
494 void clean_up(LDAP *ld, LdapServer *server, GHashTable *contact) {
495 ItemPerson *person =
496 ldapsvr_get_contact(server, g_hash_table_lookup(contact , "uid"));
497 if (person) {
498 gchar *displayName;
499 person->status = NONE;
500 displayName = g_hash_table_lookup(contact, "displayName");
501 if (displayName)
502 person->nickName = g_strdup(displayName);
504 if (server->retVal != LDAPRC_SUCCESS) {
505 if (person) {
506 ItemPerson *res =
507 addrcache_remove_person(server->addressCache, person);
508 if (!res)
509 g_critical(N_("ldapsvr_update_book: Could not clean cache\n"));
510 else
511 addritem_free_item_person(res);
514 if (ld)
515 ldapsvr_disconnect(ld);
516 /* ldapsvr_force_refresh(server);
517 ldapsvr_free_all_query(server);*/
521 * Get cn attribute from dn
523 * \param dn Distinguesh Name for current object
524 * \return AttrKeyValue, or <i>NULL</i> if none created
526 AttrKeyValue *get_cn(gchar *dn) {
527 AttrKeyValue *cn;
528 gchar *start;
529 gchar *end;
530 gchar *item;
531 gchar **key_value;
532 g_return_val_if_fail(dn != NULL, NULL);
534 cn = attrkeyvalue_create();
535 start = g_strstr_len(dn, strlen(dn), "cn");
536 if (start == NULL)
537 return NULL;
538 end = g_strstr_len(start, strlen(start), ",");
539 item = g_strndup(start, end - start);
540 if (item == NULL)
541 return NULL;
542 key_value = g_strsplit(item, "=", 2);
543 cn->key = g_strdup(key_value[0]);
544 cn->value = g_strdup(key_value[1]);
545 g_strfreev(key_value);
546 g_free(item);
547 return cn;
551 * Get ou or o attribute from dn
553 * \param dn Distinguesh Name for current object
554 * \return AttrKeyValue, or <i>NULL</i> if none created
556 AttrKeyValue *get_ou(gchar *dn) {
557 AttrKeyValue *ou;
558 gchar *start;
559 gchar *end;
560 gchar *item;
561 gchar **key_value;
563 g_return_val_if_fail(dn != NULL, NULL);
564 ou = attrkeyvalue_create();
565 start = g_strstr_len(dn, strlen(dn), ",o=");
566 if (start == NULL)
567 start = g_strstr_len(dn, strlen(dn), ",ou=");
568 if (start == NULL)
569 return NULL;
570 start++;
571 end = g_strstr_len(start, strlen(start), ",");
572 item = g_strndup(start, end - start);
573 if (item == NULL)
574 return NULL;
575 key_value = g_strsplit(item, "=", 2);
576 ou->key = g_strdup(key_value[0]);
577 ou->value = g_strdup(key_value[1]);
578 g_strfreev(key_value);
579 g_free(item);
580 return ou;
584 * Print the contents of a LDAPMod structure for debuging purposes
586 * \param mods LDAPMod structure
588 void ldapsvr_print_ldapmod(LDAPMod *mods[]) {
589 gchar *mod_op;
590 int i;
592 g_return_if_fail(mods != NULL);
593 fprintf( stderr, "Type\n");
594 for (i = 0; NULL != mods[i]; i++) {
595 LDAPMod *mod = (LDAPMod *) mods[i];
596 gchar **vals;
597 switch (mod->mod_op) {
598 case LDAP_MOD_ADD: mod_op = g_strdup("ADD"); break;
599 case LDAP_MOD_REPLACE: mod_op = g_strdup("MODIFY"); break;
600 case LDAP_MOD_DELETE: mod_op = g_strdup("DELETE"); break;
601 default: mod_op = g_strdup("UNKNOWN");
603 fprintf( stderr, "Operation: %s\tType:%s\nValues:\n", mod_op, mod->mod_type);
604 vals = mod->mod_vals.modv_strvals;
605 while (*vals) {
606 fprintf( stderr, "\t%s\n", *vals++);
612 * Make a compare for every new value we want to store in the
613 * directory with the current values. Great tool for debugging
614 * against invalid syntax in attributes
616 * \param ld AddressBook resource
617 * \param dn dn for the entry
618 * \param cnt Number of attributes to compare
619 * \param mods LDAPMod structure
621 void ldapsvr_compare_attr(LDAP *ld, gchar *dn, gint cnt, LDAPMod *mods[]) {
622 int i, rc;
624 g_return_if_fail(ld != NULL || dn != NULL || cnt >= 0 || mods != NULL);
625 for (i = 0; i < cnt; i++) {
626 rc = ldap_compare_s(ld, dn, mods[i]->mod_type, mods[i]->mod_vals.modv_strvals[0]);
627 fprintf(stderr, "ldap_compare for (%s:%s)\" failed[0x%x]: %s\n",
628 mods[i]->mod_type, mods[i]->mod_vals.modv_strvals[0], rc, ldap_err2string(rc));
633 * Add new contact to LDAP
635 * \param server AddressBook resource
636 * \param contact GHashTable with object to add
638 void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
639 gchar *email = NULL, *param = NULL;
640 LDAP *ld = NULL;
641 LDAPMod *mods[MODSIZE];
642 LDAPMod modarr[7];
643 gint cnt = 0;
644 char *cn[] = {NULL, NULL};
645 char *displayName[] = {NULL, NULL};
646 char *givenName[] = {NULL, NULL};
647 char **mail = NULL;
648 char *sn[] = {NULL, NULL};
649 char *org[] = {NULL, NULL};
650 char *obj[] = {/*"top",*/ "person", "organizationalPerson", "inetOrgPerson", NULL};
651 int rc=0;
652 GList *node;
653 AttrKeyValue *ou, *commonName;
654 ItemPerson *person;
655 gchar *base_dn;
656 GList *mailList;
658 g_return_if_fail(server != NULL || contact != NULL);
659 node = g_hash_table_lookup(contact , "mail");
660 if (node) {
661 EmailKeyValue *newEmail = node->data;
662 email = g_strdup(newEmail->mail);
664 if (email == NULL) {
665 server->retVal = LDAPRC_NODN;
666 clean_up(ld, server, contact);
667 return;
669 base_dn = g_strdup_printf("mail=%s,%s", email, server->control->baseDN);
670 g_free(email);
671 person =
672 ldapsvr_get_contact(server, g_hash_table_lookup(contact , "uid"));
673 person->externalID = g_strdup(base_dn);
674 debug_print("dn: %s\n", base_dn);
675 ld = ldapsvr_connect(server->control);
676 if (ld == NULL) {
677 clean_up(ld, server, contact);
678 debug_print("no ldap found\n");
679 return;
681 SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "objectClass", obj);
682 cnt++;
683 ou = get_ou(base_dn);
684 if (ou != NULL) {
685 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, ou->key, org, ou->value);
686 cnt++;
689 commonName = get_cn(base_dn);
690 if (commonName == NULL) {
691 param = g_hash_table_lookup(contact , "cn");
692 if (param) {
693 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "cn", cn, param);
695 else {
696 clean_up(ld, server, contact);
697 debug_print("no CN found\n");
698 return;
701 else {
702 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, commonName->key, cn, commonName->value);
703 cnt++;
704 param = g_hash_table_lookup(contact , "cn");
705 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "displayName", displayName, param);
706 g_hash_table_insert(contact, "displayName", param);
708 cnt++;
709 param = g_hash_table_lookup(contact , "givenName");
710 if (param) {
711 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "givenName", givenName, param);
712 cnt++;
714 mailList = g_hash_table_lookup(contact , "mail");
715 if (mailList) {
716 char **tmp;
717 tmp = g_malloc(sizeof(*tmp) * (g_list_length(mailList)+1));
718 mail = tmp;
719 while (mailList) {
720 EmailKeyValue *item = mailList->data;
721 *tmp++ = g_strdup((gchar *) item->mail);
722 mailList = g_list_next(mailList);
724 *tmp = NULL;
725 SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "mail", mail);
726 cnt++;
728 param = g_hash_table_lookup(contact, "sn");
729 if (param == NULL)
730 param = g_strdup(N_("Some SN"));
731 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "sn", sn, param);
732 cnt++;
733 mods[cnt] = NULL;
734 if (debug_get_mode()) {
735 ldapsvr_print_ldapmod(mods);
737 server->retVal = LDAPRC_SUCCESS;
738 rc = ldap_add_ext_s(ld, base_dn, mods, NULL, NULL);
739 if (rc) {
740 switch (rc) {
741 case LDAP_ALREADY_EXISTS:
742 server->retVal = LDAPRC_ALREADY_EXIST;
743 break;
744 default:
745 fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",base_dn, rc, ldap_err2string(rc));
746 if (rc == 0x8)
747 server->retVal = LDAPRC_STRONG_AUTH;
748 else
749 server->retVal = LDAPRC_NAMING_VIOLATION;
752 g_free(base_dn);
753 clean_up(ld, server, contact);
757 * Deside which kind of operation is required to handle
758 * updating the specified attribute
760 * \param ld AddressBook resource
761 * \param dn dn for the entry
762 * \param attr Attribute
763 * \param value New value
764 * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
766 int ldapsvr_deside_operation(LDAP *ld, char *dn, char *attr, char *value) {
767 int rc;
768 gboolean dummy = FALSE;
770 g_return_val_if_fail(ld != NULL || dn != NULL || attr != NULL, -1);
771 if (value == NULL)
772 return -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");
778 dummy = TRUE;
780 rc = ldap_compare_s(ld, dn, attr, value);
781 debug_print("ldap_compare for (%s:%s)\" error_code[0x%x]: %s\n",
782 attr, value, rc, ldap_err2string(rc));
783 switch (rc) {
784 case LDAP_COMPARE_FALSE:
785 if (dummy)
786 return LDAP_MOD_DELETE;
787 else
788 return LDAP_MOD_REPLACE;
789 case LDAP_COMPARE_TRUE: return -1;
790 case LDAP_NO_SUCH_ATTRIBUTE: return LDAP_MOD_ADD;
791 case LDAP_UNDEFINED_TYPE: return -2;
792 case LDAP_INVALID_SYNTAX: return -2;
793 default: return -2;
798 * Update contact to LDAP
800 * \param server AddressBook resource
801 * \param contact GHashTable with object to update
803 void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
804 LDAP *ld = NULL;
805 LDAPMod *mods[MODSIZE];
806 LDAPMod modarr[4];
807 gint cnt = 0;
808 gchar *param, *dn;
809 Rdn *NoRemove = NULL;
810 char *cn[] = {NULL, NULL};
811 char *givenName[] = {NULL, NULL};
812 char **mail = NULL;
813 char *sn[] = {NULL, NULL};
814 GList *mailList;
815 int mod_op;
817 g_return_if_fail(server != NULL || contact != NULL);
818 ld = ldapsvr_connect(server->control);
819 if (ld == NULL) {
820 clean_up(ld, server, contact);
821 return;
823 dn = g_hash_table_lookup(contact, "dn");
824 if (dn == NULL) {
825 clean_up(ld, server, contact);
826 return;
828 NoRemove = ldapsvr_modify_dn(contact, dn);
829 if (NoRemove) {
830 /* We are trying to change RDN */
831 gchar *newRdn = g_strdup_printf("%s=%s", NoRemove->attribute, NoRemove->value);
832 int rc = ldap_modrdn2_s(ld, dn, newRdn, 1);
833 if(rc != LDAP_SUCCESS) {
834 if (rc == LDAP_ALREADY_EXISTS) {
835 /* We are messing with a contact with more than one listed email
836 * address and the email we are changing is not the one used for dn
838 /* It needs to be able to handle renaming errors to an already defined
839 * dn. For now we just refuse the update. It will be caught later on as
840 * a LDAPRC_NAMING_VIOLATION error.
843 else {
844 fprintf(stderr, "Current dn: %s\n", dn);
845 fprintf(stderr, "new dn: %s\n", newRdn);
846 fprintf(stderr, "LDAP Error(ldap_modrdn2_s) failed[0x%x]: %s\n", rc, ldap_err2string(rc));
847 g_free(newRdn);
848 clean_up(ld, server, contact);
849 return;
852 else {
853 g_free(newRdn);
854 dn = g_strdup(NoRemove->new_dn);
857 else {
858 server->retVal = LDAPRC_NODN;
859 clean_up(ld, server, contact);
860 return;
862 param = g_hash_table_lookup(contact , "cn");
863 mod_op = ldapsvr_deside_operation(ld, dn, "displayName", param);
864 if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("cn", NoRemove->attribute) != 0)) {
865 if (mod_op == LDAP_MOD_DELETE) {
866 /* Setting param to NULL instructs OpenLDAP to remove any
867 * value stored for this attribute and remove the attribute
868 * completely. Should multiple instances of an attribute be
869 * allowed in the future param is required to have the value
870 * store for the attribute which is going to be deleted
872 param = NULL;
874 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
875 /* Having an empty string is considered a syntax error in
876 * ldap. E.g attributes with empty strings are not allowed
877 * in which case we treate this as a request for deleting
878 * the attribute.
880 mod_op = LDAP_MOD_DELETE;
881 param = NULL;
883 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
884 /* Adding an empty string is considered a syntax error in
885 * ldap. E.g attributes with empty strings are not allowed
886 * in which case we silently refuse to add this entry
889 else {
890 SETMOD(mods[cnt], modarr[cnt], mod_op, "displayName", cn, param);
891 cnt++;
892 g_hash_table_insert(contact, "displayName", param);
895 param = g_hash_table_lookup(contact , "givenName");
896 mod_op = ldapsvr_deside_operation(ld, dn, "givenName", param);
897 if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("givenName", NoRemove->attribute) != 0)) {
898 if (mod_op == LDAP_MOD_DELETE) {
899 /* Setting param to NULL instructs OpenLDAP to remove any
900 * value stored for this attribute and remove the attribute
901 * completely. Should multiple instances of an attribute be
902 * allowed in the future param is required to have the value
903 * store for the attribute which is going to be deleted
905 param = NULL;
907 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
908 /* Having an empty string is considered a syntax error in
909 * ldap. E.g attributes with empty strings are not allowed
910 * in which case we treate this as a request for deleting
911 * the attribute.
913 mod_op = LDAP_MOD_DELETE;
914 param = NULL;
916 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
917 /* Adding an empty string is considered a syntax error in
918 * ldap. E.g attributes with empty strings are not allowed
919 * in which case we silently refuse to add this entry
922 else {
923 SETMOD(mods[cnt], modarr[cnt], mod_op, "givenName", givenName, param);
924 cnt++;
927 mailList = g_hash_table_lookup(contact , "mail");
928 if (mailList) {
929 debug_print("# of mail: %d\n", g_list_length(mailList));
930 if (!(strcmp("mail", NoRemove->attribute) == 0 && g_list_length(mailList) == 1)) {
931 char **tmp;
932 tmp = g_malloc(sizeof(*tmp) * (g_list_length(mailList)+1));
933 mail = tmp;
934 while (mailList) {
935 EmailKeyValue *item = mailList->data;
936 *tmp++ = g_strdup((gchar *) item->mail);
937 mailList = g_list_next(mailList);
939 *tmp = NULL;
941 * At least one email address is required
942 * in which case it will always be a replace
944 SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "mail", mail);
945 cnt++;
948 else {
950 * an error condition since at least one email adress
951 * is required. Should never occur though.
954 param = g_hash_table_lookup(contact , "sn");
955 mod_op = ldapsvr_deside_operation(ld, dn, "sn", param);
956 if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("sn", NoRemove->attribute) != 0)) {
957 if (mod_op == LDAP_MOD_DELETE) {
958 /* Setting param to NULL instructs OpenLDAP to remove any
959 * value stored for this attribute and remove the attribute
960 * completely. Should multiple instances of an attribute be
961 * allowed in the future param is required to have the value
962 * store for the attribute which is going to be deleted
964 param = NULL;
966 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
967 /* Having an empty string is considered a syntax error in
968 * ldap. E.g attributes with empty strings are not allowed
969 * in which case we treate this as a request for deleting
970 * the attribute.
972 mod_op = LDAP_MOD_DELETE;
973 param = NULL;
975 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
976 /* Adding an empty string is considered a syntax error in
977 * ldap. E.g attributes with empty strings are not allowed
978 * in which case we silently refuse to add this entry
981 else {
982 SETMOD(mods[cnt], modarr[cnt], mod_op, "sn", sn, param);
983 cnt++;
986 debug_print("newDN: %s\n", dn);
987 if (NoRemove)
988 g_free(NoRemove);
989 server->retVal = LDAPRC_SUCCESS;
990 if (cnt > 0) {
991 int rc;
992 mods[cnt] = NULL;
993 rc = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
994 if (rc) {
995 fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",
996 dn, rc, ldap_err2string(rc));
997 server->retVal = LDAPRC_NAMING_VIOLATION;
999 if (mail)
1000 g_free(mail);
1002 /* If we do not make changes persistent at this point then changes
1003 * will be lost if the user makes new search on the same server since
1004 * changes are only present in Claws' internal cache. This issue has to
1005 * be solved in addressbook.c since this involves access to structures
1006 * which are only accessible in addressbook.c */
1007 clean_up(ld, server, contact);
1011 * Delete contact from LDAP
1013 * \param server AddressBook resource
1014 * \param contact GHashTable with object to delete
1016 void ldapsvr_delete_contact(LdapServer *server, GHashTable *contact) {
1017 LDAP *ld = NULL;
1018 gchar *dn;
1019 int rc;
1021 g_return_if_fail(server != NULL || contact != NULL);
1022 ld = ldapsvr_connect(server->control);
1023 if (ld == NULL) {
1024 clean_up(ld, server, contact);
1025 return;
1027 dn = g_hash_table_lookup(contact, "dn");
1028 if (dn == NULL) {
1029 clean_up(ld, server, contact);
1030 return;
1032 server->retVal = LDAPRC_SUCCESS;
1033 rc = ldap_delete_ext_s(ld, dn, NULL, NULL);
1034 if (rc) {
1035 fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",
1036 dn, rc, ldap_err2string(rc));
1037 server->retVal = LDAPRC_NODN;
1039 clean_up(ld, server, contact);
1043 * Update any changes to the server.
1045 * \param server AddressBook resource.
1046 * \param person ItemPerson holding user input.
1048 void ldapsvr_update_book(LdapServer *server, ItemPerson *item) {
1049 GList *node = NULL;
1050 GHashTable *contact = NULL;
1051 GList *contacts = NULL, *head = NULL;
1053 g_return_if_fail(server != NULL);
1054 debug_print("updating ldap addressbook\n");
1056 contact = g_hash_table_new(g_str_hash, g_str_equal);
1057 if (item) {
1058 gboolean result = ldapsvr_retrieve_item_person(item, contact);
1059 debug_print("Found contact to update: %s\n", result? "Yes" : "No");
1060 if (result) {
1061 if (debug_get_mode()) {
1062 addritem_print_item_person(item, stdout);
1064 contacts = g_list_append(contacts, contact);
1067 else {
1068 ItemFolder *folder = server->addressCache->rootFolder;
1069 node = folder->listFolder;
1070 if (node) {
1071 while (node) {
1072 AddrItemObject *aio = node->data;
1073 if (aio) {
1074 if (aio->type == ITEMTYPE_FOLDER) {
1075 ItemFolder *folder = (ItemFolder *) aio;
1076 GList *persons = folder->listPerson;
1077 while (persons) {
1078 AddrItemObject *aio = persons->data;
1079 if (aio) {
1080 if (aio->type == ITEMTYPE_PERSON) {
1081 ItemPerson *item = (ItemPerson *) aio;
1082 gboolean result = ldapsvr_retrieve_item_person(item, contact);
1083 debug_print("Found contact to update: %s\n", result? "Yes" : "No");
1084 if (result) {
1085 if (debug_get_mode()) {
1086 gchar *uid = g_hash_table_lookup(contact, "uid");
1087 item = ldapsvr_get_contact(server, uid);
1088 addritem_print_item_person(item, stdout);
1090 contacts = g_list_append(contacts, contact);
1094 persons = g_list_next(persons);
1098 else {
1099 fprintf(stderr, "\t\tpid : ???\n");
1101 node = g_list_next(node);
1105 head = contacts;
1106 if (debug_get_mode()) {
1107 if (contacts)
1108 debug_print("Contacts which must be updated in LDAP:\n");
1109 while (contacts) {
1110 debug_print("\tContact:\n");
1111 g_hash_table_foreach(contacts->data,
1112 ldapsvr_print_contacts_hashtable, stderr);
1113 contacts = g_list_next(contacts);
1116 if (contacts == NULL)
1117 contacts = head;
1118 while (contacts) {
1119 gchar *status;
1120 contact = (GHashTable *) contacts->data;
1121 status = (gchar *) g_hash_table_lookup(contact, "status");
1122 if (status == NULL)
1123 status = g_strdup("NULL");
1124 if (g_ascii_strcasecmp(status, "new") == 0) {
1125 ldapsvr_add_contact(server, contact);
1127 else if (g_ascii_strcasecmp(status, "update") == 0) {
1128 ldapsvr_update_contact(server, contact);
1130 else if (g_ascii_strcasecmp(status, "delete") == 0) {
1131 ldapsvr_delete_contact(server, contact);
1133 else
1134 g_critical(_("ldapsvr_update_book->Unknown status: %s\n"), status);
1135 contacts = g_list_next(contacts);
1137 ldapsvr_free_hashtable(head);
1140 #endif /* USE_LDAP */
1143 * End of Source.