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