#include "msgbase.h"
#include "serv_ldap.h"
#include "vcard.h"
+#include "tools.h"
#ifdef HAVE_LDAP
{
if (!dirserver) return;
- lprintf(7, "Unbinding from directory server\n");
+ lprintf(CTDL_INFO, "Unbinding from directory server\n");
ldap_unbind(dirserver);
dirserver = NULL;
}
-#endif /* HAVE_LDAP */
+
+
+/*
+ * Create the root node. If it's already there, so what?
+ */
+void CtdlCreateLdapRoot(void) {
+ char *dc_values[2];
+ char *objectClass_values[3];
+ LDAPMod dc, objectClass;
+ LDAPMod *mods[3];
+ char topdc[SIZ];
+ int i;
+
+ /* We just want the top-level dc, not the whole hierarchy */
+ strcpy(topdc, config.c_ldap_base_dn);
+ for (i=0; i<strlen(topdc); ++i) {
+ if (topdc[i] == ',') topdc[i] = 0;
+ }
+ for (i=0; i<strlen(topdc); ++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;
+ mods[0] = &dc;
+ mods[1] = &objectClass;
+ mods[2] = NULL;
+
+ /* Perform the transaction */
+ lprintf(CTDL_DEBUG, "Setting up Base DN node...\n");
+ begin_critical_section(S_LDAP);
+ i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
+ end_critical_section(S_LDAP);
+
+ if (i != LDAP_SUCCESS) {
+ lprintf(CTDL_CRIT, "ldap_add_s() failed: %s (%d)\n",
+ ldap_err2string(i), i);
+ }
+}
+
+
+/*
+ * Create an OU node representing a Citadel host.
+ */
+void CtdlCreateHostOU(char *host) {
+ char *dc_values[2];
+ char *objectClass_values[3];
+ LDAPMod dc, objectClass;
+ LDAPMod *mods[3];
+ int i;
+ char dn[SIZ];
+
+ /* The DN is this OU plus the base. */
+ 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;
+ mods[0] = &dc;
+ mods[1] = &objectClass;
+ mods[2] = NULL;
+
+ /* Perform the transaction */
+ lprintf(CTDL_DEBUG, "Setting up Host OU node...\n");
+ begin_critical_section(S_LDAP);
+ i = ldap_add_s(dirserver, dn, mods);
+ end_critical_section(S_LDAP);
+
+ /* ignore the error -- it's ok if it already exists
+ if (i != LDAP_SUCCESS) {
+ lprintf(CTDL_ERR, "ldap_add_s() failed: %s (%d)\n",
+ ldap_err2string(i), i);
+ }
+ */
+}
+
+
+
+
+
+
void CtdlConnectToLdap(void) {
int i;
int ldap_version = 3;
- lprintf(7, "Connecting to LDAP server %s:%d...\n",
+ lprintf(CTDL_INFO, "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) {
- lprintf(3, "Could not connect to %s:%d : %s\n",
+ lprintf(CTDL_CRIT, "Could not connect to %s:%d : %s\n",
config.c_ldap_host,
config.c_ldap_port,
strerror(errno));
ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
- lprintf(7, "Binding to %s\n", config.c_ldap_bind_dn);
+ lprintf(CTDL_INFO, "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) {
- lprintf(3, "Cannot bind: %s (%d)\n", ldap_err2string(i), i);
+ lprintf(CTDL_CRIT, "Cannot bind: %s (%d)\n", ldap_err2string(i), i);
dirserver = NULL; /* FIXME disconnect from ldap */
+ return;
}
-}
+ CtdlCreateLdapRoot();
+}
-/*
- * Write (add, or change if already exists) a directory entry to the
+/*
+ * 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.
*/
-void ctdl_vcard_to_ldap(struct CtdlMessage *msg) {
+void ctdl_vcard_to_ldap(struct CtdlMessage *msg, int op) {
struct vCard *v = NULL;
int i, j;
char this_dn[SIZ];
LDAPMod **attrs = NULL;
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[SIZ];
+ char sn[SIZ];
+ char uid[SIZ];
+ char street[SIZ];
+ char city[SIZ];
+ char state[SIZ];
+ char zipcode[SIZ];
if (dirserver == NULL) return;
if (msg == NULL) return;
if (msg->cm_fields['A'] == NULL) return;
if (msg->cm_fields['N'] == NULL) return;
+ /* Initialize variables */
+ strcpy(givenname, "");
+ strcpy(sn, "");
+
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
memset(attrs[0], 0, sizeof(LDAPMod));
attrs[0]->mod_op = LDAP_MOD_ADD;
attrs[0]->mod_type = "objectclass";
- attrs[0]->mod_values = mallok(5 * sizeof(char *));
+ attrs[0]->mod_values = mallok(3 * sizeof(char *));
attrs[0]->mod_values[0] = strdoop("inetOrgPerson");
- attrs[0]->mod_values[1] = strdoop("organizationalPerson");
- attrs[0]->mod_values[2] = strdoop("person");
- attrs[0]->mod_values[3] = strdoop("Top");
- attrs[0]->mod_values[4] = NULL;
-
+ 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) {
-/*
- v->prop[i].name
- v->prop[i].value
- */
+ 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, ';');
+ extract_token(givenname, v->prop[i].value, 1, ';');
+ }
+
+ if (!strcasecmp(v->prop[i].name, "fn")) {
+ attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ attrs[num_attrs-1] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[num_attrs-1]->mod_values[0] = strdoop(v->prop[i].value);
+ attrs[num_attrs-1]->mod_values[1] = NULL;
+ have_cn = 1;
+ }
+
+ if (!strcasecmp(v->prop[i].name, "title")) {
+ attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ attrs[num_attrs-1] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[num_attrs-1]->mod_values[0] = strdoop(v->prop[i].value);
+ attrs[num_attrs-1]->mod_values[1] = NULL;
+ }
+
+ if (!strcasecmp(v->prop[i].name, "org")) {
+ attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ attrs[num_attrs-1] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[num_attrs-1]->mod_values[0] = strdoop(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, ';'); /* po box */
+ strcat(street, " ");
+ extract_token(&street[strlen(street)],
+ v->prop[i].value, 1, ';'); /* extend addr */
+ strcat(street, " ");
+ extract_token(&street[strlen(street)],
+ v->prop[i].value, 2, ';'); /* street */
+ striplt(street);
+ extract_token(city, v->prop[i].value, 3, ';');
+ extract_token(state, v->prop[i].value, 4, ';');
+ extract_token(zipcode, v->prop[i].value, 5, ';');
+
+ attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ attrs[num_attrs-1] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[num_attrs-1]->mod_values[0] = strdoop(street);
+ attrs[num_attrs-1]->mod_values[1] = NULL;
+
+ attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ attrs[num_attrs-1] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[num_attrs-1]->mod_values[0] = strdoop(city);
+ attrs[num_attrs-1]->mod_values[1] = NULL;
+
+ attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ attrs[num_attrs-1] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[num_attrs-1]->mod_values[0] = strdoop(state);
+ attrs[num_attrs-1]->mod_values[1] = NULL;
+
+ attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ attrs[num_attrs-1] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[num_attrs-1]->mod_values[0] = strdoop(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 = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ phone_attr = num_attrs-1;
+ attrs[phone_attr] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[phone_attr]->mod_values[0] = strdoop(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 = reallok(attrs[phone_attr]->mod_values,
+ num_phones * sizeof(char *));
+ attrs[phone_attr]->mod_values[num_phones-1]
+ = strdoop(v->prop[i].value);
+ attrs[phone_attr]->mod_values[num_phones]
+ = NULL;
+ }
+ }
+
+
+ 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 = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ attrs[num_attrs-1] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[num_attrs-1]->mod_values[0] = strdoop(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 = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ alias_attr = num_attrs-1;
+ attrs[alias_attr] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[alias_attr]->mod_values[0] = strdoop(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 = reallok(attrs[alias_attr]->mod_values,
+ num_emails * sizeof(char *));
+ attrs[alias_attr]->mod_values[num_emails-2]
+ = strdoop(v->prop[i].value);
+ attrs[alias_attr]->mod_values[num_emails-1]
+ = NULL;
+ }
+
+
+ }
+
}
vcard_free(v); /* Don't need this anymore. */
+ /* "sn" (surname) based on info in vCard */
+ attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ attrs[num_attrs-1] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[num_attrs-1]->mod_values[0] = strdoop(sn);
+ attrs[num_attrs-1]->mod_values[1] = NULL;
+
+ /* "givenname" (first name) based on info in vCard */
+ if (strlen(givenname) == 0) strcpy(givenname, "_");
+ if (strlen(sn) == 0) strcpy(sn, "_");
+ attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ attrs[num_attrs-1] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[num_attrs-1]->mod_values[0] = strdoop(givenname);
+ attrs[num_attrs-1]->mod_values[1] = NULL;
+
+ /* "uid" is a Kolab compatibility thing. We just do cituser@citnode */
+ attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ attrs[num_attrs-1] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[num_attrs-1]->mod_values[0] = strdoop(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 = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
+ attrs[num_attrs-1] = mallok(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 = mallok(2 * sizeof(char *));
+ attrs[num_attrs-1]->mod_values[0] = strdoop(msg->cm_fields['A']);
+ 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(9, "this_dn: <%s>\n", this_dn);
-
+ lprintf(CTDL_DEBUG, "Calling ldap_add_s()\n");
begin_critical_section(S_LDAP);
i = ldap_add_s(dirserver, this_dn, attrs);
end_critical_section(S_LDAP);
+
+ /* If the entry already exists, repopulate it instead */
+ 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()\n");
+ begin_critical_section(S_LDAP);
+ i = ldap_modify_s(dirserver, this_dn, attrs);
+ end_critical_section(S_LDAP);
+ }
+
if (i != LDAP_SUCCESS) {
- lprintf(3, "ldap_add_s() failed: %s (%d)\n",
+ lprintf(CTDL_ERR, "ldap_add_s() failed: %s (%d)\n",
ldap_err2string(i), i);
}
+ lprintf(CTDL_DEBUG, "Freeing attributes\n");
/* Free the attributes */
for (i=0; i<num_attrs; ++i) {
if (attrs[i] != NULL) {
}
}
phree(attrs);
+ lprintf(CTDL_DEBUG, "LDAP write operation complete.\n");
}
+#endif /* HAVE_LDAP */
/*