2007-04-17 [wwp] 2.9.0cvs3
[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 2 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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 /*
21  * Functions necessary to access LDAP servers.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #ifdef USE_LDAP
29
30 #include <glib.h>
31 #include <glib/gi18n.h>
32 #include <sys/time.h>
33 #include <string.h>
34 #include <ldap.h>
35 #include <lber.h>
36
37 #include "ldapupdate.h"
38 #include "mgutils.h"
39 #include "addritem.h"
40 #include "addrcache.h"
41 #include "ldapctrl.h"
42 #include "ldapquery.h"
43 #include "ldapserver.h"
44 #include "ldaputil.h"
45 #include "utils.h"
46 #include "adbookbase.h"
47
48 /**
49  * Structure to hold user defined attributes
50  * from contacts
51  */
52 typedef struct _AttrKeyValue AttrKeyValue;
53 struct _AttrKeyValue {
54         gchar *key;
55         gchar *value;
56 };
57
58 /**
59  * Structure to hold contact information.
60  * Each addressbook will have 0..N contacts.
61  */
62 typedef struct _EmailKeyValue EmailKeyValue;
63 struct _EmailKeyValue {
64         gchar *mail;
65         gchar *alias;
66         gchar *remarks;
67 };
68
69 /**
70  * Structure to hold information about RDN.
71  */
72 typedef struct _Rdn Rdn;
73 struct _Rdn {
74         gchar *attribute;
75         gchar *value;
76         gchar *new_dn;
77 };
78
79 /**
80  * Retrieve address group item for update.
81  * \param group  Group to print.
82  * \param array  GHashTAble of item_group, or <i>NULL</i> if none created.
83  */
84 void ldapsvr_retrieve_item_group(ItemGroup *group, GHashTable *array) {
85         /* Not implemented in this release */
86         g_return_if_fail(group != NULL);
87 }
88
89 /**
90  * Create an initial EmailKeyValue structure
91  * \return empty structure
92  */
93 EmailKeyValue *emailkeyvalue_create() {
94         EmailKeyValue *buf;
95
96         buf = g_new0(EmailKeyValue, 1);
97         buf->alias = NULL;
98         buf->mail = NULL;
99         buf->remarks = NULL;
100         return buf;
101 }
102
103 /**
104  * Create an initial AttrKeyValue structure
105  * \return empty structure
106  */
107 AttrKeyValue *attrkeyvalue_create() {
108         AttrKeyValue *buf;
109
110         buf = g_new0(AttrKeyValue, 1);
111         buf->key = NULL;
112         buf->value = NULL;
113         return buf;
114 }
115
116 /**
117  * Retrieve E-Mail address object for update.
118  * \param item   ItemEmail to update.
119  * \return object, or <i>NULL</i> if none created.
120  */
121 EmailKeyValue *ldapsvr_retrieve_item_email(ItemEMail *item) {
122         EmailKeyValue *newItem;
123         g_return_val_if_fail(item != NULL, NULL);
124         newItem = emailkeyvalue_create();               
125         newItem->alias = g_strdup(ADDRITEM_NAME(item));
126         newItem->mail = g_strdup(item->address);
127         newItem->remarks = g_strdup(item->remarks);
128         return newItem;
129 }
130
131 /**
132  * Retrieve user attribute object for update.
133  * \param item   UserAttribute to update.
134  * \return object, or <i>NULL</i> if none created.
135  */
136 AttrKeyValue *ldapsvr_retrieve_attribute(UserAttribute *item) {
137         AttrKeyValue *newItem;
138         g_return_val_if_fail(item != NULL, NULL);
139         newItem = attrkeyvalue_create();
140         newItem->key = g_strdup(item->name);
141         newItem->value = g_strdup(item->value);
142         return newItem;
143 }
144
145 /**
146  * Retrieve person object for update.
147  * \param person ItemPerson to update.
148  * \param array GHashTable with user input.
149  * \return false if update is not needed, or true if update is needed.
150  */
151 gboolean ldapsvr_retrieve_item_person(ItemPerson *person, GHashTable *array) {
152         GList *node, *attr;
153
154         g_return_val_if_fail(person != NULL, FALSE);
155         switch (person->status) {
156                 case NONE: return FALSE;
157                 case ADD_ENTRY: g_hash_table_insert(array, "status", "new"); break;
158                 case UPDATE_ENTRY: g_hash_table_insert(array, "status", "update"); break;
159                 case DELETE_ENTRY: g_hash_table_insert(array, "status", "delete"); break;
160                 default: g_critical(_("ldapsvr_retrieve_item_person->Unknown status: %d"), person->status);
161         }
162         g_hash_table_insert(array, "uid", ADDRITEM_ID(person));
163         g_hash_table_insert(array, "cn", ADDRITEM_NAME(person));
164         g_hash_table_insert(array, "givenName", person->firstName);
165         g_hash_table_insert(array, "sn", person->lastName);
166         g_hash_table_insert(array, "nickName", person->nickName);
167         g_hash_table_insert(array, "dn", person->externalID);
168         node = person->listEMail;
169         attr = NULL;
170         while (node) {
171                 EmailKeyValue *newEmail = ldapsvr_retrieve_item_email(node->data);
172                 if (newEmail)
173                         attr = g_list_append(attr, newEmail);
174                 node = g_list_next(node);
175         }
176         g_hash_table_insert(array, "mail", attr);
177 /* Not implemented in this release.
178         node = person->listAttrib;
179         attr = NULL;
180         while (node) {
181                 AttrKeyValue *newAttr = ldapsvr_retrieve_attribute(node->data)
182                 if (newAttr)
183                         attr = g_list_append(attr, newAttr);
184                 node = g_list_next(node);
185         }
186         g_hash_table_insert(array, "attribute", attr);
187 */
188         return TRUE;
189 }
190
191 /**
192  * Print contents of contacts hashtable for debug.
193  * This function must be called with g_hash_table_foreach.
194  * \param key Key to process.
195  * \param data Data to process.
196  * \param fd Output stream.
197  */
198 void ldapsvr_print_contacts_hashtable(gpointer key, gpointer data, gpointer fd) {
199         gchar *keyName = (gchar *) key;
200         GList *node;
201
202         if (g_ascii_strcasecmp("mail", keyName) == 0) {
203                 node = (GList *) data;
204                 while (node) {
205                         EmailKeyValue *item = node->data;
206                         if (debug_get_mode()) {
207                                 debug_print("\t\talias = %s\n", item->alias);
208                                 debug_print("\t\tmail = %s\n", item->mail);
209                                 debug_print("\t\tremarks = %s\n", item->remarks);
210                         }
211                         else if (fd) {
212                                 FILE *stream = (FILE *) fd;
213                                 fprintf(stream, "\t\talias = %s\n", item->alias);
214                                 fprintf(stream, "\t\tmail = %s\n", item->mail);
215                                 fprintf(stream, "\t\tremarks = %s\n", item->remarks);
216                         }
217                         node = g_list_next(node);
218                 }
219         }
220         else if (g_ascii_strcasecmp("attribute", keyName) == 0) {
221                 node = (GList *) data;
222                 while (node) {
223                         AttrKeyValue *item = node->data;
224                         if (debug_get_mode()) {
225                                 debug_print("\t\t%s = %s\n", item->key, item->value);
226                         }
227                         else if (fd) {
228                                 FILE *stream = (FILE *) fd;
229                                 fprintf(stream, "\t\t%s = %s\n", item->key, item->value);
230                         }
231                         node = g_list_next(node);
232                 }
233         }
234         else {
235                 if (debug_get_mode())
236                         debug_print("\t\t%s = %s\n", keyName, (gchar *) data);
237                 else if (fd) {
238                         FILE *stream = (FILE *) fd;
239                         fprintf(stream, "\t\t%s = %s\n", keyName, (gchar *) data);
240                 }
241         }
242 }
243
244 /**
245  * Free list of changed contacts
246  *
247  * \param list List of GHashTable
248  */
249 void ldapsvr_free_hashtable(GList *list) {
250         while (list) {
251                 g_hash_table_destroy(list->data);
252                 list = g_list_next(list);
253         }
254         g_list_free(list);
255 }
256
257 /**
258  * Get person object from cache
259  *
260  * \param server Resource to LDAP
261  * \param uid PersonID in cache
262  * \return person object, or <i>NULL</i> if fail
263  */
264 ItemPerson *ldapsvr_get_contact(LdapServer *server, gchar *uid) {
265         AddrItemObject *aio;
266         g_return_val_if_fail(server != NULL || uid != NULL, NULL);
267         aio = addrcache_get_object(server->addressCache, uid);
268         if (aio) {
269                 if(aio->type == ITEMTYPE_PERSON) {
270                         return (ItemPerson *) aio;
271                 }
272         }
273         return NULL;
274 }
275
276 /**
277  * Connect to LDAP server.
278  * \param  ctl Control object to process.
279  * \return LDAP Resource to LDAP.
280  */
281 LDAP *ldapsvr_connect(LdapControl *ctl) {
282         LDAP *ld = NULL;
283         gint rc;
284         gint version;
285         gchar *uri = NULL;
286
287         g_return_val_if_fail(ctl != NULL, NULL);
288
289         uri = g_strdup_printf("ldap%s://%s:%d",
290                                 ctl->enableSSL?"s":"",
291                                 ctl->hostName, ctl->port);
292         ldap_initialize(&ld, uri);
293         g_free(uri);
294
295         if (ld == NULL)
296                 return NULL;
297
298         debug_print("connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port);
299
300 #ifdef USE_LDAP_TLS
301         /* Handle TLS */
302         version = LDAP_VERSION3;
303         rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
304         if (rc == LDAP_OPT_SUCCESS) {
305                 ctl->version = LDAP_VERSION3;
306         }
307
308         if (ctl->version == LDAP_VERSION3) {
309                 if (ctl->enableTLS && !ctl->enableSSL) {
310                         rc = ldap_start_tls_s(ld, NULL, NULL);
311                         
312                         if (rc != LDAP_SUCCESS) {
313                                 fprintf(stderr, "LDAP Error(tls): ldap_simple_bind_s: %s\n",
314                                         ldap_err2string(rc));
315                                 return NULL;
316                         }
317                 }
318         }
319 #endif
320
321         /* Bind to the server, if required */
322         if (ctl->bindDN) {
323                 if (* ctl->bindDN != '\0') {
324                         rc = claws_ldap_simple_bind_s(ld, ctl->bindDN, ctl->bindPass);
325                         if (rc != LDAP_SUCCESS) {
326                                 fprintf(stderr, "bindDN: %s, bindPass: %s\n", ctl->bindDN, ctl->bindPass);
327                                 fprintf(stderr, "LDAP Error(bind): ldap_simple_bind_s: %s\n",
328                                         ldap_err2string(rc));
329                                 return NULL;
330                         }
331                 }
332         }
333         return ld;
334 }
335
336 /**
337  * Disconnect to LDAP server.
338  * \param ld Resource to LDAP.
339  */
340 void ldapsvr_disconnect(LDAP *ld) {
341         /* Disconnect */
342         g_return_if_fail(ld != NULL);
343         ldap_unbind_ext(ld, NULL, NULL);
344 }
345
346 /**
347  * Create an initial Rdn structure
348  *
349  * \return empty structure
350  */
351 Rdn *rdn_create() {
352         Rdn *buf;
353
354         buf = g_new0(Rdn, 1);
355         buf->attribute = NULL;
356         buf->value = NULL;
357         buf->new_dn = NULL;
358         return buf;
359 }
360
361 /**
362  * update Rdn structure
363  *
364  * \param rdn Rdn structure to update
365  * \param head Uniq part of dn
366  * \param tail Common part of dn
367  */
368 void update_rdn(Rdn *rdn, gchar *head, gchar *tail) {
369         rdn->value = g_strdup(head);
370         rdn->new_dn = g_strdup_printf("mail=%s%s", head, tail);
371 }
372
373 /**
374  * Deside if dn needs to be changed
375  *
376  * \param hash GHashTable with user input.
377  * \param dn dn for current object
378  * \return Rdn structure
379  */
380 Rdn *ldapsvr_modify_dn(GHashTable *hash, gchar *dn) {
381         Rdn *rdn;
382         gchar *pos, *compare;
383         g_return_val_if_fail(hash != NULL || dn != NULL, NULL);
384         
385         pos = g_strstr_len(dn, strlen(dn), "=");
386         compare = g_strndup(dn, pos - dn);
387
388         if (!pos)
389                 return NULL;
390         pos++;
391         gchar *rest = g_strstr_len(pos, strlen(pos), ",");
392         gchar *val = g_strndup(pos, rest - pos);
393         if (val == NULL) {
394                 if (compare)
395                         g_free(compare);
396                 return NULL;
397         }
398         rdn = rdn_create();
399         rdn->value = g_strdup(val);
400         rdn->attribute = g_strdup(compare);
401         g_free(val);
402         if (strcmp("mail", rdn->attribute) == 0) {
403                 GList *list = g_hash_table_lookup(hash, rdn->attribute);
404                 while (list) {
405                         EmailKeyValue *item = list->data;
406                         compare = g_strdup((gchar *) item->mail);
407                         if (strcmp(compare, rdn->value) == 0) {
408                                 update_rdn(rdn, compare, rest);
409                                 g_free(compare);
410                                 return rdn;
411                         }
412                         list = g_list_next(list);
413                 }
414                 /* if compare and rdn->attribute are equal then last email removed/empty  */
415                 if (strcmp(compare, rdn->attribute) != 0) {
416                         /* RDN changed. Find new */
417                         update_rdn(rdn, compare, rest);
418                         g_free(compare);
419                         return rdn;
420                 }
421                 else {
422                         /* We cannot remove dn */
423                         g_free(compare);
424                         return NULL;
425                 }
426         }
427         else {
428                 compare = g_hash_table_lookup(hash, rdn->attribute);
429                 /* if compare and rdn->attribute are equal then dn removed/empty */
430                 if (strcmp(compare, rdn->attribute) != 0) {
431                         update_rdn(rdn, compare, rest);
432                         return rdn;
433                 }
434                 else {
435                         /* We cannot remove dn */
436                         return NULL;
437                 }
438         }
439         return NULL;
440 }
441
442 /**
443  * This macro is borrowed from the Balsa project
444  * Creates a LDAPMod structure
445  *
446  * \param mods Empty LDAPMod structure
447  * \param modarr Array with values to insert
448  * \param op Operation to perform on LDAP
449  * \param attr Attribute to insert
450  * \param strv Empty array which is NULL terminated
451  * \param val Value for attribute
452  */
453 #define SETMOD(mods,modarr,op,attr,strv,val) \
454    do { (mods) = &(modarr); (modarr).mod_type=attr; (modarr).mod_op=op;\
455         (strv)[0]=(val); (modarr).mod_values=strv; \
456       } while(0)
457
458 /**
459  * Creates a LDAPMod structure
460  *
461  * \param mods Empty LDAPMod structure
462  * \param modarr Array with values to insert
463  * \param op Operation to perform on LDAP
464  * \param attr Attribute to insert
465  * \param strv Array with values to insert. Must be NULL terminated
466  */
467 #define SETMODS(mods,modarr,op,attr,strv) \
468    do { (mods) = &(modarr); (modarr).mod_type=attr; \
469                 (modarr).mod_op=op; (modarr).mod_values=strv; \
470       } while(0)
471 #define MODSIZE 10
472
473 /**
474  * Clean up, close LDAP connection, and refresh cache
475  *
476  * \param ld Resource to LDAP
477  * \param server AddressBook resource
478  * \param contact GHashTable with current changed object
479  */
480 void clean_up(LDAP *ld, LdapServer *server, GHashTable *contact) {
481         ItemPerson *person = 
482                 ldapsvr_get_contact(server, g_hash_table_lookup(contact , "uid"));
483         if (person) {
484                 gchar *displayName;
485                 person->status = NONE;
486                 displayName = g_hash_table_lookup(contact, "displayName");
487                 if (displayName)
488                         person->nickName = g_strdup(displayName);
489         }
490         if (ld)
491                 ldapsvr_disconnect(ld);
492         ldapsvr_force_refresh(server);
493 /*      ldapsvr_free_all_query(server);*/
494 }
495
496 /**
497  * Get cn attribute from dn
498  *
499  * \param dn Distinguesh Name for current object
500  * \return AttrKeyValue, or <i>NULL</i> if none created
501  */
502 AttrKeyValue *get_cn(gchar *dn) {
503         AttrKeyValue *cn;
504         gchar *start;
505         gchar *end;
506         gchar *item;
507         gchar **key_value;
508         g_return_val_if_fail(dn != NULL, NULL);
509         
510         cn = attrkeyvalue_create();
511         start = g_strstr_len(dn, strlen(dn), "cn");
512         if (start == NULL)
513                 return NULL;
514         end = g_strstr_len(start, strlen(start), ",");
515         item = g_strndup(start, end - start);
516         if (item == NULL)
517                 return NULL;
518         key_value = g_strsplit(item, "=", 2);
519         cn->key = g_strdup(key_value[0]);
520         cn->value = g_strdup(key_value[1]);
521         g_strfreev(key_value);
522         g_free(item);
523         return cn;
524 }
525
526 /**
527  * Get ou or o attribute from dn
528  *
529  * \param dn Distinguesh Name for current object
530  * \return AttrKeyValue, or <i>NULL</i> if none created
531  */
532 AttrKeyValue *get_ou(gchar *dn) {
533         AttrKeyValue *ou;
534         gchar *start;
535         gchar *end;
536         gchar *item;
537         gchar **key_value;
538         
539         g_return_val_if_fail(dn != NULL, NULL);
540         ou = attrkeyvalue_create();
541         start = g_strstr_len(dn, strlen(dn), ",o=");
542         if (start == NULL)
543                 start = g_strstr_len(dn, strlen(dn), ",ou=");
544         if (start == NULL)
545                 return NULL;
546         start++;
547         end = g_strstr_len(start, strlen(start), ",");
548         item = g_strndup(start, end - start);
549         if (item == NULL)
550                 return NULL;
551         key_value = g_strsplit(item, "=", 2);
552         ou->key = g_strdup(key_value[0]);
553         ou->value = g_strdup(key_value[1]);
554         g_strfreev(key_value);
555         g_free(item);
556         return ou;
557 }
558
559 /**
560  * Print the contents of a LDAPMod structure for debuging purposes
561  *
562  * \param mods LDAPMod structure
563  */
564 void ldapsvr_print_ldapmod(LDAPMod *mods[]) {
565         gchar *mod_op;
566         int i;
567
568         fprintf( stderr, "Type\n");
569         for (i = 0; NULL != mods[i]; i++) {
570                 LDAPMod *mod = (LDAPMod *) mods[i];
571                 gchar **vals;
572                 switch (mod->mod_op) {
573                         case LDAP_MOD_ADD: mod_op = g_strdup("ADD"); break;
574                         case LDAP_MOD_REPLACE: mod_op = g_strdup("MODIFY"); break;
575                         case LDAP_MOD_DELETE: mod_op = g_strdup("DELETE"); break;
576                         default: mod_op = g_strdup("UNKNOWN");
577                 }
578                 fprintf( stderr, "Operation: %s\tType:%s\nValues:\n", mod_op, mod->mod_type);
579                 vals = mod->mod_vals.modv_strvals;
580                 while (*vals) {
581                         fprintf( stderr, "\t%s\n", *vals++);
582                 }
583         }
584 }
585
586 /**
587  * Add new contact to LDAP
588  *
589  * \param server AddressBook resource
590  * \param contact GHashTable with object to add
591  */
592 void ldapsvr_add_contact(LdapServer *server, GHashTable *contact) {
593         gchar *email = NULL, *param = NULL;
594         LDAP *ld = NULL;
595         LDAPMod *mods[MODSIZE];
596         LDAPMod modarr[7];
597         gint cnt = 0;
598         char *cn[] = {NULL, NULL};
599         char *displayName[] = {NULL, NULL};
600         char *givenName[] = {NULL, NULL};
601         char **mail = NULL;
602         char *sn[] = {NULL, NULL};
603         char *org[] = {NULL, NULL};
604         char *obj[] = {/*"top",*/ "person", "organizationalPerson", "inetOrgPerson", NULL}; 
605         int rc=0;
606         GList *node;
607         AttrKeyValue *ou, *commonName;
608         ItemPerson *person;
609         gchar *base_dn;
610         GList *mailList;
611
612         g_return_if_fail(server != NULL || contact != NULL);
613         node = g_hash_table_lookup(contact , "mail");
614         if (node) {
615                 EmailKeyValue *newEmail = node->data;
616                 email = g_strdup(newEmail->mail);
617         }
618         if (email == NULL) {
619                 server->retVal = LDAPRC_NODN;
620                 return;
621         }
622         base_dn = g_strdup_printf("mail=%s,%s", email, server->control->baseDN);
623         g_free(email);
624         person = 
625                 ldapsvr_get_contact(server, g_hash_table_lookup(contact , "uid"));
626         person->externalID = g_strdup(base_dn);
627         debug_print("dn: %s\n", base_dn);
628         ld = ldapsvr_connect(server->control);
629         if (ld == NULL) {
630                 clean_up(ld, server, contact);
631                 debug_print("no ldap found\n");
632                 return;
633         }
634         SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "objectClass", obj);
635         cnt++;
636         ou = get_ou(base_dn);
637         if (ou != NULL) {
638                 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, ou->key, org, ou->value);
639                 cnt++;
640         }
641         
642         commonName = get_cn(base_dn);
643         if (commonName == NULL) {
644                 param = g_hash_table_lookup(contact , "cn");
645                 if (param) {
646                         SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "cn", cn, param);
647                 }
648                 else {
649                         clean_up(ld, server, contact);
650                         debug_print("no CN found\n");
651                         return;
652                 }
653         }
654         else {
655                 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, commonName->key, cn, commonName->value);
656                 cnt++;
657                 param = g_hash_table_lookup(contact , "cn");
658                 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "displayName", displayName, param);
659                 g_hash_table_insert(contact, "displayName", param);
660         }
661         cnt++;
662         param = g_hash_table_lookup(contact , "givenName");
663         if (param) {
664                 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "givenName", givenName, param);
665                 cnt++;
666         }
667         mailList = g_hash_table_lookup(contact , "mail");
668         if (mailList) {
669                 mail = g_malloc(sizeof(*mail));
670                 char **tmp = g_malloc(sizeof(*tmp));
671                 mail = tmp;
672                 while (mailList) {
673                         EmailKeyValue *item = mailList->data;
674                         *tmp++ = g_strdup((gchar *) item->mail);
675                         mailList = g_list_next(mailList);
676                 }
677                 *tmp = NULL;
678                 SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "mail", mail);
679                 cnt++;
680         }
681         param = g_hash_table_lookup(contact, "sn");
682         if (param == NULL)
683                 param = g_strdup(N_("Some SN"));
684         SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_ADD, "sn", sn, param);
685         cnt++;
686         mods[cnt] = NULL;
687         if (debug_get_mode()) {
688                 ldapsvr_print_ldapmod(mods);
689         }
690         server->retVal = LDAPRC_SUCCESS;
691         rc = ldap_add_ext_s(ld, base_dn, mods, NULL, NULL);
692         if (rc) {
693                 switch (rc) {
694                         case LDAP_ALREADY_EXISTS: 
695                                 server->retVal = LDAPRC_ALREADY_EXIST;
696                                 break;
697                         default:
698                                 fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",base_dn, rc, ldap_err2string(rc));
699                                 if (rc == 0x8)
700                                         server->retVal = LDAPRC_STRONG_AUTH;
701                                 else
702                                         server->retVal = LDAPRC_NAMING_VIOLATION;
703                 }
704         }
705         g_free(base_dn);
706         clean_up(ld, server, contact);
707 }
708
709 /**
710  * Update contact to LDAP
711  *
712  * \param server AddressBook resource
713  * \param contact GHashTable with object to update
714  */
715 void ldapsvr_update_contact(LdapServer *server, GHashTable *contact) {
716         LDAP *ld = NULL;
717         LDAPMod *mods[MODSIZE];
718         LDAPMod modarr[4];
719         gint cnt = 0;
720         gchar *param, *dn;
721         Rdn *NoRemove = NULL;
722         char *cn[] = {NULL, NULL};
723         char *givenName[] = {NULL, NULL};
724         char **mail = NULL;
725         char *sn[] = {NULL, NULL};
726         GList *mailList;
727
728         g_return_if_fail(server != NULL || contact != NULL);
729         ld = ldapsvr_connect(server->control);
730         if (ld == NULL) {
731                 clean_up(ld, server, contact);
732                 return;
733         }
734         dn = g_hash_table_lookup(contact, "dn");
735         if (dn == NULL) {
736                 clean_up(ld, server, contact);
737                 return;
738         }
739         NoRemove = ldapsvr_modify_dn(contact, dn);
740         if (NoRemove) {
741                 /* We are trying to change RDN */
742                 gchar *newRdn = g_strdup_printf("%s=%s", NoRemove->attribute, NoRemove->value);
743                 int rc = ldap_modrdn2_s(ld, dn, newRdn, 1);
744                 if(rc != LDAP_SUCCESS) {
745                         if (rc ==  LDAP_ALREADY_EXISTS) {
746                                 /* We are messing with a contact with more than one listed email
747                                  * address and the email we are changing is not the one used for dn
748                                  */
749                                 /* It needs to be able to handle renaming errors to an already defined
750                                  * dn. For now we just refuse the update. It will be caught later on as
751                                  * a LDAPRC_NAMING_VIOLATION error.
752                                  */
753                         }
754                         else {
755                                 fprintf(stderr, "Current dn: %s\n", dn);
756                                 fprintf(stderr, "new dn: %s\n", newRdn);
757                                 fprintf(stderr, "LDAP Error(ldap_modrdn2_s) failed[0x%x]: %s\n", rc, ldap_err2string(rc));
758                                 g_free(newRdn);
759                                 return;
760                         }
761                 }
762                 else {
763                         g_free(newRdn);
764                         dn = g_strdup(NoRemove->new_dn);
765                 }
766         }
767         else {
768                 server->retVal = LDAPRC_NODN;
769                 clean_up(ld, server, contact);
770                 return;
771         }
772         param = g_hash_table_lookup(contact , "cn");
773         if (param && (strcmp(param, NoRemove->value) != 0 && strcmp("cn", NoRemove->attribute) != 0)) {
774                 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "displayName", cn, param);
775                 cnt++;
776                 g_hash_table_insert(contact, "displayName", param);
777         }
778         param = g_hash_table_lookup(contact , "givenName");
779         if (param && (strcmp(param, NoRemove->value) != 0 && strcmp("givenName", NoRemove->attribute) != 0)) {
780                 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "givenName", givenName, param);
781                 cnt++;
782         }
783         mailList = g_hash_table_lookup(contact , "mail");
784         if (mailList) {
785                 if (!(strcmp("mail", NoRemove->attribute) == 0 && g_list_length(mailList) == 1)) {
786                         mail = g_malloc(sizeof(*mail));
787                         char **tmp = g_malloc(sizeof(*tmp));
788                         mail = tmp;
789                         while (mailList) {
790                                 EmailKeyValue *item = mailList->data;
791                                 *tmp++ = g_strdup((gchar *) item->mail);
792                                 mailList = g_list_next(mailList);
793                         }
794                         *tmp = NULL;
795                         SETMODS(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "mail", mail);
796                         cnt++;
797                 }
798         }
799         param = g_hash_table_lookup(contact , "sn");
800         if (param && (strcmp(param, NoRemove->value) != 0 && strcmp("sn", NoRemove->attribute) != 0)) {
801                 SETMOD(mods[cnt], modarr[cnt], LDAP_MOD_REPLACE, "sn", sn, param);
802                 cnt++;
803         }
804         debug_print("newDN: %s\n", dn);
805         if (NoRemove)
806                 g_free(NoRemove);
807         server->retVal = LDAPRC_SUCCESS;
808         if (cnt > 0) {
809                 int rc;
810                 mods[cnt] = NULL;
811                 rc = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
812                 if (rc) {
813                         fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",
814                     dn, rc, ldap_err2string(rc));
815                         server->retVal = LDAPRC_NAMING_VIOLATION;
816                 }
817                 if (mail)
818                         g_free(mail);
819         }
820         clean_up(ld, server, contact);
821 }
822
823 /**
824  * Delete contact from LDAP
825  *
826  * \param server AddressBook resource
827  * \param contact GHashTable with object to delete
828  */
829 void ldapsvr_delete_contact(LdapServer *server, GHashTable *contact) {
830         LDAP *ld = NULL;
831         gchar *dn;
832         int rc;
833
834         g_return_if_fail(server != NULL || contact != NULL);
835         ld = ldapsvr_connect(server->control);
836         if (ld == NULL) {
837                 clean_up(ld, server, contact);
838                 return;
839         }
840         dn = g_hash_table_lookup(contact, "dn");
841         if (dn == NULL) {
842                 clean_up(ld, server, contact);
843                 return;
844         }
845         rc = ldap_delete_ext_s(ld, dn, NULL, NULL);
846         if (rc) {
847                 fprintf(stderr, "ldap_modify for dn=%s\" failed[0x%x]: %s\n",
848                                 dn, rc, ldap_err2string(rc));
849                 server->retVal = LDAPRC_NODN;
850         }
851         clean_up(ld, server, contact);
852 }
853
854 /**
855  * Update any changes to the server.
856  *
857  * \param server AddressBook resource.
858  * \param person ItemPerson holding user input.
859  */
860 void ldapsvr_update_book(LdapServer *server, ItemPerson *item) {
861         GList *node = NULL;
862         GHashTable *contact = NULL;
863         GList *contacts = NULL, *head = NULL;
864
865         g_return_if_fail(server != NULL);
866         debug_print("updating ldap addressbook\n");
867
868         contact = g_hash_table_new(g_str_hash, g_str_equal);
869         if (item) {
870                 gboolean result = ldapsvr_retrieve_item_person(item, contact);
871                 debug_print("Found contact to update: %s\n", result? "Yes" : "No");
872                 if (result) {
873                         if (debug_get_mode()) {
874                                 addritem_print_item_person(item, stdout);
875                         }
876                         contacts = g_list_append(contacts, contact);
877                 }
878         }
879         else {
880                 ItemFolder *folder = server->addressCache->rootFolder;
881                 node = folder->listFolder;
882                 if (node)
883                         node = g_list_copy(node);
884                 node = g_list_prepend(node, server->addressCache->rootFolder);
885                 if (node) {
886                         while (node) {
887                                 AddrItemObject *aio = node->data;
888                                 if (aio) {
889                                         if (aio->type == ITEMTYPE_FOLDER) {
890                                                 ItemFolder *folder = (ItemFolder *) aio;
891                                                 GList *persons = folder->listPerson;
892                                                 while (persons) {
893                                                         AddrItemObject *aio = persons->data;
894                                                         if (aio) {
895                                                                 if (aio->type == ITEMTYPE_PERSON) {
896                                                                         ItemPerson *item = (ItemPerson *) aio;
897                                                                         gboolean result = ldapsvr_retrieve_item_person(item, contact);
898                                                                         debug_print("Found contact to update: %s\n", result? "Yes" : "No");
899                                                                         if (result) {
900                                                                                 if (debug_get_mode()) {
901                                                                                         gchar *uid = g_hash_table_lookup(contact, "uid");
902                                                                                         item = ldapsvr_get_contact(server, uid);
903                                                                                         addritem_print_item_person(item, stdout);
904                                                                                 }
905                                                                                 contacts = g_list_append(contacts, contact);
906                                                                         }
907                                                                 }
908                                                         }
909                                                         persons = g_list_next(persons);
910                                                 }
911                                         }
912                                 }
913                                 else {
914                                         fprintf(stderr, "\t\tpid : ???\n");
915                                 }
916                                 node = g_list_next(node);
917                         }
918                         g_list_free(node);
919                 }
920         }
921         head = contacts;
922         if (debug_get_mode()) {
923                 if (contacts)
924                         debug_print("Contacts which must be updated in LDAP:\n");
925                 while (contacts) {
926                         debug_print("\tContact:\n");
927                         g_hash_table_foreach(contacts->data, 
928                                 ldapsvr_print_contacts_hashtable, stderr);
929                         contacts = g_list_next(contacts);
930                 }
931         }
932         if (contacts == NULL)
933                 contacts = head;
934         while (contacts) {
935                 gchar *status;
936                 contact = (GHashTable *) contacts->data;
937                 status = (gchar *) g_hash_table_lookup(contact, "status");
938                 if (status == NULL)
939                         status = g_strdup("NULL");
940                 if (g_ascii_strcasecmp(status, "new") == 0) {
941                         ldapsvr_add_contact(server, contact);
942                 }
943                 else if (g_ascii_strcasecmp(status, "update") == 0) {
944                         ldapsvr_update_contact(server, contact);
945                 }
946                 else if (g_ascii_strcasecmp(status, "delete") == 0) {
947                         ldapsvr_delete_contact(server, contact);
948                 }
949                 else
950                         g_critical(_("ldapsvr_update_book->Unknown status: %s\n"), status);
951                 contacts = g_list_next(contacts);
952         }
953         ldapsvr_free_hashtable(head);
954 }
955
956 #endif  /* USE_LDAP */
957
958 /*
959  * End of Source.
960  */
961