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