[Commits] ldap-plugin.c 1.8 1.9
miras at claws-mail.org
miras at claws-mail.org
Mon Nov 28 08:34:52 CET 2011
Update of /home/claws-mail/contacts/plugins/ldap
In directory claws-mail:/tmp/cvs-serv28372/plugins/ldap
Modified Files:
ldap-plugin.c
Log Message:
2011-11-28 [mir] 0.6.0cvs28
* plugins/ldap/ldap-plugin.c
* src/callbacks.c
* src/contactwindow.c
* src/printing.c
* src/utils.c
* src/utils.h
-Fix lots of bugs
-Improfements
-Refactoring
-And last: Complete LDAP plugin with full support
for write/update. Testing is adviceable.
Index: ldap-plugin.c
===================================================================
RCS file: /home/claws-mail/contacts/plugins/ldap/ldap-plugin.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- ldap-plugin.c 22 Nov 2011 00:10:53 -0000 1.8
+++ ldap-plugin.c 28 Nov 2011 07:34:49 -0000 1.9
@@ -79,6 +79,11 @@
static GList* abooks = NULL;
/* List of closed AddressBook */
static GList* closed_books = NULL;
+
+/* Object classes used by the plugin */
+static const gchar* objectclass[] = {
+ "person", "organizationalPerson", "inetOrgPerson", NULL};
+static const gchar* required_attributes[] = {"cn", "sn", NULL};
/*
* HashTable containing names of supported attributes.
* Each keys holds an AttribDef.
@@ -634,6 +639,29 @@
return attr;
}
+static gchar** ldap2native_list(const gchar** list) {
+ gchar **new_list, **tmp, **head;
+ guint len;
+
+ if (! list)
+ return list;
+
+ len = g_strv_length(list);
+ new_list = g_new0(gchar *, len + 1);
+
+ if (len < 1)
+ return new_list;
+
+ tmp = list;
+ head = new_list;
+ while (*tmp) {
+ debug_print("%s : %s\n", *tmp, native2ldap(*tmp));
+ *new_list++ = g_strdup(native2ldap(*tmp++));
+ }
+
+ return head;
+}
+
static gchar** attriblist2ldap() {
GHashTableIter iter;
gpointer key, value;
@@ -753,39 +781,38 @@
return g_strconcat("(", token, "=", search, ")", NULL);
}
-#define FILTER "(objectClass=*)"
-static gchar* filter_build(Contact* contact, gboolean intersection) {
+static gchar* filter_string_object(const gchar** list, const gchar* object) {
GString* filter;
- //gchar* token;
-
- if (contact) {
- //if (i > 0 && !(i % 2)) {
- // if (intersection)
- // filter = g_string_prepend(filter, "(&");
- // else
- // filter = g_string_prepend(filter, "(|");
- // filter = g_string_append(filter, token);
- // filter = g_string_append(filter, ")");
- //}
- //else
- // filter = g_string_append(filter, token);
- //i++;
- }
- else {
- filter = g_string_new(FILTER);
+ gchar *token, **head;
+ gboolean multi = FALSE;
+
+ filter = g_string_new("");
+ head = list;
+ while (head && *head) {
+ token = filter_token_add(object, *head++);
+ if (multi) {
+ filter = g_string_prepend(filter, "(|");
+ filter = g_string_append(filter, token);
+ filter = g_string_append(filter, ")");
+ }
+ else
+ filter = g_string_append(filter, token);
+ multi = TRUE;
+ g_free(token);
}
return g_string_free(filter, FALSE);
}
-static gchar* filter_build_standard() {
- gchar** list = standard_attribs;
- gchar* token;
- GString* filter = g_string_new("");
+static gchar* filter_string_value(const gchar** list, const gchar* value) {
+ GString* filter;
+ gchar *token, **head;
gboolean multi = FALSE;
- while(*list) {
- token = filter_token_add(native2ldap(*list++), "*");
+ filter = g_string_new("");
+ head = list;
+ while (head && *head) {
+ token = filter_token_add(*head++, value);
if (multi) {
filter = g_string_prepend(filter, "(|");
filter = g_string_append(filter, token);
@@ -800,6 +827,42 @@
return g_string_free(filter, FALSE);
}
+static gchar* filter_build(Contact* contact, gboolean intersection) {
+ GString* filter = g_string_new("");
+ gchar* token;
+ gboolean multi = FALSE;
+
+ token = filter_string_object(objectclass, "objectClass");
+ filter = g_string_prepend(filter, token);
+ g_free(token);
+
+ if (contact) {
+ if (multi) {
+ if (intersection)
+ filter = g_string_prepend(filter, "(&");
+ else
+ filter = g_string_prepend(filter, "(|");
+ filter = g_string_append(filter, token);
+ filter = g_string_append(filter, ")");
+ }
+ else
+ filter = g_string_append(filter, token);
+ }
+
+ return g_string_free(filter, FALSE);
+}
+
+static gchar* filter_build_standard() {
+ gchar **list;
+ gchar* filter;
+
+ list = ldap2native_list(standard_attribs);
+ filter = filter_string_value(list, "*");
+ g_strfreev(list);
+
+ return filter;
+}
+
static void gslist_free_ldapmessage(GSList** list) {
GSList* cur;
@@ -887,6 +950,8 @@
contact = contact_new();
value = ldap_get_dn(server->ldap, entry);
debug_print("Found: DN->%s\n", value);
+ AttribDef* attr = pack_data(ATTRIB_TYPE_STRING, "dn", value);
+ g_hash_table_insert(contact->data, g_strdup("dn"), attr);
ldap_memfree(value);
/* Process attributes */
for (attribute = ldap_first_attribute(server->ldap, entry, &ber);
@@ -1226,6 +1291,205 @@
return contacts;
}
+static gboolean contact_has_dn(Contact* contact) {
+ gpointer key, val;
+
+ return g_hash_table_lookup_extended(contact->data, "dn", &key, &val);
+}
+
+static gchar* contact_get_dn(AbookConnection* ac) {
+ gchar* dn = NULL;
+ GSList* cur;
+
+ if (! ac->contact)
+ dn = create_dummy_dn(ac->server->dn);
+ else if (! contact_has_dn(ac->contact)) {
+ cur = ac->contact->emails;
+ while(cur && ! dn) {
+ Email* e = (Email *) cur->data;
+ if (e->email)
+ dn = g_strconcat("mail=", e->email, ",", ac->server->dn, NULL);
+ else
+ cur = cur->next;
+ }
+ if (! dn) {
+ gchar* tmp = create_dummy_dn(NULL);
+ dn = g_strconcat("mail=", tmp, ",", ac->server->dn, NULL);
+ Email* a = g_new0(Email, 1);
+ a->email = g_strdup(tmp);
+ g_free(tmp);
+ ac->contact->emails = g_slist_append(ac->contact->emails, a);
+ }
+ }
+ else {
+ extract_data(ac->contact->data, "dn", (gpointer) &dn);
+ }
+
+ return dn;
+}
+
+static void print_ldapmod(LDAPMod *mods[]) {
+ gchar *mod_op;
+ int i;
+
+ for (i = 0; NULL != mods[i]; i++) {
+ LDAPMod *mod = (LDAPMod *) mods[i];
+ gchar **vals;
+ switch (mod->mod_op) {
+ case LDAP_MOD_ADD:
+ mod_op = g_strdup("ADD");
+ break;
+ case LDAP_MOD_REPLACE:
+ mod_op = g_strdup("MODIFY");
+ break;
+ case LDAP_MOD_DELETE:
+ mod_op = g_strdup("DELETE");
+ break;
+ default: mod_op = g_strdup("UNKNOWN");
+ }
+ debug_print("Operation: %s\tType:%s\nValues:\n",
+ mod_op, mod->mod_type);
+ g_free(mod_op);
+ vals = mod->mod_vals.modv_strvals;
+ while (*vals) {
+ debug_print("\t%s\n", *vals++);
+ }
+ }
+}
+
+static void init_mods(gchar* mods[][2], guint size) {
+ guint i;
+
+ for (i = 0; i < size; i++) {
+ mods[i][0] = mods[i][1] = NULL;
+ }
+}
+
+static gchar** get_object_classes() {
+ gchar** obj;
+ guint size = g_strv_length(objectclass) + 1;
+ int i;
+
+ obj = g_new0(gchar *, size);
+ for (i = 0; i < size; i++)
+ obj[i] = g_strdup(objectclass[i]);
+
+ return obj;
+}
+
+static void mods_free(LDAPMod *mods[]) {
+ int i;
+ gchar** vals;
+
+ for (i = 0; NULL != mods[i]; i++) {
+ LDAPMod* mod = (LDAPMod *) mods[i];
+ g_free(mod->mod_type);
+ vals = mod->mod_vals.modv_strvals;
+ while (*vals) {
+ g_free(*vals++);
+ }
+ }
+}
+
+static gboolean has_required_info(Contact* contact) {
+ gboolean has = TRUE;
+ gchar* attr = NULL;
+ const gchar** required = required_attributes;
+
+ while (*required && has) {
+ const gchar* key = ldap2native(*required++);
+ extract_data(contact->data, key, (gpointer) &attr);
+ if (!attr || strlen(attr) < 1)
+ has = FALSE;
+ g_free(attr);
+ attr = NULL;
+ }
+
+ return has;
+}
+
+static Contact* address_book_contact_get(AddressBook* abook, const gchar* dn) {
+ GList* cur;
+ Contact* contact = NULL;
+ gchar* uid;
+
+ if (! abook || ! dn)
+ return NULL;
+
+ cur = abook->contacts;
+ while (cur && ! contact) {
+ contact = (Contact *) cur->data;
+ extract_data(contact->data, "dn", (gpointer) &uid);
+ if (! uid || utf8_collate(dn, uid) != 0) {
+ contact = NULL;
+ cur = cur->next;
+ }
+ g_free(uid);
+ }
+
+ return contact;
+}
+
+static gchar* extract_email_from_dn(const gchar* dn) {
+ gchar *tmp1, *tmp2;
+
+ if (! dn)
+ return NULL;
+
+ tmp1 = strchr(dn, '=');
+ tmp1 += 1;
+ tmp2 = strchr(tmp1, ',');
+
+ return g_strndup(tmp1, tmp2 - tmp1);
+}
+
+static int ldap_set_dn(Server* server, GSList* list, gchar** dn) {
+ gchar* new_dn = NULL;
+ gchar *rdn = NULL;
+ Email* email;
+ int rc = 0;
+ GSList* cur;
+ gboolean found = FALSE;
+
+ if (! dn || ! *dn)
+ return rc;
+
+ rdn = extract_email_from_dn(*dn);
+ cur = list;
+ while (cur && ! found) {
+ ContactChange* c = (ContactChange *) cur->data;
+ if (c->type == CONTACT_CHANGE_EMAIL) {
+ email = (Email *) c->value;
+ if (utf8_collate(email->email, rdn) == 0)
+ found = TRUE;
+ else {
+ if (! new_dn)
+ new_dn = g_strconcat("mail=", email->email, NULL);
+ }
+ }
+ cur = cur->next;
+ }
+
+ if (! found) {
+ if (! new_dn) {
+ gchar* tmp = create_dummy_dn(NULL);
+ new_dn = g_strconcat("mail=", tmp, NULL);
+ g_free(tmp);
+ }
+ g_free(rdn);
+ debug_print("Old DN: %s -> New DN: %s\n", *dn, new_dn);
+ rc = ldap_rename_s(server->ldap, *dn, new_dn, NULL, 1, NULL, NULL);
+ if (! rc) {
+ g_free(*dn);
+ *dn = g_strconcat(new_dn, ",", server->dn, NULL);
+ }
+ }
+
+ g_free(new_dn);
+
+ return rc;
+}
+
/**
* Save a contact in AddressBook.
* @param abook Pointer to AddressBook
@@ -1235,12 +1499,118 @@
*/
gboolean plugin_set_contact(
AddressBook* abook, const Contact* contact, gchar** error) {
+ gchar* dn = NULL;
+ AbookConnection* ac;
+ Server* server;
+ guint row = 0;
+ GSList *cur, *attributes;
+ guint size = g_strv_length(standard_attribs) + g_strv_length(other_attributes);
+ LDAPMod *mods[size + 2]; /* including NULL and objects */
+ LDAPMod modarr[size + 2]; /* including NULL and objects */
+ gchar* attr[size + 1][2];
+ gchar** obj;
+ int rc;
+ gchar** emails = NULL;
/* Save contact */
+ if (! abook || ! contact) {
+ if (error)
+ *error = g_strdup(_("Missing address book or contact"));
+ return TRUE;
+ }
- abook->dirty = TRUE;
+ if (! has_required_info(contact)) {
+ if (error)
+ *error = g_strdup(_("Missing mandatory attributes"));
+ return TRUE;
+ }
+
+ ac = get_abook_connection(abook);
+ if (! ac) {
+ if (error)
+ *error = g_strdup(_("Missing address book connection"));
+ return TRUE;
+ }
+ server = ac->server;
+ ac->contact = contact;
+
+ dn = contact_get_dn(ac);
+ if (! dn) {
+ debug_print("LDAP Add: Unresolved error");
+ if (*error)
+ *error = g_strdup(_("LDAP Add: Unresolved error"));
+ abook->dirty = FALSE;
+ return TRUE;
+ }
- return FALSE;
+ /* Initialize structure */
+ init_mods(attr, size + 2);
+ obj = get_object_classes();
+
+ if (! contact) {
+ Email* a = g_new0(Email, 1);
+ a->email = extract_email_from_dn(dn);
+ contact->emails = g_slist_append(contact->emails, a);
+ }
+
+ cur = contact->emails;
+ /* First email is used as dn and will therefore be
+ * added automatically
+ */
+ if (cur)
+ cur = cur->next;
+ rc = 0;
+ while(cur) {
+ Email* e = (Email *) cur->data;
+ if (e->email) {
+ emails = g_renew(gchar *, emails, rc + 1);
+ emails[rc] = g_memdup(e->email, strlen(e->email) + 1);
+ }
+ cur = cur->next;
+ rc++;
+ }
+ if (rc) {
+ emails = g_renew(gchar *, emails, rc + 1);
+ emails[rc] = NULL;
+ SETMODS(mods[row], modarr[row], LDAP_MOD_ADD, g_strdup("mail"), emails);
+ row++;
+ }
+
+ debug_print("Adding: %s\n", dn);
+ SETMODS(mods[row], modarr[row], LDAP_MOD_ADD, g_strdup("objectClass"), obj);
+ row++;
+
+ attributes = self->attrib_list();
+ for (cur = attributes; cur; cur = g_slist_next(cur)) {
+ AttribDef* def = (AttribDef *) cur->data;
+ AttribDef* res = (AttribDef *) g_hash_table_lookup(contact->data, def->attrib_name);
+ if (res) {
+ const gchar* value = res->value.string;
+ SETMOD(mods[row], modarr[row], LDAP_MOD_ADD, \
+ g_strdup(native2ldap(def->attrib_name)), attr[row], g_strdup(value));
+ row++;
+ }
+ }
+ gslist_free(&attributes, attrib_def_free);
+ mods[row] = NULL;
+ if (debug_get_mode())
+ print_ldapmod(mods);
+
+ rc = ldap_add_ext_s(server->ldap, dn, mods, NULL, NULL);
+ g_free(dn);
+ mods_free(mods);
+ if (rc) {
+ debug_print("LDAP ADD: %s\n", ldap_err2string(rc));
+ if (error)
+ *error = g_strdup(ldap_err2string(rc));
+ abook->dirty = FALSE;
+ }
+ else {
+ abook->dirty = TRUE;
+ return FALSE;
+ }
+
+ return TRUE;
}
/**
@@ -1252,8 +1622,46 @@
*/
gboolean plugin_delete_contact(
AddressBook* abook, const Contact* contact, gchar** error) {
-
+ gchar* dn;
+ AbookConnection* ac;
+ Server* server;
+ int rc;
+
/* Delete contact */
+ if (! abook || ! contact) {
+ if (error)
+ *error = g_strdup(_("Missing address book or contact"));
+ return TRUE;
+ }
+
+ ac = get_abook_connection(abook);
+ if (! ac) {
+ if (error)
+ *error = g_strdup(_("Missing address book connection"));
+ return TRUE;
+ }
+ server = ac->server;
+ ac->contact = contact;
+
+ dn = contact_get_dn(ac);
+ if (dn) {
+ rc = ldap_delete_ext_s(server->ldap, dn, NULL, NULL);
+ g_free(dn);
+ if (rc) {
+ debug_print("LDAP Delete: %s\n", ldap_err2string(rc));
+ if (*error)
+ *error = g_strdup(ldap_err2string(rc));
+ abook->dirty = FALSE;
+ return TRUE;
+ }
+ }
+ else {
+ debug_print("LDAP Delete: No such contact\n");
+ if (*error)
+ *error = g_strdup(_("No such contact"));
+ abook->dirty = FALSE;
+ return TRUE;
+ }
abook->dirty = TRUE;
@@ -1269,8 +1677,175 @@
*/
gboolean plugin_update_contact(
AddressBook* abook, const Contact* contact, gchar** error) {
+ gchar* dn = NULL;
+ gchar* old_dn = NULL;
+ AbookConnection* ac;
+ Contact* old;
+ GSList *diff, *cur;
+ AttribDef* attrib;
+ Email* email;
+ Server* server;
+ guint row = 0;
+ guint num = 0;
+ guint size = g_strv_length(standard_attribs) + g_strv_length(other_attributes);
+ LDAPMod *mods[size + 2]; // including NULL and objects
+ LDAPMod modarr[size + 2]; // including NULL and objects
+ gchar* attr[size + 1][2];
+ int rc;
+ gchar** emails = NULL;
/* Update contact */
+ if (! abook || ! contact) {
+ if (error)
+ *error = g_strdup(_("Missing address book or contact"));
+ return TRUE;
+ }
+
+ if (! has_required_info(contact)) {
+ if (error)
+ *error = g_strdup(_("Missing mandatory attributes"));
+ return TRUE;
+ }
+
+ if (debug_get_mode())
+ contact_dump(contact, stderr);
+
+ ac = get_abook_connection(abook);
+ if (! ac) {
+ if (error)
+ *error = g_strdup(_("Missing address book connection"));
+ return TRUE;
+ }
+ server = ac->server;
+
+ ac->contact = contact;
+ dn = contact_get_dn(ac);
+ if (dn) {
+ /* Initialize structure */
+ init_mods(attr, size + 2);
+
+ debug_print("Updating: %s\n", dn);
+
+ old = address_book_contact_get(abook, dn);
+ if (old) {
+ diff = contact_diff(old, contact);
+ cur = diff;
+ old_dn = g_strdup(dn);
+ rc = ldap_set_dn(server, cur, &dn);
+ if (utf8_collate(dn, old_dn) != 0) {
+ AttribDef* attr = pack_data(ATTRIB_TYPE_STRING, "dn", (gpointer) dn);
+ g_hash_table_replace(contact->data, g_strdup("dn"), attr);
+ }
+ g_free(old_dn);
+
+ while (cur && !rc) {
+ ContactChange* c = (ContactChange *) cur->data;
+ switch (c->type) {
+ case CONTACT_CHANGE_ATTRIB:
+ attrib = (AttribDef *) c->value;
+ /*
+ * if c->action == ATTRIBUTE_MODIFY && c->value == ""
+ * then change c->action to ATTRIBUTE_DELETE since
+ * an empty string is to be treated as a missing attribute
+ */
+ if (c->action == ATTRIBUTE_MODIFY && attrib->type == ATTRIB_TYPE_STRING
+ && attrib->value.string && strlen(attrib->value.string) < 1)
+ c->action = ATTRIBUTE_DELETE;
+ break;
+ case CONTACT_CHANGE_EMAIL:
+ email = (Email *) c->value;
+ break;
+ }
+
+ switch (c->action) {
+ case ATTRIBUTE_ADD:
+ if (c->type == CONTACT_CHANGE_EMAIL) {
+ /* Ignore - emails always modify */
+ }
+ else {
+ SETMOD(mods[row], modarr[row], LDAP_MOD_ADD, \
+ g_strdup(native2ldap(attrib->attrib_name)), \
+ attr[row], g_strdup(attrib->value.string));
+ row++;
+ }
+ break;
+ case ATTRIBUTE_DELETE:
+ if (c->type == CONTACT_CHANGE_EMAIL) {
+ /* email equal dn (make dummy) */
+ SETMOD(mods[row], modarr[row], LDAP_MOD_REPLACE, \
+ g_strdup("mail"), attr[row], extract_email_from_dn(dn));
+ row++;
+ }
+ else {
+ SETMOD(mods[row], modarr[row], LDAP_MOD_DELETE, \
+ g_strdup(native2ldap(attrib->attrib_name)), \
+ attr[row], NULL);
+ row++;
+ }
+ /* check if dn is to be deleted */
+ break;
+ case ATTRIBUTE_MODIFY:
+ if (c->type == CONTACT_CHANGE_EMAIL) {
+ /* check if dn is to be modified */
+ emails = g_renew(gchar *, emails, num + 1);
+ emails[num] = g_memdup(email->email, strlen(email->email) + 1);
+ num++;
+ }
+ else {
+ SETMOD(mods[row], modarr[row], LDAP_MOD_REPLACE, \
+ g_strdup(native2ldap(attrib->attrib_name)), \
+ attr[row], g_strdup(attrib->value.string));
+ row++;
+ }
+ break;
+ }
+ cur = cur->next;
+ }
+ if (! rc) {
+ if (emails) {
+ emails = g_renew(gchar *, emails, num + 1);
+ emails[num] = NULL;
+ SETMODS(mods[row], modarr[row], LDAP_MOD_REPLACE, \
+ g_strdup("mail"), emails);
+ row++;
+ }
+ mods[row] = NULL;
+ if (debug_get_mode())
+ print_ldapmod(mods);
+
+ rc = ldap_modify_ext_s(server->ldap, dn, mods, NULL, NULL);
+ mods_free(mods);
+ }
+ g_free(dn);
+ if (rc) {
+ debug_print("LDAP MODIFY: %s\n", ldap_err2string(rc));
+ if (error)
+ *error = g_strdup(ldap_err2string(rc));
+ abook->dirty = FALSE;
+ return TRUE;
+ }
+ else {
+ abook->dirty = TRUE;
+ return FALSE;
+ }
+ gslist_free(&diff, contact_change_free);
+ }
+ else {
+ g_free(dn);
+ dn = NULL;
+ if (self->set_contact(abook, contact, error)) {
+ abook->dirty = FALSE;
+ return TRUE;
+ }
+ }
+ }
+ else {
+ debug_print("LDAP Update: Unresolved error");
+ if (*error)
+ *error = g_strdup(_("LDAP Update: Unresolved error"));
+ abook->dirty = FALSE;
+ return TRUE;
+ }
abook->dirty = TRUE;
@@ -1662,8 +2237,8 @@
return NULL;
}
- /*feature->support = PLUGIN_READ_WRITE | PLUGIN_ADVANCED_SEARCH;*/
- feature->support = PLUGIN_READ_ONLY | PLUGIN_ADVANCED_SEARCH;
+ feature->support = PLUGIN_READ_WRITE | PLUGIN_ADVANCED_SEARCH;
+ //feature->support = PLUGIN_READ_ONLY | PLUGIN_ADVANCED_SEARCH;
feature->subtype = subtype;
return feature;
@@ -1690,7 +2265,8 @@
"If port is not specified 389 is assumed for ldap\n"
"and 636 is assumed for ldaps.\n"
"If ldap is used for schema then TLS will be tried\n"
- "automatically before using plain text connection.");
+ "automatically before using plain text connection.\n"
+ "Display name and Lastname are mandatory attributes.");
}
/**
More information about the Commits
mailing list