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