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