]> code.citadel.org Git - citadel.git/blobdiff - citadel/modules/ldap/serv_ldap.c
* serv_ldap.c: upon successful connect to an LDAP server, post an aide message warnin...
[citadel.git] / citadel / modules / ldap / serv_ldap.c
index 06cf124e1d284e4aeaf89623dc8a74001e8189b1..a7551dcbfecd1efcdb4c785ffc5980e13c6b1a6f 100644 (file)
@@ -29,6 +29,7 @@
 #include <sys/wait.h>
 #include <string.h>
 #include <limits.h>
+#include <libcitadel.h>
 #include "citadel.h"
 #include "server.h"
 #include "citserver.h"
 #include "room_ops.h"
 #include "policy.h"
 #include "database.h"
-#include "msgbase.h"
 #include "serv_ldap.h"
-#include "vcard.h"
-#include "tools.h"
 
 
 #include "ctdl_module.h"
 #include <ldap.h>
 
 LDAP *dirserver = NULL;
+int ldap_time_disconnect = 0;
+
+
+
+/* There is a forward referance so.... */
+int delete_from_ldap(char *cn, char *ou, void **object);
+
 
 /*
  * LDAP connector cleanup function
  */
 void serv_ldap_cleanup(void)
 {
-       if (!dirserver) return;
-
-       lprintf(CTDL_INFO, "Unbinding from directory server\n");
-       ldap_unbind(dirserver);
+       if (dirserver) {
+               CtdlLogPrintf(CTDL_INFO,
+                       "LDAP: Unbinding from directory server\n");
+               ldap_unbind(dirserver);
+       }
        dirserver = NULL;
+       ldap_time_disconnect = 0;
+}
+
+
+/*
+ * connect_to_ldap
+ *
+ * BIG FAT WARNING
+ * Make sure this function is only called from within a begin_critical_section(S_LDAP)
+ * If you don't things will break!!!!!.
+ */
+
+
+int connect_to_ldap(void)
+{
+       int i;
+       int ldap_version = 3;
+
+       if (dirserver) {        // Already connected
+               ldap_time_disconnect = 1 ;      // reset the timer.
+               return 0;
+       }
+
+       CtdlLogPrintf(CTDL_INFO, "LDAP: Connecting to LDAP server %s:%d...\n",
+               config.c_ldap_host, config.c_ldap_port);
+
+       dirserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
+       if (dirserver == NULL) {
+               CtdlLogPrintf(CTDL_CRIT,
+                       "LDAP: Could not connect to %s:%d : %s\n",
+                       config.c_ldap_host, config.c_ldap_port,
+                       strerror(errno));
+               CtdlAideMessage(strerror(errno),
+                            "LDAP: Could not connect to server.");
+               return -1;
+       }
+       else {
+               CtdlAideMessage(
+                       "WARNING: populating an external LDAP address book is deprecated.\n"
+                       "This function will be discontinued in a future release.\n"
+                       "Please migrate to vCard-based address books as soon as possible.\n"
+                       "Visit the Citadel support forum if you need further assistance.\n"
+                       ,
+                       "Warning: LDAP address book is deprecated"
+               );
+       }
+
+       ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION,
+                       &ldap_version);
+
+       CtdlLogPrintf(CTDL_INFO, "LDAP: Binding to %s\n", config.c_ldap_bind_dn);
+
+       i = ldap_simple_bind_s(dirserver,
+                              config.c_ldap_bind_dn,
+                              config.c_ldap_bind_pw);
+       if (i != LDAP_SUCCESS) {
+               CtdlLogPrintf(CTDL_CRIT, "LDAP: Cannot bind: %s (%d)\n",
+                       ldap_err2string(i), i);
+               dirserver = NULL;       /* FIXME disconnect from ldap */
+               CtdlAideMessage(ldap_err2string(i),
+                            "LDAP: Cannot bind to server");
+               return -1;
+       }
+       ldap_time_disconnect = 1;
+       return 0;
 }
 
 
@@ -72,7 +143,8 @@ void serv_ldap_cleanup(void)
 /*
  * Create the root node.  If it's already there, so what?
  */
