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