47472ca989e9679e76ef6e5a37b37bb5d5054f1a
[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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
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 #include "editaddress_other_attributes_ldap.h"
60
61 /**
62  * Structure to hold user defined attributes
63  * from contacts
64  */
65 typedef struct _AttrKeyValue AttrKeyValue;
66 struct _AttrKeyValue {
67         gchar *key;
68         gchar *value;
69 };
70
71 /**
72  * Structure to hold contact information.
73  * Each addressbook will have 0..N contacts.
74  */
75 typedef struct _EmailKeyValue EmailKeyValue;
76 struct _EmailKeyValue {
77         gchar *mail;
78         gchar *alias;
79         gchar *remarks;
80 };
81
82 /**
83  * Structure to hold information about RDN.
84  */
85 typedef struct _Rdn Rdn;
86 struct _Rdn {
87         gchar *attribute;
88         gchar *value;
89         gchar *new_dn;
90 };
91
92 /**
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.
96  */
97 void ldapsvr_retrieve_item_group(ItemGroup *group, GHashTable *array) {
98         /* Not implemented in this release */
99         g_return_if_fail(group != NULL);
100 }
101
102 /**
103  * Create an initial EmailKeyValue structure
104  * \return empty structure
105  */
106 EmailKeyValue *emailkeyvalue_create() {
107         EmailKeyValue *buf;
108
109         buf = g_new0(EmailKeyValue, 1);
110         buf->alias = NULL;
111         buf->mail = NULL;
112         buf->remarks = NULL;
113         return buf;
114 }
115
116 /**
117  * Create an initial AttrKeyValue structure
118  * \return empty structure
119  */
120 AttrKeyValue *attrkeyvalue_create() {
121         AttrKeyValue *buf;
122
123         buf = g_new0(AttrKeyValue, 1);
124         buf->key = NULL;
125         buf->value = NULL;
126         return buf;
127 }
128
129 /**
130  * Free created AttrKeyValue structure
131  * \param akv AttrKeyValue structure to free
132  */
133 void attrkeyvalue_free(AttrKeyValue *akv) {
134         if (akv->key) {
135                 g_free(akv->key);
136                 akv->key = NULL;
137         }
138         if (akv->value) {
139                 g_free(akv->value);
140                 akv->value = NULL;
141         }
142         g_free(akv);
143         akv = NULL;
144 }
145
146 /**
147  * Retrieve E-Mail address object for update.
148  * \param item   ItemEmail to update.
149  * \return object, or <i>NULL</i> if none created.
150  */
151 EmailKeyValue *ldapsvr_retrieve_item_email(ItemEMail *item) {
152         EmailKeyValue *newItem;
153         g_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);
158         return newItem;
159 }
160
161 /**
162  * Retrieve user attribute object for update.
163  * \param item   UserAttribute to update.
164  * \return object, or <i>NULL</i> if none created.
165  */
166 AttrKeyValue *ldapsvr_retrieve_attribute(UserAttribute *item) {
167         AttrKeyValue *newItem;
168         g_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);
172         return newItem;
173 }
174
175 /**
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.
180  */
181 gboolean ldapsvr_retrieve_item_person(ItemPerson *person, GHashTable *array) {
182         GList *node, *attr;
183
184         g_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);
191         }
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         node = person->listEMail;
199         attr = NULL;
200         while (node) {
201                 EmailKeyValue *newEmail = ldapsvr_retrieve_item_email(node->data);
202                 if (newEmail)
203                         attr = g_list_append(attr, newEmail);
204                 node = g_list_next(node);
205         }
206         g_hash_table_insert(array, "mail", attr);
207         node = person->listAttrib;
208         attr = NULL;
209         while (node) {
210                 AttrKeyValue *newAttr = ldapsvr_retrieve_attribute(node->data);
211                 if (newAttr)
212                         attr = g_list_append(attr, newAttr);
213                 node = g_list_next(node);
214         }
215         g_hash_table_insert(array, "attribute", attr);
216         return TRUE;
217 }
218
219 /**
220  * Print contents of contacts hashtable for debug.
221  * This function must be called with g_hash_table_foreach.
222  * \param key Key to process.
223  * \param data Data to process.
224  * \param fd Output stream.
225  */
226 void ldapsvr_print_contacts_hashtable(gpointer key, gpointer data, gpointer fd) {
227         gchar *keyName = (gchar *) key;
228         GList *node;
229
230         if (g_ascii_strcasecmp("mail", keyName) == 0) {
231                 node = (GList *) data;
232                 while (node) {
233                         EmailKeyValue *item = node->data;
234                         if (debug_get_mode()) {
235                                 debug_print("\t\talias = %s\n", item->alias);
236                                 debug_print("\t\tmail = %s\n", item->mail);
237                                 debug_print("\t\tremarks = %s\n", item->remarks);
238                         }
239                         else if (fd) {
240                                 FILE *stream = (FILE *) fd;
241                                 fprintf(stream, "\t\talias = %s\n", item->alias);
242                                 fprintf(stream, "\t\tmail = %s\n", item->mail);
243                                 fprintf(stream, "\t\tremarks = %s\n", item->remarks);
244                         }
245                         node = g_list_next(node);
246                 }
247         }
248         else if (g_ascii_strcasecmp("attribute", keyName) == 0) {
249                 node = (GList *) data;
250                 while (node) {
251                         AttrKeyValue *item = node->data;
252                         if (debug_get_mode()) {
253                                 debug_print("\t\t%s = %s\n", item->key, item->value);
254                         }
255                         else if (fd) {
256                                 FILE *stream = (FILE *) fd;
257                                 fprintf(stream, "\t\t%s = %s\n", item->key, item->value);
258                         }
259                         node = g_list_next(node);
260                 }
261         }
262         else {
263                 if (debug_get_mode())
264                         debug_print("\t\t%s = %s\n", keyName, (gchar *) data);
265                 else if (fd) {
266                         FILE *stream = (FILE *) fd;
267                         fprintf(stream, "\t\t%s = %s\n", keyName, (gchar *) data);
268                 }
269         }
270 }
271
272 /**
273  * Free list of changed contacts
274  *
275  * \param list List of GHashTable
276  */
277 void ldapsvr_free_hashtable(GList *list) {
278         GList *tmp = list;
279         while (tmp) {
280                 g_hash_table_destroy(tmp->data);
281                 tmp->data = NULL;
282                 tmp = g_list_next(tmp);
283         }
284         g_list_free(list);
285         list = NULL;
286 }
287
288 /**
289  * Get person object from cache
290  *
291  * \param server Resource to LDAP
292  * \param uid PersonID in cache
293  * \return person object, or <i>NULL</i> if fail
294  */
295 ItemPerson *ldapsvr_get_contact(LdapServer *server, gchar *uid) {
296         AddrItemObject *aio;
297         g_return_val_if_fail(server != NULL || uid != NULL, NULL);
298         aio = addrcache_get_object(server->addressCache, uid);
299         if (aio) {
300                 if(aio->type == ITEMTYPE_PERSON) {
301                         return (ItemPerson *) aio;
302                 }
303         }
304         return NULL;
305 }
306
307 /**
308  * Connect to LDAP server.
309  * \param  ctl Control object to process.
310  * \return LDAP Resource to LDAP.
311  */
312 LDAP *ldapsvr_connect(LdapControl *ctl) {
313         LDAP *ld = NULL;
314         gint rc;
315         gint version;
316         gchar *uri = NULL;
317
318         g_return_val_if_fail(ctl != NULL, NULL);
319
320         uri = g_strdup_printf("ldap%s://%s:%d",
321                                 ctl->enableSSL?"s":"",
322                                 ctl->hostName, ctl->port);
323         ldap_initialize(&ld, uri);
324         g_free(uri);
325
326         if (ld == NULL)
327                 return NULL;
328
329         debug_print("connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port);
330
331 #ifdef USE_LDAP_TLS
332         /* Handle TLS */
333         version = LDAP_VERSION3;
334         rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
335         if (rc == LDAP_OPT_SUCCESS) {
336                 ctl->version = LDAP_VERSION3;
337         }
338
339         if (ctl->version == LDAP_VERSION3) {
340                 if (ctl->enableTLS && !ctl->enableSSL) {
341                         rc = ldap_start_tls_s(ld, NULL, NULL);
342                         
343                         if (rc != LDAP_SUCCESS) {
344                                 g_printerr("LDAP Error(tls): ldap_simple_bind_s: %s\n",
345                                         ldap_err2string(rc));
346                                 return NULL;
347                         }
348                 }
349         }
350 #endif
351
352         /* Bind to the server, if required */
353         if (ctl->bindDN) {
354                 if (* ctl->bindDN != '\0') {
355                         rc = claws_ldap_simple_bind_s(ld, ctl->bindDN, ctl->bindPass);
356                         if (rc != LDAP_SUCCESS) {
357                                 g_printerr("bindDN: %s, bindPass: %s\n", ctl->bindDN, ctl->bindPass);
358                                 g_printerr("LDAP Error(bind): ldap_simple_bind_s: %s\n",
359                                         ldap_err2string(rc));
360                                 return NULL;
361                         }
362                 }
363         }
364         return ld;
365 }
366
367 /**
368  * Disconnect to LDAP server.
369  * \param ld Resource to LDAP.
370  */
371 void ldapsvr_disconnect(LDAP *ld) {
372         /* Disconnect */
373         g_return_if_fail(ld != NULL);
374         ldap_unbind_ext(ld, NULL, NULL);
375 }
376
377 /**
378  * Create an initial Rdn structure
379  *
380  * \return empty structure
381  */
382 Rdn *rdn_create() {
383         Rdn *buf;
384
385         buf = g_new0(Rdn, 1);
386         buf->attribute = NULL;
387         buf->value = NULL;
388         buf->new_dn = NULL;
389         return buf;
390 }
391
392 /**
393  * Free a created Rdn structure
394  * \param rdn Structure to free
395  */
396 void rdn_free(Rdn *rdn) {
397         if (rdn->attribute) {
398                 g_free(rdn->attribute);
399                 rdn->attribute = NULL;
400         }
401         if (rdn->value) {
402                 g_free(rdn->value);
403                 rdn->value = NULL;
404         }
405         if (rdn->new_dn) {
406                 g_free(rdn->new_dn);
407                 rdn->new_dn = NULL;
408         }
409         g_free(rdn);
410         rdn = NULL;
411 }
412
413 /**
414  * update Rdn structure
415  *
416  * \param rdn Rdn structure to update
417  * \param head Uniq part of dn
418  * \param tail Common part of dn
419  */
420 void update_rdn(Rdn *rdn, gchar *head, gchar *tail) {
421         rdn->value = g_strdup(head);
422         rdn->new_dn = g_strdup_printf("mail=%s%s", head, tail);
423 }
424
425 /**
426  * Deside if dn needs to be changed
427  *
428  * \param hash GHashTable with user input.
429  * \param dn dn for current object
430  * \return Rdn structure
431  */
432 Rdn *ldapsvr_modify_dn(GHashTable *hash, gchar *dn) {
433         Rdn *rdn;
434         gchar *pos, *compare;
435         gchar *rest;
436         gchar *val;
437         g_return_val_if_fail(hash != NULL || dn != NULL, NULL);
438         
439         pos = g_strstr_len(dn, strlen(dn), "=");
440         compare = g_strndup(dn, pos - dn);
441
442         if (!pos)
443                 return NULL;
444         pos++;
445         rest = g_strstr_len(pos, strlen(pos), ",");
446         val = g_strndup(pos, rest - pos);
447         if (val == NULL) {
448                 if (compare)
449                         g_free(compare);
450                 return NULL;
451         }
452         rdn = rdn_create();
453         rdn->value = g_strdup(val);
454         rdn->attribute = g_strdup(compare);
455         g_free(val);
456         if (strcmp("mail", rdn->attribute) == 0) {
457                 GList *list = g_hash_table_lookup(hash, rdn->attribute);
458                 while (list) {
459                         EmailKeyValue *item = list->data;
460                         compare = g_strdup((gchar *) item->mail);
461                         if (strcmp(compare, rdn->value) == 0) {
462                                 update_rdn(rdn, compare, rest);
463                                 g_free(compare);
464                                 return rdn;
465                         }
466                         list = g_list_next(list);
467                 }
468                 /* if compare and rdn->attribute are equal then last email removed/empty  */
469                 if (strcmp(compare, rdn->attribute) != 0) {
470                         /* RDN changed. Find new */
471                         update_rdn(rdn, compare, rest);
472                         g_free(compare);
473                         return rdn;
474                 }
475                 else {
476                         /* We cannot remove dn */
477                         g_free(compare);
478                         rdn_free(rdn);
479                         return NULL;
480                 }
481         }
482         else {
483                 compare = g_hash_table_lookup(hash, rdn->attribute);
484                 /* if compare and rdn->attribute are equal then dn removed/empty */
485                 if (strcmp(compare, rdn->attribute) != 0) {
486                         update_rdn(rdn, compare, rest);
487                         return rdn;
488                 }
489                 else {
490                         /* We cannot remove dn */
491                         rdn_free(rdn);
492                         return NULL;
493                 }
494         }
495         rdn_free(rdn);
496         return NULL;
497 }
498
499 /**
500  * This macro is borrowed from the Balsa project
501  * Creates a LDAPMod structure
502  *
503  * \param mods Empty LDAPMod structure
504  * \param modarr Array with values to insert
505  * \param op Operation to perform on LDAP
506  * \param attr Attribute to insert
507  * \param strv Empty array which is NULL terminated
508  * \param val Value for attribute
509  */
510 #define SETMOD(mods,modarr,op,attr,strv,val) \
511    do { (mods) = &(modarr); (modarr).mod_type=attr; (modarr).mod_op=op;\
512         (strv)[0]=(val); (modarr).mod_values=strv; \
513       } while(0)
514
515 /**
516  * Creates a LDAPMod structure
517  *
518  * \param mods Empty LDAPMod structure
519  * \param modarr Array with values to insert
520  * \param op Operation to perform on LDAP
521  * \param attr Attribute to insert
522  * \param strv Array with values to insert. Must be NULL terminated
523  */
524 #define SETMODS(mods,modarr,op,attr,strv) \
525    do { (mods) = &(modarr); (modarr).mod_type=attr; \
526                 (modarr).mod_op=op; (modarr).mod_values=strv; \
527       } while(0)
528 #define MODSIZE 10
529
530 /**
531  * Clean up, close LDAP connection, and refresh cache
532  *
533  * \param ld Resource to LDAP
534  * \param server AddressBook resource
535  * \param contact GHashTable with current changed object
536  */
537 void clean_up(LDAP *ld, LdapServer *server, GHashTable *contact) {
538         ItemPerson *person = 
539                 ldapsvr_get_contact(server, g_hash_table_lookup(contact , "uid"));
540         if (person) {
541                 gchar *displayName;
542                 person->status = NONE;
543                 displayName = g_hash_table_lookup(contact, "displayName");
544                 if (displayName)
545                         person->nickName = g_strdup(displayName);
546         }
547         if (server->retVal != LDAPRC_SUCCESS) {
548                 if (person) {
549                         ItemPerson *res = 
550                                 addrcache_remove_person(server->addressCache, person);
551                         if (!res)
552                                 g_critical(N_("ldapsvr_update_book: Could not clean cache\n"));
553                         else
554                                 addritem_free_item_person(res);
555                 }
556         }
557         if (ld)
558                 ldapsvr_disconnect(ld);
559 }
560
561 /**
562  * Get cn attribute from dn
563  *
564  * \param dn Distinguesh Name for current object
565  * \return AttrKeyValue, or <i>NULL</i> if none created
566  */
567 AttrKeyValue *get_cn(gchar *dn) {
568         AttrKeyValue *cn;
569         gchar *start;
570         gchar *end;
571         gchar *item;
572         gchar **key_value;
573         g_return_val_if_fail(dn != NULL, NULL);
574         
575         cn = attrkeyvalue_create();
576         start = g_strstr_len(dn, strlen(dn), "cn");
577         if (start == NULL) {
578                 attrkeyvalue_free(cn);
579                 return NULL;
580         }
581         end = g_strstr_len(start, strlen(start), ",");
582         item = g_strndup(start, end - start);
583         if (item == NULL) {
584                 attrkeyvalue_free(cn);
585                 return NULL;
586         }
587         key_value = g_strsplit(item, "=", 2);
588         cn->key = g_strdup(key_value[0]);
589         cn->value = g_strdup(key_value[1]);
590         g_strfreev(key_value);
591         g_free(item);
592         return cn;
593 }
594
595 /**
596  * Get mail attribute from dn
597  *
598  * \param dn Distinguesh Name for current object
599  * \return AttrKeyValue, or <i>NULL</i> if none created
600  */
601 AttrKeyValue *get_mail(gchar *dn) {
602         AttrKeyValue *mail;
603         gchar *start;
604         gchar *end;
605         gchar *item;
606         gchar **key_value;
607         g_return_val_if_fail(dn != NULL, NULL);
608         
609         mail = attrkeyvalue_create();
610         start = g_strstr_len(dn, strlen(dn), "mail");
611         if (start == NULL) {
612                 attrkeyvalue_free(mail);
613                 return NULL;
614         }
615         end = g_strstr_len(start, strlen(start), ",");
616         item = g_strndup(start, end - start);
617         if (item == NULL) {
618                 attrkeyvalue_free(mail);
619                 return NULL;
620         }
621         key_value = g_strsplit(item, "=", 2);
622         mail->key = g_strdup(key_value[0]);
623         mail->value = g_strdup(key_value[1]);
624         g_strfreev(key_value);
625         g_free(item);
626         return mail;
627 }
628
629 /**
630  * Get ou or o attribute from dn
631  *
632  * \param dn Distinguesh Name for current object
633  * \return AttrKeyValue, or <i>NULL</i> if none created
634  */
635 AttrKeyValue *get_ou(gchar *dn) {
636         AttrKeyValue *ou;
637         gchar *start;
638         gchar *end;
639         gchar *item;
640         gchar **key_value;
641         
642         g_return_val_if_fail(dn != NULL, NULL);
643         ou = attrkeyvalue_create();
644         start = g_strstr_len(dn, strlen(dn), ",o=");
645         if (start == NULL)
646                 start = g_strstr_len(dn, strlen(dn), ",ou=");
647         if (start == NULL) {
648                 attrkeyvalue_free(ou);
649                 return NULL;
650         }
651         start++;
652         end = g_strstr_len(start, strlen(start), ",");
653         item = g_strndup(start, end - start);
654         if (item == NULL) {
655                 attrkeyvalue_free(ou);
656                 return NULL;
657         }
658         key_value = g_strsplit(item, "=", 2);
659         ou->key = g_strdup(key_value[0]);
660         ou->value = g_strdup(key_value[1]);
661         g_strfreev(key_value);
662         g_free(item);
663         return ou;
664 }
665
666 /**
667  * Print the contents of a LDAPMod structure for debuging purposes
668  *
669  * \param mods LDAPMod structure
670  */
671 void ldapsvr_print_ldapmod(LDAPMod *mods[]) {
672         gchar *mod_op;
673         int i;
674
675         g_return_if_fail(mods != NULL);
676         g_printerr( "Type\n");
677         for (i = 0; NULL != mods[i]; i++) {
678                 LDAPMod *mod = (LDAPMod *) mods[i];
679                 gchar **vals;
680                 switch (mod->mod_op) {
681                         case LDAP_MOD_ADD: mod_op = g_strdup("ADD"); break;
682                         case LDAP_MOD_REPLACE: mod_op = g_strdup("MODIFY"); break;
683                         case LDAP_MOD_DELETE: mod_op = g_strdup("DELETE"); break;
684                         default: mod_op = g_strdup("UNKNOWN");
685                 }
686                 g_printerr( "Operation: %s\tType:%s\nValues:\n", mod_op, mod->mod_type);
687                 vals = mod->mod_vals.modv_strvals;
688                 while (*vals) {
689                         g_printerr( "\t%s\n", *vals++);
690                 }
691         }
692 }
693
694 /**
695  * Make a compare for every new value we want to store in the
696  * directory with the current values. Great tool for debugging
697  * against invalid syntax in attributes
698  *
699  * \param ld AddressBook resource
700  * \param dn dn for the entry
701  * \param cnt Number of attributes to compare
702  * \param  mods LDAPMod structure
703  */
704 void ldapsvr_compare_attr(LDAP *ld, gchar *dn, gint cnt, LDAPMod *mods[]) {
705         int i, rc;
706         
707         g_return_if_fail(ld != NULL || dn != NULL || cnt >= 0 || mods != NULL);
708         for (i = 0; i < cnt; i++) {
709                 gchar *value = g_strdup(mods[i]->mod_vals.modv_strvals[0]);
710                 if (!value || strcmp(value, "") == 0)
711                         value = g_strdup("thisisonlyadummy");
712                 rc = ldap_compare_s(ld, dn, mods[i]->mod_type, value);
713                 g_printerr("ldap_compare for (%s:%s)\" failed[0x%x]: %s\n",
714                 mods[i]->mod_type, value, rc, ldap_err2string(rc));
715                 g_free(value);
716         }
717 }
718
719 /**
720  * compare attribute to LDAP in case of LDAP_INAPPROPRIATE_MATCHING
721  *
722  * \param ld AddressBook resource
723  * \param server Reference to server
724  * \param dn dn for the entry
725  * \param attr Attribute
726  * \param value New value
727  * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
728  */
729 int ldapsvr_compare_manual_attr(LDAP *ld, LdapServer *server, gchar *dn, char *attr, char *value) {
730         LDAPMessage *res, *e = NULL;
731         BerElement *ber;
732         struct berval **vals;
733         int rc;
734         LdapControl *ctl;
735         gchar *filter;
736         gchar *attribute;
737         int retVal = -2, i;
738         AttrKeyValue *mail;
739
740         g_return_val_if_fail(ld != NULL || server != NULL || attr != NULL, -1);
741         ctl = server->control;
742         mail = get_mail(dn);
743         if (! mail)
744                 return -2;
745         filter = g_strdup_printf("(&(mail=%s)(%s=*))", mail->value, attr);
746         attrkeyvalue_free(mail);
747         if (ctl) {
748                 rc = ldap_search_s(ld, ctl->baseDN, /*LDAP_SCOPE_SUBTREE*/LDAP_SCOPE_ONELEVEL, filter, NULL, 0, &res);
749                 if (rc) {
750                         g_printerr("ldap_search for attr=%s\" failed[0x%x]: %s\n",attr, rc, ldap_err2string(rc));
751                         retVal = -2;
752                 }
753                 else {
754                         e = ldap_first_entry(ld, res);
755                         /* entry has this attribute */
756                         if (e) {
757                                 attribute = ldap_first_attribute( ld, e, &ber );
758                                 if (attribute) {
759                                         if (value) {
760                                                 if( ( vals = ldap_get_values_len( ld, e, attr ) ) != NULL ) {
761                                                         for( i = 0; vals[i] != NULL; i++ ) {
762                                                                 debug_print("Compare: %s=%s\n", attr, vals[i]->bv_val);
763                                                                 /* attribute has same value */
764                                                                 if (strcmp(vals[i]->bv_val, value) == 0)
765                                                                         retVal = -1;
766                                                                 /* attribute has new value */
767                                                                 else
768                                                                         retVal = LDAP_MOD_REPLACE;
769                                                         }
770                                                 }
771                                                 ldap_value_free_len(vals);
772                                         }
773                                         else
774                                                 retVal = LDAP_MOD_DELETE;
775                                 }
776                                 if( ber != NULL ) {
777                                         ber_free( ber, 0 );
778                                 }
779                                 ldap_memfree(attribute);
780                         }
781                         /* entry does not have this attribute */
782                         else {
783                                 /* Only add if this is a real attribute */
784                                 if (value)
785                                         retVal = LDAP_MOD_ADD;
786                                 /* This is dummy value used to avoid ldap_compare error */
787                                 else
788                                         retVal = -1;
789                         }
790                 }
791         }
792         else
793                 retVal = -2;
794         g_free(filter);
795         return retVal;
796 }
797
798 /**
799  * Deside which kind of operation is required to handle
800  * updating the specified attribute
801  *
802  * \param ld AddressBook resource
803  * \param server Reference to server
804  * \param dn dn for the entry
805  * \param attr Attribute
806  * \param value New value
807  * \return int, return will be LDAP_MOD_ADD, LDAP_MOD_REPLACE, or LDAP_MOD_DELETE
808  */
809 int ldapsvr_deside_operation(LDAP *ld, LdapServer *server, char *dn, char *attr, char *value) {
810         int rc;
811         gboolean dummy = FALSE;
812
813         g_return_val_if_fail(ld != NULL || server != NULL || dn != NULL || attr != NULL, -1);
814         if (value == NULL)
815                 return -1;
816         /* value containing empty string cause invalid syntax. A bug in
817          * the LDAP library? Therefore we add a dummy value
818          */
819         if (strcmp(value,"") == 0) {
820                 value = g_strdup("thisisonlyadummy");
821                 dummy = TRUE;
822         }
823         rc = ldap_compare_s(ld, dn, attr, value);
824         debug_print("ldap_compare for (%s:%s)\" error_code[0x%x]: %s\n",
825         attr, value, rc, ldap_err2string(rc));
826         switch (rc) {
827                 case LDAP_COMPARE_FALSE: 
828                         if (dummy)
829                                 return LDAP_MOD_DELETE;
830                         else
831                                 return LDAP_MOD_REPLACE;
832                 case LDAP_COMPARE_TRUE: return -1;
833                 case LDAP_NO_SUCH_ATTRIBUTE: return LDAP_MOD_ADD;
834                 /* LDAP_INAPPROPRIATE_MATCHING needs extensive testing because I
835                  * am not aware off the condition causing this return value!
836                  */
837                 case LDAP_INAPPROPRIATE_MATCHING:
838                         if (dummy)
839                                 value = NULL;
840                         return ldapsvr_compare_manual_attr(ld, server, dn, attr, value);
841                 case LDAP_UNDEFINED_TYPE: return -2;
842                 case LDAP_INVALID_SYNTAX: return -2;
843                 default: return -2;
844         }
845 }
846
847 /**
848  * Check if attribute is part of the current search criteria
849  *
850  * \param list Array containing attributes in the current search criteria
851  * \param attr Attribute to check
852  * \result <i>TRUE</i> if attribute is found in the current search criteria
853  */
854 gboolean ldapsvr_check_search_attributes(char **list, char *attr) {
855         while (*list) {
856                 if (strcmp(*list++, attr) == 0)
857                         return TRUE;
858         }
859         return FALSE;
860 }
861
862 /**
863  * Deside which other attributes needs updating
864  *
865  * \param ld LDAP resource
866  * \param server AddressBook resource
867  * \param dn dn for the entry
868  * \param contact GHashTable with information for the current contact
869  */
870 void ldapsvr_handle_other_attributes(LDAP *ld, LdapServer *server, char *dn, GHashTable *contact) {
871         GList *node;
872         gboolean CHECKED_ATTRIBUTE[ATTRIBUTE_SIZE + 1];
873         LDAPMod *mods[ATTRIBUTE_SIZE + 1];
874         LDAPMod modarr[ATTRIBUTE_SIZE];
875         gint cnt = 0;
876         char *attr[ATTRIBUTE_SIZE + 1][2];
877         int mod_op, rc, i;
878
879         g_return_if_fail(server != NULL || dn != NULL || contact != NULL);
880         for (i = 0; i <= ATTRIBUTE_SIZE; i++) {
881                 CHECKED_ATTRIBUTE[i] = FALSE;
882                 attr[i][0] = attr[i][1] = NULL;
883         }
884         node = g_hash_table_lookup(contact , "attribute");
885         while (node) {
886                 AttrKeyValue *item = node->data;
887                 if (item) {
888                         int index = get_attribute_index(item->key);
889                         if (index >= 0) {
890                                 debug_print("Found other attribute: %s = %s\n", item->key, item->value);
891                                 mod_op = ldapsvr_deside_operation(ld, server, dn, item->key, item->value);
892                                 /* Only consider attributes which we no how to handle.
893                                  * Set to TRUE in CHECKED_ATTRIBUTE array to indicate no further action
894                                  */
895                                 if (mod_op < 0) {
896                                         CHECKED_ATTRIBUTE[index] = TRUE;
897                                         node = g_list_next(node);
898                                         continue;
899                                 }
900                                 if (mod_op == LDAP_MOD_DELETE) {
901                                         /* Setting param to NULL instructs OpenLDAP to remove any
902                                         * value stored for this attribute and remove the attribute
903                                         * completely. Should multiple instances of an attribute be
904                                         * allowed in the future param is required to have the value
905                                         * store for the attribute which is going to be deleted
906                                         */
907                                         item->value = NULL;
908                                 }
909                                 if (mod_op == LDAP_MOD_REPLACE && strcmp(item->value, "") == 0) {
910                                         /* Having an empty string is considered a syntax error in
911                                         * ldap. E.g attributes with empty strings are not allowed
912                                         * in which case we treate this as a request for deleting
913                                         * the attribute.
914                                         */
915                                         mod_op = LDAP_MOD_DELETE;
916                                         item->value = NULL;
917                                 }
918                                 if (mod_op == LDAP_MOD_ADD && strcmp(item->value, "") == 0) {
919                                         /* Adding an empty string is considered a syntax error in
920                                         * ldap. E.g attributes with empty strings are not allowed
921                                         * in which case we silently refuse to add this entry
922                                         */
923                                 }
924                                 else {
925                                         SETMOD(mods[cnt], modarr[cnt], mod_op, g_strdup(item->key), attr[cnt], g_strdup(item->value));
926                                         cnt++;
927                                         CHECKED_ATTRIBUTE[index] = TRUE;
928                                 }
929                         }
930                 }
931                 node = g_list_next(node);
932         }
933         char **attribs = ldapctl_attribute_array(server->control);
934         for (i = 0; i < ATTRIBUTE_SIZE; i++) {
935                 /* Attributes which holds no information are to be removed */
936                 if (CHECKED_ATTRIBUTE[i] == FALSE) {
937                         /* Only consider those attributes which is currently part of the search criteria.
938                          * If attributes are not part of the search criteria they would seem to hold
939                          * no information since their values will not be populated in the GUI
940                          */
941                         if (ldapsvr_check_search_attributes(attribs, (char *) ATTRIBUTE[i])) {
942                                 mod_op = ldapsvr_deside_operation(ld, server, dn, (char *) ATTRIBUTE[i], "");
943                                 if (mod_op == LDAP_MOD_DELETE) {
944                                         SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_DELETE, g_strdup((char *) ATTRIBUTE[i]), attr[cnt], NULL);
945                                         cnt++;
946                                 }
947                         }
948                 }
949         }
950         ldapctl_free_attribute_array(attribs);
951         mods[cnt] = NULL;
952         if (debug_get_mode())
953                 ldapsvr_print_ldapmod(mods);
954         server->retVal = LDAPRC_SUCCESS;
955         rc = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
956         if (rc) {
957                 switch (rc) {
958                         case LDAP_ALREADY_EXISTS: 
959                                 server->retVal = LDAPRC_ALREADY_EXIST;
960                                 break;
961                         default:
962                                 g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",dn, rc, ldap_err2string(rc));
963                                 if (rc == 0x8)
964                                         server->retVal = LDAPRC_STRONG_AUTH;
965                                 else
966                                         server->retVal = LDAPRC_NAMING_VIOLATION;
967                 }
968         }
969         else {
970                 /* Only consider those attributes which is currently part of the search criteria.
971                  * If attributes are not part of the search criteria they would seem to hold
972                  * no information since their values will not be populated in the GUI
973                  */
974                 char **attribs = ldapctl_attribute_array(server->control);
975                 for (i = 0; i < ATTRIBUTE_SIZE; i++) {
976                         if (ldapsvr_check_search_attributes(attribs, (char *) ATTRIBUTE[i])) {
977                                 if (CHECKED_ATTRIBUTE[i] == FALSE) {
978                                         AddrItemObject *aio = addrcache_get_object(server->addressCache, g_hash_table_lookup(contact , "uid"));
979                                         ItemPerson *person = (ItemPerson *) aio;
980                                         addritem_person_remove_attribute(person, (const gchar *) ATTRIBUTE[i]);
981                                 }
982                         }
983                 }
984                 ldapctl_free_attribute_array(attribs);
985         }
986 }
987
988 /**
989  * Add new contact to LDAP
990  *
991  * \param server AddressBook resource
992  * \param contact GHashTable with object to add
993  */
994 void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
995         gchar *email = NULL, *param = NULL;
996         LDAP *ld = NULL;
997         LDAPMod *mods[MODSIZE];
998         LDAPMod modarr[7];
999         gint cnt = 0;
1000         char *cn[] = {NULL, NULL};
1001         char *displayName[] = {NULL, NULL};
1002         char *givenName[] = {NULL, NULL};
1003         char **mail = NULL;
1004         char *sn[] = {NULL, NULL};
1005         char *org[] = {NULL, NULL};
1006         char *obj[] = {/*"top",*/ "person", "organizationalPerson", "inetOrgPerson", NULL}; 
1007         int rc=0;
1008         GList *node;
1009         AttrKeyValue *ou, *commonName;
1010         ItemPerson *person;
1011         gchar *base_dn;
1012         GList *mailList;
1013
1014         g_return_if_fail(server != NULL || contact != NULL);
1015         node = g_hash_table_lookup(contact , "mail");
1016         if (node) {
1017                 EmailKeyValue *newEmail = node->data;
1018                 email = g_strdup(newEmail->mail);
1019         }
1020         if (email == NULL) {
1021                 server->retVal = LDAPRC_NODN;
1022                 clean_up(ld, server, contact);
1023                 return;
1024         }
1025         base_dn = g_strdup_printf("mail=%s,%s", email, server->control->baseDN);
1026         g_free(email);
1027         person = 
1028                 ldapsvr_get_contact(server, g_hash_table_lookup(contact , "uid"));
1029         person->externalID = g_strdup(base_dn);
1030         debug_print("dn: %s\n", base_dn);
1031         ld = ldapsvr_connect(server->control);
1032         if (ld == NULL) {
1033                 clean_up(ld, server, contact);
1034                 debug_print("no ldap found\n");
1035                 return;
1036         }
1037         SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "objectClass", obj);
1038         cnt++;
1039         ou = get_ou(base_dn);
1040         if (ou != NULL) {
1041                 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, g_strdup(ou->key), org, g_strdup(ou->value));
1042                 cnt++;
1043                 attrkeyvalue_free(ou);
1044         }
1045         
1046         commonName = get_cn(base_dn);
1047         if (commonName == NULL) {
1048                 param = g_hash_table_lookup(contact , "cn");
1049                 if (param) {
1050                         SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "cn", cn, param);
1051                 }
1052                 else {
1053                         clean_up(ld, server, contact);
1054                         debug_print("no CN found\n");
1055                         return;
1056                 }
1057         }
1058         else {
1059                 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, g_strdup(commonName->key), cn, g_strdup(commonName->value));
1060                 cnt++;
1061                 param = g_hash_table_lookup(contact , "cn");
1062                 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "displayName", displayName, param);
1063                 g_hash_table_insert(contact, "displayName", param);
1064                 attrkeyvalue_free(commonName);
1065         }
1066         cnt++;
1067         param = g_hash_table_lookup(contact , "givenName");
1068         if (param) {
1069                 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "givenName", givenName, param);
1070                 cnt++;
1071         }
1072         mailList = g_hash_table_lookup(contact , "mail");
1073         if (mailList) {
1074                 char **tmp;
1075                 tmp = g_malloc(sizeof(*tmp) * (g_list_length(mailList)+1));
1076                 mail = tmp;
1077                 while (mailList) {
1078                         EmailKeyValue *item = mailList->data;
1079                         *tmp++ = g_strdup((gchar *) item->mail);
1080                         mailList = g_list_next(mailList);
1081                 }
1082                 *tmp = NULL;
1083                 SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "mail", mail);
1084                 cnt++;
1085         }
1086         param = g_hash_table_lookup(contact, "sn");
1087         if (param == NULL)
1088                 param = g_strdup(N_("Some SN"));
1089         SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "sn", sn, param);
1090         cnt++;
1091         mods[cnt] = NULL;
1092         if (debug_get_mode()) {
1093                 ldapsvr_print_ldapmod(mods);
1094         }
1095         server->retVal = LDAPRC_SUCCESS;
1096         rc = ldap_add_ext_s(ld, base_dn, mods, NULL, NULL);
1097         if (rc) {
1098                 switch (rc) {
1099                         case LDAP_ALREADY_EXISTS: 
1100                                 server->retVal = LDAPRC_ALREADY_EXIST;
1101                                 break;
1102                         default:
1103                                 g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",base_dn, rc, ldap_err2string(rc));
1104                                 if (rc == 0x8)
1105                                         server->retVal = LDAPRC_STRONG_AUTH;
1106                                 else
1107                                         server->retVal = LDAPRC_NAMING_VIOLATION;
1108                 }
1109         }
1110         ldapsvr_handle_other_attributes(ld, server, base_dn, contact);
1111         g_free(base_dn);
1112         clean_up(ld, server, contact);
1113 }
1114
1115 /**
1116  * Update contact to LDAP
1117  *
1118  * \param server AddressBook resource
1119  * \param contact GHashTable with object to update
1120  */
1121 void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
1122         LDAP *ld = NULL;
1123         LDAPMod *mods[MODSIZE];
1124         LDAPMod modarr[4];
1125         gint cnt = 0;
1126         gchar *param, *dn;
1127         Rdn *NoRemove = NULL;
1128         char *cn[] = {NULL, NULL};
1129         char *givenName[] = {NULL, NULL};
1130         char **mail = NULL;
1131         char *sn[] = {NULL, NULL};
1132         GList *mailList;
1133         int mod_op;
1134
1135         g_return_if_fail(server != NULL || contact != NULL);
1136         ld = ldapsvr_connect(server->control);
1137         if (ld == NULL) {
1138                 clean_up(ld, server, contact);
1139                 return;
1140         }
1141         dn = g_hash_table_lookup(contact, "dn");
1142         if (dn == NULL) {
1143                 clean_up(ld, server, contact);
1144                 return;
1145         }
1146         NoRemove = ldapsvr_modify_dn(contact, dn);
1147         if (NoRemove) {
1148                 /* We are trying to change RDN */
1149                 gchar *newRdn = g_strdup_printf("%s=%s", NoRemove->attribute, NoRemove->value);
1150                 int rc = ldap_modrdn2_s(ld, dn, newRdn, 1);
1151                 if(rc != LDAP_SUCCESS) {
1152                         if (rc ==  LDAP_ALREADY_EXISTS) {
1153                                 /* We are messing with a contact with more than one listed email
1154                                  * address and the email we are changing is not the one used for dn
1155                                  */
1156                                 /* It needs to be able to handle renaming errors to an already defined
1157                                  * dn. For now we just refuse the update. It will be caught later on as
1158                                  * a LDAPRC_NAMING_VIOLATION error.
1159                                  */
1160                         }
1161                         else {
1162                                 g_printerr("Current dn: %s\n", dn);
1163                                 g_printerr("new dn: %s\n", newRdn);
1164                                 g_printerr("LDAP Error(ldap_modrdn2_s) failed[0x%x]: %s\n", rc, ldap_err2string(rc));
1165                                 g_free(newRdn);
1166                                 clean_up(ld, server, contact);
1167                                 return;
1168                         }
1169                 }
1170                 else {
1171                         g_free(newRdn);
1172                         dn = g_strdup(NoRemove->new_dn);
1173                 }
1174         }
1175         else {
1176                 server->retVal = LDAPRC_NODN;
1177                 clean_up(ld, server, contact);
1178                 return;
1179         }
1180         param = g_hash_table_lookup(contact , "cn");
1181         mod_op = ldapsvr_deside_operation(ld, server, dn, "displayName", param);
1182         if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("cn", NoRemove->attribute) != 0)) {
1183                 if (mod_op == LDAP_MOD_DELETE) {
1184                         /* Setting param to NULL instructs OpenLDAP to remove any
1185                          * value stored for this attribute and remove the attribute
1186                          * completely. Should multiple instances of an attribute be
1187                          * allowed in the future param is required to have the value
1188                          * store for the attribute which is going to be deleted
1189                          */
1190                         param = NULL;
1191                 }
1192                 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
1193                         /* Having an empty string is considered a syntax error in
1194                          * ldap. E.g attributes with empty strings are not allowed
1195                          * in which case we treate this as a request for deleting
1196                          * the attribute.
1197                          */
1198                         mod_op = LDAP_MOD_DELETE;
1199                         param = NULL;
1200                 }
1201                 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
1202                         /* Adding an empty string is considered a syntax error in
1203                          * ldap. E.g attributes with empty strings are not allowed
1204                          * in which case we silently refuse to add this entry
1205                          */
1206                 }
1207                 else {
1208                         SETMOD(mods[cnt], modarr[cnt], mod_op, "displayName", cn, param);
1209                         cnt++;
1210                         g_hash_table_insert(contact, "displayName", param);
1211                 }
1212         }
1213         param = g_hash_table_lookup(contact , "givenName");
1214         mod_op = ldapsvr_deside_operation(ld, server, dn, "givenName", param);
1215         if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("givenName", NoRemove->attribute) != 0)) {
1216                 if (mod_op == LDAP_MOD_DELETE) {
1217                         /* Setting param to NULL instructs OpenLDAP to remove any
1218                          * value stored for this attribute and remove the attribute
1219                          * completely. Should multiple instances of an attribute be
1220                          * allowed in the future param is required to have the value
1221                          * store for the attribute which is going to be deleted
1222                          */
1223                         param = NULL;
1224                 }
1225                 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
1226                         /* Having an empty string is considered a syntax error in
1227                          * ldap. E.g attributes with empty strings are not allowed
1228                          * in which case we treate this as a request for deleting
1229                          * the attribute.
1230                          */
1231                         mod_op = LDAP_MOD_DELETE;
1232                         param = NULL;
1233                 }
1234                 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
1235                         /* Adding an empty string is considered a syntax error in
1236                          * ldap. E.g attributes with empty strings are not allowed
1237                          * in which case we silently refuse to add this entry
1238                          */
1239                 }
1240                 else {
1241                         SETMOD(mods[cnt], modarr[cnt], mod_op, "givenName", givenName, param);
1242                         cnt++;
1243                 }
1244         }
1245         mailList = g_hash_table_lookup(contact , "mail");
1246         if (mailList) {
1247                 debug_print("# of mail: %d\n", g_list_length(mailList));
1248                 if (!(strcmp("mail", NoRemove->attribute) == 0 && g_list_length(mailList) == 1)) {
1249                         char **tmp;
1250                         tmp = g_malloc(sizeof(*tmp) * (g_list_length(mailList)+1));
1251                         mail = tmp;
1252                         while (mailList) {
1253                                 EmailKeyValue *item = mailList->data;
1254                                 *tmp++ = g_strdup((gchar *) item->mail);
1255                                 mailList = g_list_next(mailList);
1256                         }
1257                         *tmp = NULL;
1258                         /*
1259                          * At least one email address is required
1260                          * in which case it will always be a replace
1261                          */
1262                         SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "mail", mail);
1263                         cnt++;
1264                 }
1265         }
1266         else {
1267                 /*
1268                  * an error condition since at least one email adress
1269                  * is required. Should never occur though.
1270                  */
1271         }
1272         param = g_hash_table_lookup(contact , "sn");
1273         mod_op = ldapsvr_deside_operation(ld, server, dn, "sn", param);
1274         if (mod_op >= 0 && (strcmp(param, NoRemove->value) != 0 && strcmp("sn", NoRemove->attribute) != 0)) {
1275                 if (mod_op == LDAP_MOD_DELETE) {
1276                         /* Setting param to NULL instructs OpenLDAP to remove any
1277                          * value stored for this attribute and remove the attribute
1278                          * completely. Should multiple instances of an attribute be
1279                          * allowed in the future param is required to have the value
1280                          * store for the attribute which is going to be deleted
1281                          */
1282                         param = NULL;
1283                 }
1284                 if (mod_op == LDAP_MOD_REPLACE && strcmp(param, "") == 0) {
1285                         /* Having an empty string is considered a syntax error in
1286                          * ldap. E.g attributes with empty strings are not allowed
1287                          * in which case we treate this as a request for deleting
1288                          * the attribute.
1289                          */
1290                         mod_op = LDAP_MOD_DELETE;
1291                         param = NULL;
1292                 }
1293                 if (mod_op == LDAP_MOD_ADD && strcmp(param, "") == 0) {
1294                         /* Adding an empty string is considered a syntax error in
1295                          * ldap. E.g attributes with empty strings are not allowed
1296                          * in which case we silently refuse to add this entry
1297                          */
1298                 }
1299                 else {
1300                         SETMOD(mods[cnt], modarr[cnt], mod_op, "sn", sn, param);
1301                         cnt++;
1302                 }
1303         }
1304         debug_print("newDN: %s\n", dn);
1305         if (NoRemove)
1306                 rdn_free(NoRemove);
1307         server->retVal = LDAPRC_SUCCESS;
1308         if (cnt > 0) {
1309                 int rc;
1310                 mods[cnt] = NULL;
1311                 rc = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
1312                 if (rc) {
1313                         g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",
1314                     dn, rc, ldap_err2string(rc));
1315                         server->retVal = LDAPRC_NAMING_VIOLATION;
1316                 }
1317                 if (mail)
1318                         g_free(mail);
1319         }
1320         ldapsvr_handle_other_attributes(ld, server, dn, contact);
1321         /* If we do not make changes persistent at this point then changes
1322          * will be lost if the user makes new search on the same server since
1323          * changes are only present in Claws' internal cache. This issue has to
1324          * be solved in addressbook.c since this involves access to structures
1325          * which are only accessible in addressbook.c */
1326         clean_up(ld, server, contact);
1327 }
1328
1329 /**
1330  * Delete contact from LDAP
1331  *
1332  * \param server AddressBook resource
1333  * \param contact GHashTable with object to delete
1334  */
1335 void ldapsvr_delete_contact(LdapServer *server, GHashTable *contact) {
1336         LDAP *ld = NULL;
1337         gchar *dn;
1338         int rc;
1339
1340         g_return_if_fail(server != NULL || contact != NULL);
1341         ld = ldapsvr_connect(server->control);
1342         if (ld == NULL) {
1343                 clean_up(ld, server, contact);
1344                 return;
1345         }
1346         dn = g_hash_table_lookup(contact, "dn");
1347         if (dn == NULL) {
1348                 clean_up(ld, server, contact);
1349                 return;
1350         }
1351         server->retVal = LDAPRC_SUCCESS;
1352         rc = ldap_delete_ext_s(ld, dn, NULL, NULL);
1353         if (rc) {
1354                 g_printerr("ldap_modify for dn=%s\" failed[0x%x]: %s\n",
1355                                 dn, rc, ldap_err2string(rc));
1356                 server->retVal = LDAPRC_NODN;
1357         }
1358         clean_up(ld, server, contact);
1359 }
1360
1361 /**
1362  * Update any changes to the server.
1363  *
1364  * \param server AddressBook resource.
1365  * \param person ItemPerson holding user input.
1366  */
1367 void ldapsvr_update_book(LdapServer *server, ItemPerson *item) {
1368         GList *node = NULL;
1369         GHashTable *contact = NULL;
1370         GList *contacts = NULL, *head = NULL;
1371
1372         g_return_if_fail(server != NULL);
1373         debug_print("updating ldap addressbook\n");
1374
1375         contact = g_hash_table_new(g_str_hash, g_str_equal);
1376         if (item) {
1377                 gboolean result = ldapsvr_retrieve_item_person(item, contact);
1378                 debug_print("Found contact to update: %s\n", result? "Yes" : "No");
1379                 if (result) {
1380                         if (debug_get_mode()) {
1381                                 addritem_print_item_person(item, stdout);
1382                         }
1383                         contacts = g_list_append(contacts, contact);
1384                 }
1385         }
1386         else {
1387                 ItemFolder *folder = server->addressCache->rootFolder;
1388                 node = folder->listFolder;
1389                 if (node) {
1390                         while (node) {
1391                                 AddrItemObject *aio = node->data;
1392                                 if (aio) {
1393                                         if (aio->type == ITEMTYPE_FOLDER) {
1394                                                 ItemFolder *folder = (ItemFolder *) aio;
1395                                                 GList *persons = folder->listPerson;
1396                                                 while (persons) {
1397                                                         AddrItemObject *aio = persons->data;
1398                                                         if (aio) {
1399                                                                 if (aio->type == ITEMTYPE_PERSON) {
1400                                                                         ItemPerson *item = (ItemPerson *) aio;
1401                                                                         gboolean result = ldapsvr_retrieve_item_person(item, contact);
1402                                                                         debug_print("Found contact to update: %s\n", result? "Yes" : "No");
1403                                                                         if (result) {
1404                                                                                 if (debug_get_mode()) {
1405                                                                                         gchar *uid = g_hash_table_lookup(contact, "uid");
1406                                                                                         item = ldapsvr_get_contact(server, uid);
1407                                                                                         addritem_print_item_person(item, stdout);
1408                                                                                 }
1409                                                                                 contacts = g_list_append(contacts, contact);
1410                                                                         }
1411                                                                 }
1412                                                         }
1413                                                         persons = g_list_next(persons);
1414                                                 }
1415                                         }
1416                                 }
1417                                 else {
1418                                         g_printerr("\t\tpid : ???\n");
1419                                 }
1420                                 node = g_list_next(node);
1421                         }
1422                 }
1423         }
1424         head = contacts;
1425         if (debug_get_mode()) {
1426                 if (contacts)
1427                         debug_print("Contacts which must be updated in LDAP:\n");
1428                 while (contacts) {
1429                         debug_print("\tContact:\n");
1430                         g_hash_table_foreach(contacts->data, 
1431                                 ldapsvr_print_contacts_hashtable, stderr);
1432                         contacts = g_list_next(contacts);
1433                 }
1434         }
1435         if (contacts == NULL)
1436                 contacts = head;
1437         while (contacts) {
1438                 gchar *status;
1439                 contact = (GHashTable *) contacts->data;
1440                 status = (gchar *) g_hash_table_lookup(contact, "status");
1441                 if (status == NULL)
1442                         status = g_strdup("NULL");
1443                 if (g_ascii_strcasecmp(status, "new") == 0) {
1444                         ldapsvr_add_contact(server, contact);
1445                 }
1446                 else if (g_ascii_strcasecmp(status, "update") == 0) {
1447                         ldapsvr_update_contact(server, contact);
1448                 }
1449                 else if (g_ascii_strcasecmp(status, "delete") == 0) {
1450                         ldapsvr_delete_contact(server, contact);
1451                 }
1452                 else
1453                         g_critical(_("ldapsvr_update_book->Unknown status: %s\n"), status);
1454                 contacts = g_list_next(contacts);
1455         }
1456         ldapsvr_free_hashtable(head);
1457 }
1458
1459 #endif  /* USE_LDAP */
1460
1461 /*
1462  * End of Source.
1463  */
1464