-void CtdlCreateLdapRoot(void) {
+void create_ldap_root(void)
+{
        char *dc_values[2];
        char *objectClass_values[3];
        LDAPMod dc, objectClass;
@@ -82,43 +154,49 @@ void CtdlCreateLdapRoot(void) {
 
        /* We just want the top-level dc, not the whole hierarchy */
        strcpy(topdc, config.c_ldap_base_dn);
-       for (i=0; !IsEmptyStr(&topdc[i]); ++i) {
+       for (i = 0; topdc[i]; ++i) {
                if (topdc[i] == ',') {
                        topdc[i] = 0;
                        break;
                }
        }
-       for (i=0; !IsEmptyStr(&topdc[i]); ++i) {
-               if (topdc[i] == '=') strcpy(topdc, &topdc[i+1]);
+       for (i = 0; topdc[i]; ++i) {
+               if (topdc[i] == '=')
+                       strcpy(topdc, &topdc[i + 1]);
        }
 
        /* Set up the transaction */
-       dc.mod_op               = LDAP_MOD_ADD;
-       dc.mod_type             = "dc";
-       dc_values[0]            = topdc;
-       dc_values[1]            = NULL;
-       dc.mod_values           = dc_values;
-       objectClass.mod_op      = LDAP_MOD_ADD;
-       objectClass.mod_type    = "objectClass";
-       objectClass_values[0]   = "top";
-       objectClass_values[1]   = "domain";
-       objectClass_values[2]   = NULL;
-       objectClass.mod_values  = objectClass_values;
+       dc.mod_op = LDAP_MOD_ADD;
+       dc.mod_type = "dc";
+       dc_values[0] = topdc;
+       dc_values[1] = NULL;
+       dc.mod_values = dc_values;
+       objectClass.mod_op = LDAP_MOD_ADD;
+       objectClass.mod_type = "objectClass";
+       objectClass_values[0] = "top";
+       objectClass_values[1] = "domain";
+       objectClass_values[2] = NULL;
+       objectClass.mod_values = objectClass_values;
        mods[0] = &dc;
        mods[1] = &objectClass;
        mods[2] = NULL;
 
        /* Perform the transaction */
-       lprintf(CTDL_DEBUG, "Setting up Base DN node...\n");
+       CtdlLogPrintf(CTDL_DEBUG, "LDAP: Setting up Base DN node...\n");
+
        begin_critical_section(S_LDAP);
+       if (connect_to_ldap()) {
+               end_critical_section(S_LDAP);
+               return;
+       }
        i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
        end_critical_section(S_LDAP);
 
        if (i == LDAP_ALREADY_EXISTS) {
-               lprintf(CTDL_INFO, "Base DN is already present in the directory; no need to add it again.\n");
-       }
-       else if (i != LDAP_SUCCESS) {
-               lprintf(CTDL_CRIT, "ldap_add_s() failed: %s (%d)\n",
+               CtdlLogPrintf(CTDL_INFO,
+                       "LDAP: Base DN is already present in the directory; no need to add it again.\n");
+       else if (i != LDAP_SUCCESS) {
+               CtdlLogPrintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
                        ldap_err2string(i), i);
        }
 }
@@ -126,8 +204,11 @@ void CtdlCreateLdapRoot(void) {
 
 /*
  * Create an OU node representing a Citadel host.
+ * parameter cn is not used, its just there to keep the hook interface consistant
+ * parameter object not used here, present for interface compatability
  */
-void CtdlCreateHostOU(char *host) {
+int create_ldap_host_OU(char *cn, char *host, void **object)
+{
        char *dc_values[2];
        char *objectClass_values[3];
        LDAPMod dc, objectClass;
@@ -139,34 +220,41 @@ void CtdlCreateHostOU(char *host) {
        snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
 
        /* Set up the transaction */
-       dc.mod_op               = LDAP_MOD_ADD;
-       dc.mod_type             = "ou";
-       dc_values[0]            = host;
-       dc_values[1]            = NULL;
-       dc.mod_values           = dc_values;
-       objectClass.mod_op      = LDAP_MOD_ADD;
-       objectClass.mod_type    = "objectClass";
-       objectClass_values[0]   = "top";
-       objectClass_values[1]   = "organizationalUnit";
-       objectClass_values[2]   = NULL;
-       objectClass.mod_values  = objectClass_values;
+       dc.mod_op = LDAP_MOD_ADD;
+       dc.mod_type = "ou";
+       dc_values[0] = host;
+       dc_values[1] = NULL;
+       dc.mod_values = dc_values;
+       objectClass.mod_op = LDAP_MOD_ADD;
+       objectClass.mod_type = "objectClass";
+       objectClass_values[0] = "top";
+       objectClass_values[1] = "organizationalUnit";
+       objectClass_values[2] = NULL;
+       objectClass.mod_values = objectClass_values;
        mods[0] = &dc;
        mods[1] = &objectClass;
        mods[2] = NULL;
 
        /* Perform the transaction */
-       lprintf(CTDL_DEBUG, "Setting up Host OU node...\n");
+       CtdlLogPrintf(CTDL_DEBUG, "LDAP: Setting up Host OU node...\n");
+
        begin_critical_section(S_LDAP);
+       if (connect_to_ldap()) {
+               end_critical_section(S_LDAP);
+               return -1;
+       }
        i = ldap_add_s(dirserver, dn, mods);
        end_critical_section(S_LDAP);
 
        if (i == LDAP_ALREADY_EXISTS) {
-               lprintf(CTDL_INFO, "Host OU is already present in the directory; no need to add it again.\n");
-       }
-       else if (i != LDAP_SUCCESS) {
-               lprintf(CTDL_CRIT, "ldap_add_s() failed: %s (%d)\n",
+               CtdlLogPrintf(CTDL_INFO,
+                       "LDAP: Host OU is already present in the directory; no need to add it again.\n");
+       else if (i != LDAP_SUCCESS) {
+               CtdlLogPrintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
                        ldap_err2string(i), i);
+               return -1;
        }
+       return 0;
 }
 
 
@@ -174,408 +262,206 @@ void CtdlCreateHostOU(char *host) {
 
 
 
+/*
+ * Create a base LDAP object for the interface
+ */
 
+int create_ldap_object(char *cn, char *ou, void **object)
+{
+       // We do nothing here, this just gets the base structure created by the interface.
+       CtdlLogPrintf(CTDL_DEBUG, "LDAP: Created ldap object\n");
+       return 0;
+}
 
-void CtdlConnectToLdap(void) {
-       int i;
-       int ldap_version = 3;
 
-       lprintf(CTDL_INFO, "Connecting to LDAP server %s:%d...\n",
-               config.c_ldap_host, config.c_ldap_port);
+/*
+ * Add an attribute to the ldap object
+ */
 
-       dirserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
-       if (dirserver == NULL) {
-               lprintf(CTDL_CRIT, "Could not connect to %s:%d : %s\n",
-                       config.c_ldap_host,
-                       config.c_ldap_port,
-                       strerror(errno));
-               return;
-       }
+int add_ldap_object(char *cn, char *ou, void **object)
+{
+       LDAPMod **attrs;
+       int num_attrs = 0;
+       int num_values = 0;
+       int cur_attr;
 
-       ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
 
-       lprintf(CTDL_INFO, "Binding to %s\n", config.c_ldap_bind_dn);
+       CtdlLogPrintf(CTDL_DEBUG,
+               "LDAP: Adding ldap attribute name:\"%s\" value:\"%s\"\n",
+               cn, ou);
 
-       i = ldap_simple_bind_s(dirserver,
-                               config.c_ldap_bind_dn,
-                               config.c_ldap_bind_pw
-       );
-       if (i != LDAP_SUCCESS) {
-               lprintf(CTDL_CRIT, "Cannot bind: %s (%d)\n", ldap_err2string(i), i);
-               dirserver = NULL;       /* FIXME disconnect from ldap */
-               return;
+       attrs = *object;
+       if (attrs) {
+               while (attrs[num_attrs])
+                       num_attrs++;
        }
 
-       CtdlCreateLdapRoot();
+       for (cur_attr = 0; cur_attr < num_attrs; cur_attr++) {
+               if (!strcmp(attrs[cur_attr]->mod_type, cn)) {   // Adding a value to the attribute
+                       if (attrs[cur_attr]->mod_values) {
+                               while (attrs[cur_attr]->
+                                      mod_values[num_values]) {
+                                       if (!strcmp
+                                           (ou,
+                                            attrs[cur_attr]->
+                                            mod_values[num_values])) {
+                                               CtdlLogPrintf(CTDL_DEBUG,
+                                                       "LDAP: Ignoring duplicate attribute/value pair\n");
+                                               return 0;
+                                       }
+                                       num_values++;
+                               }
+                       }
+                       attrs[cur_attr]->mod_values =
+                           realloc(attrs[cur_attr]->mod_values,
+                                   (num_values + 2) * (sizeof(char *)));
+                       attrs[cur_attr]->mod_values[num_values] =
+                           strdup(ou);
+                       attrs[cur_attr]->mod_values[num_values + 1] = NULL;
+                       return 0;
+               }
+       }
+       if (num_attrs)
+               attrs =
+                   realloc(attrs, (sizeof(LDAPMod *)) * (num_attrs + 2));
+       else
+               attrs = malloc((sizeof(LDAPMod *)) * (num_attrs + 2));
+       attrs[num_attrs] = malloc(sizeof(LDAPMod));
+       memset(attrs[num_attrs], 0, sizeof(LDAPMod));
+       attrs[num_attrs + 1] = NULL;
+       attrs[num_attrs]->mod_op = LDAP_MOD_ADD;
+       attrs[num_attrs]->mod_type = strdup(cn);
+       attrs[num_attrs]->mod_values = malloc(2 * sizeof(char *));
+       attrs[num_attrs]->mod_values[0] = strdup(ou);
+       attrs[num_attrs]->mod_values[1] = NULL;
+       *object = attrs;
+       return 0;
 }
 
 
-/* 
- * vCard-to-LDAP conversions.
- *
- * If 'op' is set to V2L_WRITE, then write
- * (add, or change if already exists) a directory entry to the
- * LDAP server, based on the information supplied in a vCard.
- *
- * If 'op' is set to V2L_DELETE, then delete the entry from LDAP.
+/*
+ * SAve the object to the LDAP server
  */
-void ctdl_vcard_to_ldap(struct CtdlMessage *msg, int op) {
-       struct vCard *v = NULL;
-       int i, j;
+int save_ldap_object(char *cn, char *ou, void **object)
+{
+       int i;
+
        char this_dn[SIZ];
-       LDAPMod **attrs = NULL;
+       LDAPMod **attrs;
        int num_attrs = 0;
-       int num_emails = 0;
-       int alias_attr = (-1);
-       int num_phones = 0;
-       int phone_attr = (-1);
-       int have_addr = 0;
-       int have_cn = 0;
-
-       char givenname[128];
-       char sn[128];
-       char uid[256];
-       char street[256];
-       char city[128];
-       char state[3];
-       char zipcode[10];
-       char calFBURL[256];
-
-       if (dirserver == NULL) return;
-       if (msg == NULL) return;
-       if (msg->cm_fields['M'] == NULL) return;
-       if (msg->cm_fields['A'] == NULL) return;
-       if (msg->cm_fields['N'] == NULL) return;
-
-       /* Initialize variables */
-       strcpy(givenname, "");
-       strcpy(sn, "");
-       strcpy(calFBURL, "");
-
-       sprintf(this_dn, "cn=%s,ou=%s,%s",
-               msg->cm_fields['A'],
-               msg->cm_fields['N'],
-               config.c_ldap_base_dn
-       );
-               
-       sprintf(uid, "%s@%s",
-               msg->cm_fields['A'],
-               msg->cm_fields['N']
-       );
-
-       /* Are we just deleting?  If so, it's simple... */
-       if (op == V2L_DELETE) {
-               lprintf(CTDL_DEBUG, "Calling ldap_delete_s()\n");
-               begin_critical_section(S_LDAP);
-               i = ldap_delete_s(dirserver, this_dn);
-               end_critical_section(S_LDAP);
-               if (i != LDAP_SUCCESS) {
-                       lprintf(CTDL_ERR, "ldap_delete_s() failed: %s (%d)\n",
-                               ldap_err2string(i), i);
-               }
-               return;
-       }
-
-       /*
-        * If we get to this point then it must be a V2L_WRITE operation.
-        */
-
-       /* First make sure the OU for the user's home Citadel host is created */
-       CtdlCreateHostOU(msg->cm_fields['N']);
-
-       /* The first LDAP attribute will be an 'objectclass' list.  Citadel
-        * doesn't do anything with this.  It's just there for compatibility
-        * with Kolab.
-        */
-       num_attrs = 1;
-       attrs = malloc( (sizeof(LDAPMod *) * num_attrs) );
-       attrs[0] = malloc(sizeof(LDAPMod));
-       memset(attrs[0], 0, sizeof(LDAPMod));
-       attrs[0]->mod_op        = LDAP_MOD_ADD;
-       attrs[0]->mod_type      = "objectclass";
-       attrs[0]->mod_values    = malloc(3 * sizeof(char *));
-       attrs[0]->mod_values[0] = strdup("citadelInetOrgPerson");
-       attrs[0]->mod_values[1] = NULL;
-
-       /* Convert the vCard fields to LDAP properties */
-       v = vcard_load(msg->cm_fields['M']);
-       if (v->numprops) for (i=0; i<(v->numprops); ++i) if (striplt(v->prop[i].value), strlen(v->prop[i].value) > 0) {
-
-               if (!strcasecmp(v->prop[i].name, "n")) {
-                       extract_token(sn,               v->prop[i].value, 0, ';', sizeof sn);
-                       extract_token(givenname,        v->prop[i].value, 1, ';', sizeof givenname);
-               }
-
-               if (!strcasecmp(v->prop[i].name, "fn")) {
-                       attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-                       attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-                       memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-                       attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-                       attrs[num_attrs-1]->mod_type            = "cn";
-                       attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-                       attrs[num_attrs-1]->mod_values[0]       = strdup(v->prop[i].value);
-                       attrs[num_attrs-1]->mod_values[1]       = NULL;
-                       have_cn = 1;
-               }
-
-               if (!strcasecmp(v->prop[i].name, "title")) {
-                       attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-                       attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-                       memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-                       attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-                       attrs[num_attrs-1]->mod_type            = "title";
-                       attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-                       attrs[num_attrs-1]->mod_values[0]       = strdup(v->prop[i].value);
-                       attrs[num_attrs-1]->mod_values[1]       = NULL;
-               }
+       int count = 0;
 
-               if (!strcasecmp(v->prop[i].name, "org")) {
-                       attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-                       attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-                       memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-                       attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-                       attrs[num_attrs-1]->mod_type            = "o";
-                       attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-                       attrs[num_attrs-1]->mod_values[0]       = strdup(v->prop[i].value);
-                       attrs[num_attrs-1]->mod_values[1]       = NULL;
-               }
-
-               if ( (!strcasecmp(v->prop[i].name, "adr"))
-                  ||(!strncasecmp(v->prop[i].name, "adr;", 4)) ) {
-                       /* Unfortunately, we can only do a single address */
-                       if (!have_addr) {
-                               have_addr = 1;
-                               strcpy(street, "");
-                               extract_token(&street[strlen(street)],
-                                       v->prop[i].value, 0, ';', (sizeof street - strlen(street))); /* po box */
-                               strcat(street, " ");
-                               extract_token(&street[strlen(street)],
-                                       v->prop[i].value, 1, ';', (sizeof street - strlen(street))); /* extend addr */
-                               strcat(street, " ");
-                               extract_token(&street[strlen(street)],
-                                       v->prop[i].value, 2, ';', (sizeof street - strlen(street))); /* street */
-                               striplt(street);
-                               extract_token(city, v->prop[i].value, 3, ';', sizeof city);
-                               extract_token(state, v->prop[i].value, 4, ';', sizeof state);
-                               extract_token(zipcode, v->prop[i].value, 5, ';', sizeof zipcode);
-
-                               attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-                               attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-                               memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-                               attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-                               attrs[num_attrs-1]->mod_type            = "street";
-                               attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-                               attrs[num_attrs-1]->mod_values[0]       = strdup(street);
-                               attrs[num_attrs-1]->mod_values[1]       = NULL;
-
-                               attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-                               attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-                               memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-                               attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-                               attrs[num_attrs-1]->mod_type            = "l";
-                               attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-                               attrs[num_attrs-1]->mod_values[0]       = strdup(city);
-                               attrs[num_attrs-1]->mod_values[1]       = NULL;
-
-                               attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-                               attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-                               memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-                               attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-                               attrs[num_attrs-1]->mod_type            = "st";
-                               attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-                               attrs[num_attrs-1]->mod_values[0]       = strdup(state);
-                               attrs[num_attrs-1]->mod_values[1]       = NULL;
-
-                               attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-                               attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-                               memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-                               attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-                               attrs[num_attrs-1]->mod_type            = "postalcode";
-                               attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-                               attrs[num_attrs-1]->mod_values[0]       = strdup(zipcode);
-                               attrs[num_attrs-1]->mod_values[1]       = NULL;
-                       }
-               }
-
-               if ( (!strcasecmp(v->prop[i].name, "tel"))
-                  ||(!strncasecmp(v->prop[i].name, "tel;", 4)) ) {
-                       ++num_phones;
-                       /* The first 'tel' property creates the 'telephoneNumber' attribute */
-                       if (num_phones == 1) {
-                               attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-                               phone_attr = num_attrs-1;
-                               attrs[phone_attr] = malloc(sizeof(LDAPMod));
-                               memset(attrs[phone_attr], 0, sizeof(LDAPMod));
-                               attrs[phone_attr]->mod_op               = LDAP_MOD_ADD;
-                               attrs[phone_attr]->mod_type             = "telephoneNumber";
-                               attrs[phone_attr]->mod_values           = malloc(2 * sizeof(char *));
-                               attrs[phone_attr]->mod_values[0]        = strdup(v->prop[i].value);
-                               attrs[phone_attr]->mod_values[1]        = NULL;
-                       }
-                       /* Subsequent 'tel' properties *add to* the 'telephoneNumber' attribute */
-                       else {
-                               attrs[phone_attr]->mod_values = realloc(attrs[phone_attr]->mod_values,
-                                                                    num_phones * sizeof(char *));
-                               attrs[phone_attr]->mod_values[num_phones-1]
-                                                                       = strdup(v->prop[i].value);
-                               attrs[phone_attr]->mod_values[num_phones]
-                                                                       = NULL;
-                       }
-               }
+       if (dirserver == NULL)
+               return -1;
+       if (cn == NULL)
+               return -1;
 
+       sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
 
-               if ( (!strcasecmp(v->prop[i].name, "email"))
-                  ||(!strcasecmp(v->prop[i].name, "email;internet")) ) {
-       
-                       ++num_emails;
-                       lprintf(CTDL_DEBUG, "email addr %d\n", num_emails);
-
-                       /* The first email address creates the 'mail' attribute */
-                       if (num_emails == 1) {
-                               attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-                               attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-                               memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-                               attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-                               attrs[num_attrs-1]->mod_type            = "mail";
-                               attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-                               attrs[num_attrs-1]->mod_values[0]       = strdup(v->prop[i].value);
-                               attrs[num_attrs-1]->mod_values[1]       = NULL;
-                       }
-                       /* The second email address creates the 'alias' attribute */
-                       else if (num_emails == 2) {
-                               attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-                               alias_attr = num_attrs-1;
-                               attrs[alias_attr] = malloc(sizeof(LDAPMod));
-                               memset(attrs[alias_attr], 0, sizeof(LDAPMod));
-                               attrs[alias_attr]->mod_op               = LDAP_MOD_ADD;
-                               attrs[alias_attr]->mod_type             = "alias";
-                               attrs[alias_attr]->mod_values           = malloc(2 * sizeof(char *));
-                               attrs[alias_attr]->mod_values[0]        = strdup(v->prop[i].value);
-                               attrs[alias_attr]->mod_values[1]        = NULL;
-                       }
-                       /* Subsequent email addresses *add to* the 'alias' attribute */
-                       else if (num_emails > 2) {
-                               attrs[alias_attr]->mod_values = realloc(attrs[alias_attr]->mod_values,
-                                                                    num_emails * sizeof(char *));
-                               attrs[alias_attr]->mod_values[num_emails-2]
-                                                                       = strdup(v->prop[i].value);
-                               attrs[alias_attr]->mod_values[num_emails-1]
-                                                                       = NULL;
-                       }
+       CtdlLogPrintf(CTDL_INFO, "LDAP: Calling ldap_add_s() for dn of '%s'\n",
+               this_dn);
 
-
-               }
-
-               /* Calendar free/busy URL (take the first one we find, but if a subsequent
-                * one contains the "pref" designation then we go with that instead.)
-                */
-               if ( (!strcasecmp(v->prop[i].name, "fburl"))
-                  ||(!strncasecmp(v->prop[i].name, "fburl;", 6)) ) {
-                       if ( (IsEmptyStr(calFBURL))
-                          || (!strncasecmp(v->prop[i].name, "fburl;pref", 10)) ) {
-                               safestrncpy(calFBURL, v->prop[i].value, sizeof calFBURL);
+       /* The last attribute must be a NULL one. */
+       attrs = (LDAPMod **) * object;
+       if (attrs) {
+               while (attrs[num_attrs]) {
+                       count = 0;
+                       while (attrs[num_attrs]->mod_values[count]) {
+                               CtdlLogPrintf(CTDL_DEBUG,
+                                       "LDAP: attribute %d, value %d = \'%s=%s\'\n",
+                                       num_attrs, count,
+                                       attrs[num_attrs]->mod_type,
+                                       attrs[num_attrs]->
+                                       mod_values[count]);
+                               count++;
                        }
+                       num_attrs++;
                }
-
-       }
-       vcard_free(v);  /* Don't need this anymore. */
-
-       /* "sn" (surname) based on info in vCard */
-       attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-       attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-       memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-       attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-       attrs[num_attrs-1]->mod_type            = "sn";
-       attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-       attrs[num_attrs-1]->mod_values[0]       = strdup(sn);
-       attrs[num_attrs-1]->mod_values[1]       = NULL;
-
-       /* "givenname" (first name) based on info in vCard */
-       if (IsEmptyStr(givenname)) strcpy(givenname, "_");
-       if (IsEmptyStr(sn)) strcpy(sn, "_");
-       attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-       attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-       memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-       attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-       attrs[num_attrs-1]->mod_type            = "givenname";
-       attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-       attrs[num_attrs-1]->mod_values[0]       = strdup(givenname);
-       attrs[num_attrs-1]->mod_values[1]       = NULL;
-
-       /* "uid" is a Kolab compatibility thing.  We just do cituser@citnode */
-       attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-       attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-       memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-       attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-       attrs[num_attrs-1]->mod_type            = "uid";
-       attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-       attrs[num_attrs-1]->mod_values[0]       = strdup(uid);
-       attrs[num_attrs-1]->mod_values[1]       = NULL;
-
-       /* Add a "cn" (Common Name) attribute based on the user's screen name,
-        * but only there was no 'fn' (full name) property in the vCard 
-        */
-       if (!have_cn) {
-               attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-               attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-               memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-               attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-               attrs[num_attrs-1]->mod_type            = "cn";
-               attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-               attrs[num_attrs-1]->mod_values[0]       = strdup(msg->cm_fields['A']);
-               attrs[num_attrs-1]->mod_values[1]       = NULL;
+       } else {
+               CtdlLogPrintf(CTDL_ERR,
+                       "LDAP: no attributes in save_ldap_object\n");
+               return -1;
        }
 
-       /* Add a "calFBURL" attribute if a calendar free/busy URL exists */
-       if (!IsEmptyStr(calFBURL)) {
-               attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-               attrs[num_attrs-1] = malloc(sizeof(LDAPMod));
-               memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
-               attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
-               attrs[num_attrs-1]->mod_type            = "calFBURL";
-               attrs[num_attrs-1]->mod_values          = malloc(2 * sizeof(char *));
-               attrs[num_attrs-1]->mod_values[0]       = strdup(calFBURL);
-               attrs[num_attrs-1]->mod_values[1]       = NULL;
-       }
-       
-       /* The last attribute must be a NULL one. */
-       attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
-       attrs[num_attrs - 1] = NULL;
-       
-       lprintf(CTDL_DEBUG, "Calling ldap_add_s() for '%s'\n", this_dn);
        begin_critical_section(S_LDAP);
+       if (connect_to_ldap()) {
+               end_critical_section(S_LDAP);
+               return -1;
+       }
+
        i = ldap_add_s(dirserver, this_dn, attrs);
-       end_critical_section(S_LDAP);
+
+       if (i == LDAP_SERVER_DOWN) {
+               CtdlAideMessage
+                   ("The LDAP server appears to be down.\nThe save to LDAP did not occurr.\n",
+                    "LDAP: save failed");
+               end_critical_section(S_LDAP);
+               return -1;
+       }
 
        /* If the entry already exists, repopulate it instead */
+       /* repopulating doesn't work as Citadel may want some attributes to be deleted.
+        * we have no way of knowing which attributes to delete and LDAP won't work it out for us
+        * so now we delete the old entry and create a new one.
+        */
        if (i == LDAP_ALREADY_EXISTS) {
-               for (j=0; j<(num_attrs-1); ++j) {
-                       attrs[j]->mod_op = LDAP_MOD_REPLACE;
-               }
-               lprintf(CTDL_DEBUG, "Calling ldap_modify_s() for '%s'\n", this_dn);
-               begin_critical_section(S_LDAP);
-               i = ldap_modify_s(dirserver, this_dn, attrs);
                end_critical_section(S_LDAP);
+               CtdlLogPrintf(CTDL_INFO,
+                       "LDAP: Create, already exists, deleteing first.\n");
+               if (delete_from_ldap(cn, ou, NULL))
+                       return -1;
+               begin_critical_section(S_LDAP);
+               CtdlLogPrintf(CTDL_INFO,
+                       "LDAP: Calling ldap_add_s() to recreate for dn of '%s'\n",
+                       this_dn);
+               i = ldap_add_s(dirserver, this_dn, attrs);
        }
 
        if (i != LDAP_SUCCESS) {
-               lprintf(CTDL_ERR, "ldap_add_s() failed: %s (%d)\n",
+               CtdlLogPrintf(CTDL_ERR, "LDAP: ldap_add_s() failed: %s (%d)\n",
                        ldap_err2string(i), i);
+               CtdlAideMessage
+                   ("The LDAP server refused the save command.\nDid you update the schema?\n",
+                    "LDAP: save failed (schema?)");
+               end_critical_section(S_LDAP);
+               return -1;
+       }
+       end_critical_section(S_LDAP);
+       return 0;
+}
+
+
+/*
+ * Free the object
+ */
+int free_ldap_object(char *cn, char *ou, void **object)
+{
+       int i, j;
+
+       LDAPMod **attrs;
+       int num_attrs = 0;
+
+       attrs = (LDAPMod **) * object;
+       if (attrs) {
+               while (attrs[num_attrs])
+                       num_attrs++;
        }
 
-       lprintf(CTDL_DEBUG, "Freeing attributes\n");
+       CtdlLogPrintf(CTDL_DEBUG, "LDAP: Freeing attributes\n");
        /* Free the attributes */
-       for (i=0; i<num_attrs; ++i) {
+       for (i = 0; i < num_attrs; ++i) {
                if (attrs[i] != NULL) {
 
                        /* First, free the value strings */
                        if (attrs[i]->mod_values != NULL) {
-                               for (j=0; attrs[i]->mod_values[j] != NULL; ++j) {
+                               for (j = 0;
+                                    attrs[i]->mod_values[j] != NULL;
+                                    ++j) {
                                        free(attrs[i]->mod_values[j]);
                                }
                        }
 
-                       /* Free the value strings pointer list */       
+                       /* Free the value strings pointer list */
                        if (attrs[i]->mod_values != NULL) {
                                free(attrs[i]->mod_values);
                        }
@@ -584,8 +470,74 @@ void ctdl_vcard_to_ldap(struct CtdlMessage *msg, int op) {
                        free(attrs[i]);
                }
        }
+       free(attrs[i]);
        free(attrs);
-       lprintf(CTDL_DEBUG, "LDAP write operation complete.\n");
+       *object = NULL;
+       return 0;
+}
+
+
+/*
+ * Delete a record from the LDAP
+ *
+ * parameter object not used here, present for hook interface compatability
+ */
+int delete_from_ldap(char *cn, char *ou, void **object)
+{
+       int i;
+
+       char this_dn[SIZ];
+
+       if (dirserver == NULL)
+               return -1;
+       if (cn == NULL)
+               return -1;
+
+       sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
+
+       CtdlLogPrintf(CTDL_DEBUG, "LDAP: Calling ldap_delete_s()\n");
+
+       begin_critical_section(S_LDAP);
+       if (connect_to_ldap()) {
+               end_critical_section(S_LDAP);
+               return -1;
+       }
+
+       i = ldap_delete_s(dirserver, this_dn);
+
+       if (i == LDAP_SERVER_DOWN) {
+               end_critical_section(S_LDAP);
+               CtdlAideMessage
+                   ("The LDAP server appears to be down.\nThe delete from LDAP did not occurr.\n",
+                    "LDAP: delete failed");
+               return -1;
+       }
+
+       if (i != LDAP_SUCCESS) {
+               CtdlLogPrintf(CTDL_ERR,
+                       "LDAP: ldap_delete_s() failed: %s (%d)\n",
+                       ldap_err2string(i), i);
+               end_critical_section(S_LDAP);
+               CtdlAideMessage(ldap_err2string(i), "LDAP: delete failed");
+               return -1;
+       }
+       end_critical_section(S_LDAP);
+       return 0;
+}
+
+
+
+
+void ldap_disconnect_timer(void)
+{
+       begin_critical_section(S_LDAP);
+       if (ldap_time_disconnect) {
+               ldap_time_disconnect--;
+               end_critical_section(S_LDAP);
+               return;
+       }
+       serv_ldap_cleanup();
+       end_critical_section(S_LDAP);
 }
 
 
@@ -597,15 +549,35 @@ void ctdl_vcard_to_ldap(struct CtdlMessage *msg, int op) {
  */
 CTDL_MODULE_INIT(ldap)
 {
+       if (!threading)
+       {
 #ifdef HAVE_LDAP
-       CtdlRegisterCleanupHook(serv_ldap_cleanup);
-
-       if (!IsEmptyStr(config.c_ldap_host)) {
-               CtdlConnectToLdap();
-       }
-
+               if (!IsEmptyStr(config.c_ldap_base_dn)) {
+                       CtdlRegisterCleanupHook(serv_ldap_cleanup);
+                       CtdlRegisterSessionHook(ldap_disconnect_timer, EVT_TIMER);
+                       CtdlRegisterDirectoryServiceFunc(delete_from_ldap,
+                                                        DIRECTORY_USER_DEL,
+                                                        "ldap");
+                       CtdlRegisterDirectoryServiceFunc(create_ldap_host_OU,
+                                                        DIRECTORY_CREATE_HOST,
+                                                        "ldap");
+                       CtdlRegisterDirectoryServiceFunc(create_ldap_object,
+                                                        DIRECTORY_CREATE_OBJECT,
+                                                        "ldap");
+                       CtdlRegisterDirectoryServiceFunc(add_ldap_object,
+                                                        DIRECTORY_ATTRIB_ADD,
+                                                        "ldap");
+                       CtdlRegisterDirectoryServiceFunc(save_ldap_object,
+                                                        DIRECTORY_SAVE_OBJECT,
+                                                        "ldap");
+                       CtdlRegisterDirectoryServiceFunc(free_ldap_object,
+                                                        DIRECTORY_FREE_OBJECT,
+                                                        "ldap");
+                       create_ldap_root();
+               }
 #endif                         /* HAVE_LDAP */
-
+       }
+       
        /* return our Subversion id for the Log */
        return "$Id$";
 }