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