2007-06-06 [colin] 2.9.2cvs43
[claws.git] / src / ldapupdate.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2003-2007 Michael Rasmussen and the Claws Mail team
4  *
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.
9  *
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.
14  *
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.
18  */
19
20 /*
21  * Functions necessary to access LDAP servers.
22  */
23
24 /*
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
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #  include "config.h"
38 #endif
39
40 #ifdef USE_LDAP
41
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>
48
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"
59
60 /**
61  * Structure to hold user defined attributes
62  * from contacts
63  */
64 typedef struct _AttrKeyValue AttrKeyValue;
65 struct _AttrKeyValue {
66         gchar *key;
67         gchar *value;
68 };
69
70 /**
71  * Structure to hold contact information.
72  * Each addressbook will have 0..N contacts.
73  */
74 typedef struct _EmailKeyValue EmailKeyValue;
75 struct _EmailKeyValue {
76         gchar *mail;
77         gchar *alias;
78         gchar *remarks;
79 };
80
81 /**
82  * Structure to hold information about RDN.
83  */
84 typedef struct _Rdn Rdn;
85 struct _Rdn {
86         gchar *attribute;
87         gchar *value;
88         gchar *new_dn;
89 };
90
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.
95  */
96 void ldapsvr_retrieve_item_group(ItemGroup *group, GHashTable *array) {
97         /* Not implemented in this release */
98         g_return_if_fail(group != NULL);
99 }
100
101 /**
102  * Create an initial EmailKeyValue structure
103  * \return empty structure
104  */
105 EmailKeyValue *emailkeyvalue_create() {
106         EmailKeyValue *buf;
107
108         buf = g_new0(EmailKeyValue, 1);
109         buf->alias = NULL;
110         buf->mail = NULL;
111         buf->remarks = NULL;
112         return buf;
113 }
114
115 /**
116  * Create an initial AttrKeyValue structure
117  * \return empty structure
118  */
119 AttrKeyValue *attrkeyvalue_create() {
120         AttrKeyValue *buf;
121
122         buf = g_new0(AttrKeyValue, 1);
123         buf->key = NULL;
124         buf->value = NULL;
125         return buf;
126 }
127
128 /**
129  * Retrieve E-Mail address object for update.
130  * \param item   ItemEmail to update.
131  * \return object, or <i>NULL</i> if none created.
132  */
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;
141 }
142
143 /**
144  * Retrieve user attribute object for update.
145  * \param item   UserAttribute to update.
146  * \return object, or <i>NULL</i> if none created.
147  */
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;
155 }
156
157 /**
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.
162  */
163 gboolean ldapsvr_retrieve_item_person(ItemPerson *person, GHashTable *array) {
164         GList *node, *attr;
165
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);
173         }
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);
187         }
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);
197         }
198         g_hash_table_insert(array, "attribute", attr);
199 */
200         return TRUE;
201 }
202
203 /**
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.
209  */
210 void ldapsvr_print_contacts_hashtable(gpointer key, gpointer data, gpointer fd) {
211         gchar *keyName = (gchar *) key;
212         GList *node;
213
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);
222                         }
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);
228                         }
229                         node = g_list_next(node);
230                 }
231         }
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);
238                         }
239                         else if (fd) {
240                                 FILE *stream = (FILE *) fd;
241                                 fprintf(stream, "\t\t%s = %s\n", item->key, item->value);
242                         }
243                         node = g_list_next(node);
244                 }
245         }
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);
252                 }
253         }
254 }
255
256 /**
257  * Free list of changed contacts
258  *
259  * \param list List of GHashTable
260  */
261 void ldapsvr_free_hashtable(GList *list) {
262         while (list) {
263                 g_hash_table_destroy(list->data);
264                 list = g_list_next(list);
265         }
266         g_list_free(list);
267 }
268
269 /**
270  * Get person object from cache
271  *
272  * \param server Resource to LDAP
273  * \param uid PersonID in cache
274  * \return person object, or <i>NULL</i> if fail
275  */
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;
283                 }
284         }
285         return NULL;
286 }
287
288 /**
289  * Connect to LDAP server.
290  * \param  ctl Control object to process.
291  * \return LDAP Resource to LDAP.
292  */
293 LDAP *ldapsvr_connect(LdapControl *ctl) {
294         LDAP *ld = NULL;
295         gint rc;
296         gint version;
297         gchar *uri = NULL;
298
299         g_return_val_if_fail(ctl != NULL, NULL);
300
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);
306
307         if (ld == NULL)
308                 return NULL;
309
310         debug_print("connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port);
311
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;
318         }
319
320         if (ctl->version == LDAP_VERSION3) {
321                 if (ctl->enableTLS && !ctl->enableSSL) {
322                         rc = ldap_start_tls_s(ld, NULL, NULL);
323                         
324                         if (rc != LDAP_SUCCESS) {
325                                 fprintf(stderr, "LDAP Error(tls): ldap_simple_bind_s: %s\n",
326                                         ldap_err2string(rc));
327                                 return NULL;
328                         }
329                 }
330         }
331 #endif
332
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;
342                         }
343                 }
344         }
345         return ld;
346 }
347
348 /**
349  * Disconnect to LDAP server.
350  * \param ld Resource to LDAP.
351  */
352 void ldapsvr_disconnect(LDAP *ld) {
353         /* Disconnect */
354         g_return_if_fail(ld != NULL);
355         ldap_unbind_ext(ld, NULL, NULL);
356 }
357
358 /**
359  * Create an initial Rdn structure
360  *
361  * \return empty structure
362  */
363 Rdn *rdn_create() {
364         Rdn *buf;
365
366         buf = g_new0(Rdn, 1);
367         buf->attribute = NULL;
368         buf->value = NULL;
369         buf->new_dn = NULL;
370         return buf;
371 }
372
373 /**
374  * update Rdn structure
375  *
376  * \param rdn Rdn structure to update
377  * \param head Uniq part of dn
378  * \param tail Common part of dn
379  */
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);
383 }
384
385 /**
386  * Deside if dn needs to be changed
387  *
388  * \param hash GHashTable with user input.
389  * \param dn dn for current object
390  * \return Rdn structure
391  */
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);
398         
399         pos = g_strstr_len(dn, strlen(dn), "=");
400         compare = g_strndup(dn, pos - dn);
401
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;
411         }
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;
425                         }
426                         list = g_list_next(list);
427                 }
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;
434                 }
435                 else {
436                         /* We cannot remove dn */
437                         g_free(compare);
438                         return NULL;
439                 }
440         }
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;
447                 }
448                 else {
449                         /* We cannot remove dn */
450                         return NULL;
451                 }
452         }
453         return NULL;
454 }
455
456 /**
457  * This macro is borrowed from the Balsa project
458  * Creates a LDAPMod structure
459  *
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
466  */
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)
471
472 /**
473  * Creates a LDAPMod structure
474  *
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
480  */
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
486
487 /**
488  * Clean up, close LDAP connection, and refresh cache
489  *
490  * \param ld Resource to LDAP
491  * \param server AddressBook resource
492  * \param contact GHashTable with current changed object
493  */
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);
503         }
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);
512                 }
513         }
514         if (ld)
515                 ldapsvr_disconnect(ld);
516 /*      ldapsvr_force_refresh(server);
517         ldapsvr_free_all_query(server);*/
518 }
519
520 /**
521  * Get cn attribute from dn
522  *
523  * \param dn Distinguesh Name for current object
524  * \return AttrKeyValue, or <i>NULL</i> if none created
525  */
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);
533         
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;
548 }
549
550 /**
551  * Get ou or o attribute from dn
552  *
553  * \param dn Distinguesh Name for current object
554  * \return AttrKeyValue, or <i>NULL</i> if none created
555  */
556 AttrKeyValue *get_ou(gchar *dn) {
557         AttrKeyValue *ou;
558         gchar *start;
559         gchar *end;
560         gchar *item;
561         gchar **key_value;
562         
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;
581 }
582
583 /**
584  * Print the contents of a LDAPMod structure for debuging purposes
585  *
586  * \param mods LDAPMod structure
587  */
588 void ldapsvr_print_ldapmod(LDAPMod *mods[]) {
589         gchar *mod_op;
590         int i;
591
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");
602                 }
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++);
607                 }
608         }
609 }
610
611 /**
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
615  *
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
620  */
621 void ldapsvr_compare_attr(LDAP *ld, gchar *dn, gint cnt, LDAPMod *mods[]) {
622         int i, rc;
623         
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));
629         }
630 }
631
632 /**
633  * Add new contact to LDAP
634  *
635  * \param server AddressBook resource
636  * \param contact GHashTable with object to add
637  */
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;
657
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);
663         }
664         if (email == NULL) {
665                 server->retVal = LDAPRC_NODN;
666                 clean_up(ld, server, contact);
667                 return;
668         }
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;
680         }
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++;
687         }
688         
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);
694                 }
695                 else {
696                         clean_up(ld, server, contact);
697                         debug_print("no CN found\n");
698                         return;
699                 }
700         }
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);
707         }
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++;
713         }
714         mailList = g_hash_table_lookup(contact , "mail");
715         if (mailList) {
716                 char **tmp;
717                 mail = g_malloc(sizeof(*mail));
718                 tmp = g_malloc(sizeof(*tmp));
719                 mail = tmp;
720                 while (mailList) {
721                         EmailKeyValue *item = mailList->data;
722                         *tmp++ = g_strdup((gchar *) item->mail);
723                         mailList = g_list_next(mailList);
724                 }
725                 *tmp = NULL;
726                 SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "mail", mail);
727                 cnt++;
728         }
729         param = g_hash_table_lookup(contact, "sn");
730         if (param == NULL)
731                 param = g_strdup(N_("Some SN"));
732         SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "sn", sn, param);
733         cnt++;
734         mods[cnt] = NULL;
735         if (debug_get_mode()) {
736                 ldapsvr_print_ldapmod(mods);
737         }
738         server->retVal = LDAPRC_SUCCESS;
739         rc = ldap_add_ext_s(ld, base_dn, mods, NULL, NULL);
740         if (rc) {
741                 switch (rc) {
742                         case LDAP_ALREADY_EXISTS: 
743                                 server->retVal = LDAPRC_ALREADY_EXIST;
744                                 break;
745                         default:
746                                 fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",base_dn, rc, ldap_err2string(rc));
747                                 if (rc == 0x8)
748                                         server->retVal = LDAPRC_STRONG_AUTH;
749                                 else
750                                         server->retVal = LDAPRC_NAMING_VIOLATION;
751                 }
752         }
753         g_free(base_dn);
754         clean_up(ld, server, contact);
755 }
756
757 /**
758  * Deside which kind of operation is required to handle
759  * updating the specified attribute
760  *
761  * \param ld AddressBook resource
762  * \param dn dn for the entry
763  * \param attr Attribute
764  * \param value New value
765  * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
766  */
767 int ldapsvr_deside_operation(LDAP *ld, char *dn, char *attr, char *value) {
768         int rc;
769         gboolean dummy = FALSE;
770
771         g_return_val_if_fail(ld != NULL || dn != NULL || attr != NULL, -1);
772         if (value == NULL)
773                 return -1;
774         /* value containing empty string cause invalid syntax. A bug in
775          * the LDAP library? Therefore we add a dummy value
776          */
777         if (strcmp(value,"") == 0) {
778                 value = g_strdup("thisisonlyadummy");
779                 dummy = TRUE;
780         }
781         rc = ldap_compare_s(ld, dn, attr, value);
782         debug_print("ldap_compare for (%s:%s)\" error_code[0x%x]: %s\n",
783         attr, value, rc, ldap_err2string(rc));
784         switch (rc) {
785                 case LDAP_COMPARE_FALSE: 
786                         if (dummy)
787                                 return LDAP_MOD_DELETE;
788                         else
789                                 return LDAP_MOD_REPLACE;
790                 case LDAP_COMPARE_TRUE: return -1;
791                 case LDAP_NO_SUCH_ATTRIBUTE: return LDAP_MOD_ADD;
792                 case LDAP_UNDEFINED_TYPE: return -2;
793                 case LDAP_INVALID_SYNTAX: return -2;
794                 default: return -2;
795         }
796 }
797
798 /**
799  * Update contact to LDAP
800  *
801  * \param server AddressBook resource
802  * \param contact GHashTable with object to update
803  */
804 void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
805         LDAP *ld = NULL;
806         LDAPMod *mods[MODSIZE];
807         LDAPMod modarr[4];
808         gint cnt = 0;
809         gchar *param, *dn;
810         Rdn *NoRemove = NULL;
811         char *cn[] = {NULL, NULL};
812         char *givenName[] = {NULL, NULL};
813         char **mail = NULL;
814         char *sn[] = {NULL, NULL};
815         GList *mailList;
816         int mod_op;
817
818         g_return_if_fail(server != NULL || contact != NULL);
819         ld = ldapsvr_connect(server->control);
820         if (ld == NULL) {
821                 clean_up(ld, server, contact);
822                 return;
823         }
824         dn = g_hash_table_lookup(contact, "dn");
825         if (dn == NULL) {
826                 clean_up(ld, server, contact);
827                 return;
828         }
829         NoRemove = ldapsvr_modify_dn(contact, dn);
830         if (NoRemove) {
831                 /* We are trying to change RDN */
832                 gchar *newRdn = g_strdup_printf("%s=%s", NoRemove->attribute, NoRemove->value);
833                 int rc = ldap_modrdn2_s(ld, dn, newRdn, 1);
834                 if(rc != LDAP_SUCCESS) {
835                         if (rc ==  LDAP_ALREADY_EXISTS) {
836                                 /* We are messing with a contact with more than one listed email
837                                  * address and the email we are changing is not the one used for dn
838                                  */
839                                 /* It needs to be able to handle renaming errors to an already defined
840                                  * dn. For now we just refuse the update. It will be caught later on as
841                                  * a LDAPRC_NAMING_VIOLATION error.
842                                  */
843                         }
844                         else {
845                                 fprintf(stderr, "Current dn: %s\n", dn);
846                                 fprintf(stderr, "new dn: %s\n", newRdn);
847                                 fprintf(stderr, "LDAP Error(ldap_modrdn2_s) failed[0x%x]: %s\n", rc, ldap_err2string(rc));
848                                 g_free(newRdn);
849                                 clean_up(ld, server, contact);
850                                 return;
851                         }
852                 }
853                 else {
854                         g_free(newRdn);
855                         dn = g_strdup(NoRemove->new_dn);
856                 }
857         }
858         else {
859                 server->retVal = LDAPRC_NODN;
860                 clean_up(ld, server, contact);
861                 return;
862         }
863         param = g_hash_table_lookup(contact , "cn");
864         mod_op = ldapsvr_deside_operation(ld, dn, "displayName", param);
865         if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("cn", NoRemove->attribute) != 0)) {
866                 if (mod_op == LDAP_MOD_DELETE) {
867                         /* Setting param to NULL instructs OpenLDAP to remove any
868                          * value stored for this attribute and remove the attribute
869                          * completely. Should multiple instances of an attribute be
870                          * allowed in the future param is required to have the value
871                          * store for the attribute which is going to be deleted
872                          */
873                         param = NULL;
874                 }
875                 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
876                         /* Having an empty string is considered a syntax error in
877                          * ldap. E.g attributes with empty strings are not allowed
878                          * in which case we treate this as a request for deleting
879                          * the attribute.
880                          */
881                         mod_op = LDAP_MOD_DELETE;
882                         param = NULL;
883                 }
884                 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
885                         /* Adding an empty string is considered a syntax error in
886                          * ldap. E.g attributes with empty strings are not allowed
887                          * in which case we silently refuse to add this entry
888                          */
889                 }
890                 else {
891                         SETMOD(mods[cnt], modarr[cnt], mod_op, "displayName", cn, param);
892                         cnt++;
893                         g_hash_table_insert(contact, "displayName", param);
894                 }
895         }
896         param = g_hash_table_lookup(contact , "givenName");
897         mod_op = ldapsvr_deside_operation(ld, dn, "givenName", param);
898         if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("givenName", NoRemove->attribute) != 0)) {
899                 if (mod_op == LDAP_MOD_DELETE) {
900                         /* Setting param to NULL instructs OpenLDAP to remove any
901                          * value stored for this attribute and remove the attribute
902                          * completely. Should multiple instances of an attribute be
903                          * allowed in the future param is required to have the value
904                          * store for the attribute which is going to be deleted
905                          */
906                         param = NULL;
907                 }
908                 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
909                         /* Having an empty string is considered a syntax error in
910                          * ldap. E.g attributes with empty strings are not allowed
911                          * in which case we treate this as a request for deleting
912                          * the attribute.
913                          */
914                         mod_op = LDAP_MOD_DELETE;
915                         param = NULL;
916                 }
917                 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
918                         /* Adding an empty string is considered a syntax error in
919                          * ldap. E.g attributes with empty strings are not allowed
920                          * in which case we silently refuse to add this entry
921                          */
922                 }
923                 else {
924                         SETMOD(mods[cnt], modarr[cnt], mod_op, "givenName", givenName, param);
925                         cnt++;
926                 }
927         }
928         mailList = g_hash_table_lookup(contact , "mail");
929         if (mailList) {
930                 debug_print("# of mail: %d\n", g_list_length(mailList));
931                 if (!(strcmp("mail", NoRemove->attribute) == 0 && g_list_length(mailList) == 1)) {
932                         char **tmp;
933                         mail = g_malloc(sizeof(*mail));
934                         tmp = g_malloc(sizeof(*tmp));
935                         mail = tmp;
936                         while (mailList) {
937                                 EmailKeyValue *item = mailList->data;
938                                 *tmp++ = g_strdup((gchar *) item->mail);
939                                 mailList = g_list_next(mailList);
940                         }
941                         *tmp = NULL;
942                         /*
943                          * At least one email address is required
944                          * in which case it will always be a replace
945                          */
946                         SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "mail", mail);
947                         cnt++;
948                 }
949         }
950         else {
951                 /*
952                  * an error condition since at least one email adress
953                  * is required. Should never occur though.
954                  */
955         }
956         param = g_hash_table_lookup(contact , "sn");
957         mod_op = ldapsvr_deside_operation(ld, dn, "sn", param);
958         if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("sn", NoRemove->attribute) != 0)) {
959                 if (mod_op == LDAP_MOD_DELETE) {
960                         /* Setting param to NULL instructs OpenLDAP to remove any
961                          * value stored for this attribute and remove the attribute
962                          * completely. Should multiple instances of an attribute be
963                          * allowed in the future param is required to have the value
964                          * store for the attribute which is going to be deleted
965                          */
966                         param = NULL;
967                 }
968                 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
969                         /* Having an empty string is considered a syntax error in
970                          * ldap. E.g attributes with empty strings are not allowed
971                          * in which case we treate this as a request for deleting
972                          * the attribute.
973                          */
974                         mod_op = LDAP_MOD_DELETE;
975                         param = NULL;
976                 }
977                 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
978                         /* Adding an empty string is considered a syntax error in
979                          * ldap. E.g attributes with empty strings are not allowed
980                          * in which case we silently refuse to add this entry
981                          */
982                 }
983                 else {
984                         SETMOD(mods[cnt], modarr[cnt], mod_op, "sn", sn, param);
985                         cnt++;
986                 }
987         }
988         debug_print("newDN: %s\n", dn);
989         if (NoRemove)
990                 g_free(NoRemove);
991         server->retVal = LDAPRC_SUCCESS;
992         if (cnt > 0) {
993                 int rc;
994                 mods[cnt] = NULL;
995                 rc = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
996                 if (rc) {
997                         fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",
998                     dn, rc, ldap_err2string(rc));
999                         server->retVal = LDAPRC_NAMING_VIOLATION;
1000                 }
1001                 if (mail)
1002                         g_free(mail);
1003         }
1004         /* If we do not make changes persistent at this point then changes
1005          * will be lost if the user makes new search on the same server since
1006          * changes are only present in Claws' internal cache. This issue has to
1007          * be solved in addressbook.c since this involves access to structures
1008          * which are only accessible in addressbook.c */
1009         clean_up(ld, server, contact);
1010 }
1011
1012 /**
1013  * Delete contact from LDAP
1014  *
1015  * \param server AddressBook resource
1016  * \param contact GHashTable with object to delete
1017  */
1018 void ldapsvr_delete_contact(LdapServer *server, GHashTable *contact) {
1019         LDAP *ld = NULL;
1020         gchar *dn;
1021         int rc;
1022
1023         g_return_if_fail(server != NULL || contact != NULL);
1024         ld = ldapsvr_connect(server->control);
1025         if (ld == NULL) {
1026                 clean_up(ld, server, contact);
1027                 return;
1028         }
1029         dn = g_hash_table_lookup(contact, "dn");
1030         if (dn == NULL) {
1031                 clean_up(ld, server, contact);
1032                 return;
1033         }
1034         server->retVal = LDAPRC_SUCCESS;
1035         rc = ldap_delete_ext_s(ld, dn, NULL, NULL);
1036         if (rc) {
1037                 fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",
1038                                 dn, rc, ldap_err2string(rc));
1039                 server->retVal = LDAPRC_NODN;
1040         }
1041         clean_up(ld, server, contact);
1042 }
1043
1044 /**
1045  * Update any changes to the server.
1046  *
1047  * \param server AddressBook resource.
1048  * \param person ItemPerson holding user input.
1049  */
1050 void ldapsvr_update_book(LdapServer *server, ItemPerson *item) {
1051         GList *node = NULL;
1052         GHashTable *contact = NULL;
1053         GList *contacts = NULL, *head = NULL;
1054
1055         g_return_if_fail(server != NULL);
1056         debug_print("updating ldap addressbook\n");
1057
1058         contact = g_hash_table_new(g_str_hash, g_str_equal);
1059         if (item) {
1060                 gboolean result = ldapsvr_retrieve_item_person(item, contact);
1061                 debug_print("Found contact to update: %s\n", result? "Yes" : "No");
1062                 if (result) {
1063                         if (debug_get_mode()) {
1064                                 addritem_print_item_person(item, stdout);
1065                         }
1066                         contacts = g_list_append(contacts, contact);
1067                 }
1068         }
1069         else {
1070                 ItemFolder *folder = server->addressCache->rootFolder;
1071                 node = folder->listFolder;
1072                 if (node) {
1073                         while (node) {
1074                                 AddrItemObject *aio = node->data;
1075                                 if (aio) {
1076                                         if (aio->type == ITEMTYPE_FOLDER) {
1077                                                 ItemFolder *folder = (ItemFolder *) aio;
1078                                                 GList *persons = folder->listPerson;
1079                                                 while (persons) {
1080                                                         AddrItemObject *aio = persons->data;
1081                                                         if (aio) {
1082                                                                 if (aio->type == ITEMTYPE_PERSON) {
1083                                                                         ItemPerson *item = (ItemPerson *) aio;
1084                                                                         gboolean result = ldapsvr_retrieve_item_person(item, contact);
1085                                                                         debug_print("Found contact to update: %s\n", result? "Yes" : "No");
1086                                                                         if (result) {
1087                                                                                 if (debug_get_mode()) {
1088                                                                                         gchar *uid = g_hash_table_lookup(contact, "uid");
1089                                                                                         item = ldapsvr_get_contact(server, uid);
1090                                                                                         addritem_print_item_person(item, stdout);
1091                                                                                 }
1092                                                                                 contacts = g_list_append(contacts, contact);
1093                                                                         }
1094                                                                 }
1095                                                         }
1096                                                         persons = g_list_next(persons);
1097                                                 }
1098                                         }
1099                                 }
1100                                 else {
1101                                         fprintf(stderr, "\t\tpid : ???\n");
1102                                 }
1103                                 node = g_list_next(node);
1104                         }
1105                 }
1106         }
1107         head = contacts;
1108         if (debug_get_mode()) {
1109                 if (contacts)
1110                         debug_print("Contacts which must be updated in LDAP:\n");
1111                 while (contacts) {
1112                         debug_print("\tContact:\n");
1113                         g_hash_table_foreach(contacts->data, 
1114                                 ldapsvr_print_contacts_hashtable, stderr);
1115                         contacts = g_list_next(contacts);
1116                 }
1117         }
1118         if (contacts == NULL)
1119                 contacts = head;
1120         while (contacts) {
1121                 gchar *status;
1122                 contact = (GHashTable *) contacts->data;
1123                 status = (gchar *) g_hash_table_lookup(contact, "status");
1124                 if (status == NULL)
1125                         status = g_strdup("NULL");
1126                 if (g_ascii_strcasecmp(status, "new") == 0) {
1127                         ldapsvr_add_contact(server, contact);
1128                 }
1129                 else if (g_ascii_strcasecmp(status, "update") == 0) {
1130                         ldapsvr_update_contact(server, contact);
1131                 }
1132                 else if (g_ascii_strcasecmp(status, "delete") == 0) {
1133                         ldapsvr_delete_contact(server, contact);
1134                 }
1135                 else
1136                         g_critical(_("ldapsvr_update_book->Unknown status: %s\n"), status);
1137                 contacts = g_list_next(contacts);
1138         }
1139         ldapsvr_free_hashtable(head);
1140 }
1141
1142 #endif  /* USE_LDAP */
1143
1144 /*
1145  * End of Source.
1146  */
1147