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