#include "ldap_plugin.h" /*Debug helper function*/ void ldap_debug(ldap_connection *conn, int level, char *message, ...) { va_list arglist; char buffer[4096]; char *type = (conn->type==CONNECTION_TYPE_LOCAL?"local":"remote"); int debug_level = conn->debug_level; if (level > debug_level) { return; } va_start(arglist, message); vsprintf(buffer, message, arglist); switch (level) { case 0: //Error printf("[%s] ERROR: %s\n", type, buffer); if (conn->handle) { async_add_pairlist_log(conn->handle, buffer, SYNC_LOG_ERROR); } break; case 1: //Warning printf("[%s] WARNING: %s\n", type, buffer); break; case 2: //Information printf("[%s] INFORMATION: %s\n", type, buffer); break; case 3: //debug printf("[%s] DEBUG: %s\n", type, buffer); break; case 4: //debug printf("[%s] FULL DEBUG: %s\n", type, buffer); break; } va_end(arglist); } /*********************************************************************** * * Function: sync_connect * * Summary: gets called by multisync when sync starts * ***********************************************************************/ ldap_connection *sync_connect(sync_pair *handle, connection_type type, sync_object_type object_types) { ldap_connection *conn; conn = g_malloc0(sizeof(ldap_connection)); g_assert(conn); conn->handle = handle; conn->commondata.object_types = object_types; conn->type = type; conn->evolution_support = 0; ldap_debug(conn, 3, "start: sync_connect"); sprintf(conn->statefile, "%s/%sldap", sync_get_datapath(handle), (type==CONNECTION_TYPE_LOCAL?"local":"remote")); sprintf(conn->dbfile, "%s/%sstate", sync_get_datapath(handle), (type==CONNECTION_TYPE_LOCAL?"local":"remote")); ldap_debug(conn, 3, "Statefile: %s", conn->statefile); if (load_ldap_state(conn)) { sync_set_requestfailed(handle); return NULL; } //connect if (ldap_start(conn)) { sync_set_requestfailed(handle); return NULL; } //Set ldap v3 ldap_set_version(conn); //set encryption if (conn->encryption) { if (ldap_encrypt(conn)) { if (conn->encryption == 2) { //encryption required ldap_debug(conn, 0, "Unable to start required encryption"); sync_set_requestfailed(handle); return NULL; } } } //Check Authentication if (ldap_makebind(conn)) { sync_set_requestfailed(handle); return NULL; } //Check evolution support if (!ldap_check_evolution(conn)) { conn->evolution_support = 1; } srand(time(NULL)); ldap_debug(conn, 3, "end: sync_connect"); sync_set_requestdone(handle); return(conn); } /*convert data_entry into changed_info*/ changed_object *add_changed(ldap_connection *conn, struct data_entry *data, int change_type) { GString *vcard = NULL; changed_object *change = NULL; change = g_malloc0(sizeof(changed_object)); change->uid = data->uid; change->change_type = change_type; change->object_type = SYNC_OBJECT_TYPE_PHONEBOOK; change->comp = NULL; change->removepriority = NULL; if (change_type != SYNC_OBJ_HARDDELETED && change_type != SYNC_OBJ_SOFTDELETED) { //Convert attributes to VCARD vcard = ldap2vcard(conn, data->ldapdata); ldap_debug(conn, 3, "From ldap generated VCARD:\n%s", vcard->str); change->comp = vcard->str; } free(data); return change; } /*********************************************************************** * * Function: get_changes * * Summary: Get the list of changes from the ldap server by comparing it against * a local xml database * ***********************************************************************/ void get_changes(ldap_connection *conn, sync_object_type newdbs) { GList *changes = NULL; change_info *chinfo = NULL; GList *xmllist = NULL, *ldaplist = NULL; struct data_entry *xmlentry, *ldapentry; GList *xmlfirst, *ldapfirst; int i = 0, n = 0; //Get all entries from ldap server and xml file ldaplist = load_ldap_entries(conn); xmllist = load_xml_entries(conn); ldap_debug(conn, 3, "Got %i from ldap, %i from xml", g_list_length(ldaplist), g_list_length(xmllist)); ldap_debug(conn, 2, "Searching for changes"); // Now create the list of changes for (i = 0; g_list_nth(ldaplist, i);) { ldapentry = ((struct data_entry *)(g_list_nth_data(ldaplist, i))); ldap_debug(conn, 4, "New ldapentry: on list %i, %i", g_list_length(ldaplist), i); n = 0; for (n = 0; g_list_nth(xmllist, n);) { ldap_debug(conn, 4, "New xmlentry: on list %i, %i", g_list_length(g_list_first(xmllist)), n); xmlentry = ((struct data_entry *)(g_list_nth_data(xmllist, n))); ldap_debug(conn, 3, "Comparing %s with %s", ldapentry->uid, xmlentry->uid); if (strcmp(xmlentry->uid, ldapentry->uid) == 0) { ldap_debug(conn, 3, "Entries equal: Tsldap %s, Tsxml %s", ldapentry->modifyTimestamp, xmlentry->modifyTimestamp); if (strcmp(xmlentry->modifyTimestamp, ldapentry->modifyTimestamp) != 0) { //Entry has been modified, Update on ldaplist ldap_debug(conn, 2, "Modified entry found: %s", ldapentry->uid); get_ldap_data(conn, ldapentry); if (newdbs) { g_list_nth(ldaplist, i)->data = add_changed(conn, ldapentry, SYNC_OBJ_ADDED); } else { g_list_nth(ldaplist, i)->data = add_changed(conn, ldapentry, SYNC_OBJ_MODIFIED); } //remove from xmllist xmllist = g_list_remove(xmllist, xmlentry); //Move ldaplist on i++; } else { //Entry has not been modified ldap_debug(conn, 2, "Unmodified entry found: %s", ldapentry->uid); //Remove it from ldaplist and xmllist if (newdbs) { get_ldap_data(conn, ldapentry); g_list_nth(ldaplist, i)->data = add_changed(conn, ldapentry, SYNC_OBJ_ADDED); //Move ldaplist on i++; } else { ldaplist = g_list_remove(ldaplist, ldapentry); } xmllist = g_list_remove(xmllist, xmlentry); } goto nextldap; } n++; } //Could not find it on the xmllist, so must be new //Update it on the ldaplist ldap_debug(conn, 2, "New entry found: %s", ldapentry->uid); get_ldap_data(conn, ldapentry); g_list_nth(ldaplist, i)->data = add_changed(conn, ldapentry, SYNC_OBJ_ADDED); //Move ldaplist on i++; nextldap: ; } done: ldap_debug(conn, 3, "Got %i on ldap, %i on xml", g_list_length(ldaplist), g_list_length(xmllist)); ldap_debug(conn, 2, "Looking for deleted items"); // Now get the deleted entries n = 0; for (n = 0; g_list_nth(xmllist, n); n++) { if (!newdbs) { ldap_debug(conn, 2, "Deleted entry found: %s", ((struct data_entry*)(g_list_nth_data(xmllist, n)))->uid); g_list_nth(xmllist, n)->data = add_changed(conn, ((struct data_entry*)(g_list_nth_data(xmllist, n))), SYNC_OBJ_HARDDELETED); } } ldap_debug(conn, 2, "Done searching for changes"); //Now merge ldaplist and xmllist if (xmllist && !newdbs) { xmllist = g_list_concat(ldaplist, xmllist); } else { xmllist = ldaplist; } chinfo = g_malloc0(sizeof(change_info)); chinfo->newdbs = 0; chinfo->changes = xmllist; sync_set_requestdata(chinfo, conn->handle); ldap_debug(conn, 2, "Found %i changes", g_list_length(xmllist)); } /*********************************************************************** * * Function: syncobj_modify * * Summary: Modify or add a ldap entry * ***********************************************************************/ void syncobj_modify(ldap_connection *conn, char *comp, char *uid, sync_object_type objtype, char *uidret, int *uidretlen) { LDAPMod **ldapdata = NULL; if (!conn->write) { //Write support disabled sync_set_requestdone(conn->handle); return; } ldap_debug(conn, 2, "start: syncobj_modify"); ldap_debug(conn, 3, "COMP: %s\n", comp); //Convert the vcard to our LDAP data ldapdata = vcard2ldap(conn, comp); if (uid) { //Modify a entry ldap_debug(conn, 2, "Modifying: %s", uid); //Delete it uid = quoted_decode(uid); if (ldap_delete_entry(conn, uid) != 0) { ldap_debug(conn, 1, "Could not delete entry! Possible duplicates"); } //free(uid); //We need to send a requestfailed, so that multisync allows us to change the uid of the object. Plain stupid. sync_set_requestfailed(conn->handle); } else { //Add a new entry if (ldap_add_entry(conn, ldapdata, uidret, 0) != 0) { sync_set_requestfailed(conn->handle); } else { sync_set_requestdone(conn->handle); } } //free(ldapdata); *uidretlen = strlen(uidret); } /*Delete a entry given by the encoded cn*/ void syncobj_delete(ldap_connection *conn, char *uid, sync_object_type objtype, int softdelete) { if (!conn->write) { //Write support disabled sync_set_requestdone(conn->handle); return; } //Decode the uid uid = quoted_decode(uid); if (ldap_delete_entry(conn, uid) != 0) { sync_set_requestfailed(conn->handle); } else { sync_set_requestdone(conn->handle); } free(uid); } void sync_done(ldap_connection *conn, gboolean success) { if (success) { save_xml_entries(conn); } sync_set_requestdone(conn->handle); ldap_debug(conn, 2, "Done syncing"); } void sync_disconnect(ldap_connection *conn) { //Check if we have an Ldap connection if(conn->ld) { if(ldap_unbind(conn->ld) != LDAP_SUCCESS) { sync_log(conn->handle, "Couldn't unbind LDAP server", SYNC_LOG_ERROR); } else { sync_log(conn->handle, "Disconnected from LDAP server", SYNC_LOG_SUCCESS); } } //ldap_memfree(conn->ld); conn->ld = NULL; sync_set_requestdone(conn->handle); } char* short_name() { return("ldap-sync"); } char *long_name() { return("LDAP"); } sync_object_type object_types() { return(SYNC_OBJECT_TYPE_PHONEBOOK); } void plugin_init(void) { } char *plugin_info() { return("Allows synchronization of a addressbook against a specific DN in a LDAP server."); } gboolean always_connected() { return(FALSE); } int plugin_API_version(void) { return(3); }