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