From: Dave West Date: Fri, 5 Oct 2007 02:19:17 +0000 (+0000) Subject: Big change to the ldap code to break its dependancy on serv_vcard.c and X-Git-Tag: v7.86~3017 X-Git-Url: https://code.citadel.org/?p=citadel.git;a=commitdiff_plain;h=e646cd5a79374f3a3aee98df28468dacecfe6ec0 Big change to the ldap code to break its dependancy on serv_vcard.c and vice-versa. This is also the beginings of a universal directory services interface. Also added the missing pager module source code. --- diff --git a/citadel/include/ctdl_module.h b/citadel/include/ctdl_module.h index ce1b148f8..5d8764db4 100644 --- a/citadel/include/ctdl_module.h +++ b/citadel/include/ctdl_module.h @@ -66,6 +66,21 @@ void CtdlRegisterMaintenanceThread(char *name, void *(*thread_proc) (void *arg)) void CtdlRegisterSearchFuncHook(void (*fcn_ptr)(int *, long **, char *), char *name); + +/* + * Directory services hooks for LDAP etc + */ + +#define DIRECTORY_USER_DEL 1 // Delete a user entry +#define DIRECTORY_CREATE_HOST 2 // Create a host entry if not already there. +#define DIRECTORY_CREATE_OBJECT 3 // Create a new object for directory entry +#define DIRECTORY_ATTRIB_ADD 4 // Add an attribute to the directory entry object +#define DIRECTORY_SAVE_OBJECT 5 // Save the object to the directory service +#define DIRECTORY_FREE_OBJECT 6 // Free the object and its attributes + +int CtdlRegisterDirectoryServiceFunc(int (*func)(char *cn, char *ou, void **object), int cmd, char *module); +int CtdlDoDirectoryServiceFunc(char *cn, char *ou, void **object, char *module, int cmd); + /* TODODRW: This needs to be changed into a hook type interface * for now we have this horrible hack */ diff --git a/citadel/modules/ldap/serv_ldap.c b/citadel/modules/ldap/serv_ldap.c index ac2ef9f43..018a1f845 100644 --- a/citadel/modules/ldap/serv_ldap.c +++ b/citadel/modules/ldap/serv_ldap.c @@ -39,7 +39,6 @@ #include "database.h" #include "msgbase.h" #include "serv_ldap.h" -#include "vcard.h" #include "tools.h" @@ -126,8 +125,10 @@ 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 CtdlCreateLdapHostOU(char *cn, char *host, void **object) { char *dc_values[2]; char *objectClass_values[3]; LDAPMod dc, objectClass; @@ -166,7 +167,9 @@ void CtdlCreateHostOU(char *host) { else if (i != LDAP_SUCCESS) { lprintf(CTDL_CRIT, "ldap_add_s() failed: %s (%d)\n", ldap_err2string(i), i); + return -1; } + return 0; } @@ -210,346 +213,107 @@ void CtdlConnectToLdap(void) { } -/* - * 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, 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[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; - } - - 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; - } - } +/* + * Create a base LDAP object for the interface + */ + +int CtdlCreateLdapObject(char *cn, char *ou, void **object) +{ + // We do nothing here, this just gets the base structure created by the interface. + lprintf (CTDL_DEBUG, "Created ldap object\n"); + return 0; +} - if ( (!strcasecmp(v->prop[i].name, "email")) - ||(!strcasecmp(v->prop[i].name, "email;internet")) ) { +/* + * Add an attribute to the ldap object + */ + +int CtdlAddLdapAttr(char *cn, char *ou, void **object) +{ + LDAPMod **attrs ; + int num_attrs = 0; + int num_values = 0; + int cur_attr; - ++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; - } - - - } - - /* 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); + + lprintf (CTDL_DEBUG, "Adding ldap attribute\n"); + + attrs = *object; + if (attrs) + { + while (attrs[num_attrs]) + num_attrs++; + } + + 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]) + 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; } - - } - 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; } + 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; +} - /* 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; - } + +/* + * SAve the object to the LDAP server + */ +int CtdlSaveLdapObject(char *cn, char *ou, void **object) +{ + int i, j; + + char this_dn[SIZ]; + LDAPMod **attrs ; + int num_attrs = 0; + int count = 0; + + if (dirserver == NULL) return -1; + if (ou == NULL) return -1; + if (cn == NULL) return -1; + sprintf(this_dn, "cn=%s,ou=%s,%s", cn, ou, config.c_ldap_base_dn); + /* The last attribute must be a NULL one. */ - attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) ); - attrs[num_attrs - 1] = NULL; + attrs = (LDAPMod **)*object; + if (attrs) + { + while (attrs[num_attrs]) + num_attrs++; + } lprintf(CTDL_DEBUG, "Calling ldap_add_s() for '%s'\n", this_dn); + 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) { + for (j=0; j<(num_attrs); ++j) { attrs[j]->mod_op = LDAP_MOD_REPLACE; } lprintf(CTDL_DEBUG, "Calling ldap_modify_s() for '%s'\n", this_dn); @@ -561,6 +325,27 @@ void ctdl_vcard_to_ldap(struct CtdlMessage *msg, int op) { if (i != LDAP_SUCCESS) { lprintf(CTDL_ERR, "ldap_add_s() failed: %s (%d)\n", ldap_err2string(i), i); + return -1; + } + return 0; +} + + +/* + * Free the object + */ +int CtdlFreeLdapObject(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"); @@ -584,8 +369,42 @@ 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 CtdlDeleteFromLdap(char *cn, char *ou, void **object) +{ + int i; + + char this_dn[SIZ]; + + if (dirserver == NULL) return -1; + if (ou == NULL) return -1; + if (cn == NULL) return -1; + + sprintf(this_dn, "cn=%s,ou=%s,%s", cn, ou, config.c_ldap_base_dn); + + 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 -1; + } + return 0; } @@ -599,6 +418,13 @@ CTDL_MODULE_INIT(ldap) { #ifdef HAVE_LDAP CtdlRegisterCleanupHook(serv_ldap_cleanup); + CtdlRegisterDirectoryServiceFunc(CtdlDeleteFromLdap, DIRECTORY_USER_DEL, "ldap"); + CtdlRegisterDirectoryServiceFunc(CtdlCreateLdapHostOU, DIRECTORY_CREATE_HOST, "ldap"); + CtdlRegisterDirectoryServiceFunc(CtdlCreateLdapObject, DIRECTORY_CREATE_OBJECT, "ldap"); + CtdlRegisterDirectoryServiceFunc(CtdlAddLdapAttr, DIRECTORY_ATTRIB_ADD, "ldap"); + CtdlRegisterDirectoryServiceFunc(CtdlSaveLdapObject, DIRECTORY_SAVE_OBJECT, "ldap"); + CtdlRegisterDirectoryServiceFunc(CtdlFreeLdapObject, DIRECTORY_FREE_OBJECT, "ldap"); + if (!IsEmptyStr(config.c_ldap_host)) { CtdlConnectToLdap(); diff --git a/citadel/modules/pager/serv_pager.c b/citadel/modules/pager/serv_pager.c new file mode 100644 index 000000000..01d154060 --- /dev/null +++ b/citadel/modules/pager/serv_pager.c @@ -0,0 +1,222 @@ +/* + * This module implements an external pager hook for when notifcation + * of a new email is wanted. + * Based on bits of serv_funambol + */ + +#include "sysdep.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include +#include +#include +#include +#include "citadel.h" +#include "server.h" +#include "citserver.h" +#include "support.h" +#include "config.h" +#include "control.h" +#include "room_ops.h" +#include "user_ops.h" +#include "policy.h" +#include "database.h" +#include "msgbase.h" +#include "tools.h" +#include "internet_addressing.h" +#include "domain.h" +#include "clientsocket.h" +#include "serv_pager.h" + +#include "ctdl_module.h" + +#define PAGER_CONFIG_MESSAGE "__ Push email settings __" +#define PAGER_CONFIG_TEXT "textmessage" + +/* + * Create the notify message queue. We use the exact same room + */ +void create_pager_queue(void) { + struct ctdlroom qrbuf; + + create_room(FNBL_QUEUE_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX); + + /* + * Make sure it's set to be a "system room" so it doesn't show up + * in the nown rooms list for Aides. + */ + if (lgetroom(&qrbuf, FNBL_QUEUE_ROOM) == 0) { + qrbuf.QRflags2 |= QR2_SYSTEM; + lputroom(&qrbuf); + } +} +void do_pager_queue(void) { + static int doing_queue = 0; + + /* + * This is a simple concurrency check to make sure only one queue run + * is done at a time. We could do this with a mutex, but since we + * don't really require extremely fine granularity here, we'll do it + * with a static variable instead. + */ + if (doing_queue) return; + doing_queue = 1; + + /* + * Go ahead and run the queue + */ + lprintf(CTDL_DEBUG, "serv_pager: processing notify queue\n"); + + if (getroom(&CC->room, FNBL_QUEUE_ROOM) != 0) { + lprintf(CTDL_ERR, "Cannot find room <%s>\n", FNBL_QUEUE_ROOM); + return; + } + CtdlForEachMessage(MSGS_ALL, 0L, NULL, + SPOOLMIME, NULL, notify_pager, NULL); + + lprintf(CTDL_DEBUG, "serv_pager: queue run completed\n"); + doing_queue = 0; +} + +/* + * Call the external tool + */ +void notify_pager(long msgnum, void *userdata) { + struct CtdlMessage *msg; + struct ctdlroom qrbuf; + + /* W means 'wireless', which contains the unix name */ + msg = CtdlFetchMessage(msgnum, 1); + if ( msg->cm_fields['W'] == NULL) { + goto nuke; + } + /* Are we allowed to push? */ + if (IsEmptyStr(config.c_pager_program)) { + return; + } else if (IsEmptyStr(config.c_pager_program) && IsEmptyStr(config.c_funambol_host)) { + goto nuke; + } else { + lprintf(CTDL_INFO, "Pager alerter enabled\n"); + } + + /* Get the configuration. We might be allowed system wide but the user + may have configured otherwise */ + long configMsgNum = pager_getConfigMessage(msg->cm_fields['W']); + int allowed = pager_isPagerAllowedByPrefs(configMsgNum); + if (allowed != 0 && pager_doesUserWant(configMsgNum) == 0) { + goto nuke; + } else if (allowed != 0) { + return; + } + char *num = pager_getUserPhoneNumber(configMsgNum); + char command[SIZ]; + snprintf(command, sizeof command, "%s %s -u %s", config.c_pager_program, num, &msg->cm_fields['W']); + system(command); + + nuke: + CtdlFreeMessage(msg); + long todelete[1]; + todelete[0] = msgnum; + CtdlDeleteMessages(FNBL_QUEUE_ROOM, todelete, 1, ""); +} + +long pager_getConfigMessage(char *username) { + struct ctdlroom qrbuf; // scratch for room + struct ctdluser user; // ctdl user instance + char configRoomName[ROOMNAMELEN]; + struct CtdlMessage *template; + struct CtdlMessage *msg; + struct cdbdata *cdbfr; + long *msglist = NULL; + int num_msgs = 0; + long confMsgNum = -1; + // Get the user + getuser(&user, username); + + MailboxName(configRoomName, sizeof configRoomName, &user, USERCONFIGROOM); + int prefroom = getroom(&qrbuf, configRoomName); + + /* Do something really, really stoopid here. Raid the room on ourselves, + loop through the messages manually and find it. I don't want + to use a CtdlForEachMessage callback here, as we would be + already in one */ + cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long)); + if (cdbfr != NULL) { + msglist = (long *) cdbfr->ptr; + cdbfr->ptr = NULL; /* CtdlForEachMessage() now owns this memory */ + num_msgs = cdbfr->len / sizeof(long); + cdb_free(cdbfr); + } else { + return -1; /* No messages at all? No further action. */ + } + int a; + for (a = 0; a < num_msgs; ++a) { + msg = CtdlFetchMessage(msglist[a], 1); + if (msg != NULL) { + if (msg->cm_fields['U'] != NULL && strncasecmp(msg->cm_fields['U'], PAGER_CONFIG_MESSAGE, + strlen(PAGER_CONFIG_MESSAGE)) == 0) { + confMsgNum = msglist[a]; + } + CtdlFreeMessage(msg); + } + } + return confMsgNum; + +} +int pager_isPagerAllowedByPrefs(long configMsgNum) { + // Do a simple string search to see if 'textmessage' is selected as the + // type. This string would be at the very top of the message contents. + if (configMsgNum == -1) { + return -1; + } + struct CtdlMessage *prefMsg; + prefMsg = CtdlFetchMessage(configMsgNum, 1); + char *msgContents = prefMsg->cm_fields['M']; + return strncasecmp(msgContents, PAGER_CONFIG_TEXT, strlen(PAGER_CONFIG_TEXT)); +} +int pager_doesUserWant(long configMsgNum) { + if (configMsgNum == -1) { + return -1; + } + struct CtdlMessage *prefMsg; + prefMsg = CtdlFetchMessage(configMsgNum, 1); + char *msgContents = prefMsg->cm_fields['M']; + return strncasecmp(msgContents, "none", 4); +} + /* warning: fetching twice gravely inefficient, will fix some time */ +char *pager_getUserPhoneNumber(long configMsgNum) { + if (configMsgNum == -1) { + return; + } + struct CtdlMessage *prefMsg; + prefMsg = CtdlFetchMessage(configMsgNum, 1); + char *msgContents = prefMsg->cm_fields['M']; + char *lines = strtok(msgContents, "textmessage\n"); + return lines; +} +CTDL_MODULE_INIT(pager) +{ + create_pager_queue(); + CtdlRegisterSessionHook(do_pager_queue, EVT_TIMER); + + /* return our Subversion id for the Log */ + return "$Id: serv_pager.c $"; +} diff --git a/citadel/modules/vcard/serv_vcard.c b/citadel/modules/vcard/serv_vcard.c index 54d3c39cb..016de159c 100644 --- a/citadel/modules/vcard/serv_vcard.c +++ b/citadel/modules/vcard/serv_vcard.c @@ -63,7 +63,6 @@ #include "mime_parser.h" #include "vcard.h" #include "serv_vcard.h" -#include "serv_ldap.h" #include "ctdl_module.h" @@ -128,6 +127,195 @@ void vcard_extract_internet_addresses(struct CtdlMessage *msg, } +/* + * 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, int op) { + struct vCard *v = NULL; + int i; + int num_emails = 0; + int num_phones = 0; + int have_addr = 0; + int have_cn = 0; + + void *objectlist = NULL; + + 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 (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(uid, "%s@%s", + msg->cm_fields['A'], + msg->cm_fields['N'] + ); + + /* Are we just deleting? If so, it's simple... */ + if (op == V2L_DELETE) { + (void) CtdlDoDirectoryServiceFunc (msg->cm_fields['A'], msg->cm_fields['N'], NULL, "ldap", DIRECTORY_USER_DEL); + 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 */ + (void) CtdlDoDirectoryServiceFunc (NULL, msg->cm_fields['N'], NULL, "ldap", DIRECTORY_CREATE_HOST); + + /* Next create the directory service object */ + (void) CtdlDoDirectoryServiceFunc(NULL, NULL, &objectlist, "ldap", DIRECTORY_CREATE_OBJECT); + + /* The first LDAP attribute will be an 'objectclass' list. Citadel + * doesn't do anything with this. It's just there for compatibility + * with Kolab. + */ + (void) CtdlDoDirectoryServiceFunc("objectclass", "citadelInetOrgPerson", &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + + /* 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")) { + (void) CtdlDoDirectoryServiceFunc("cn", v->prop[i].value, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + have_cn = 1; + } + + if (!strcasecmp(v->prop[i].name, "title")) { + (void) CtdlDoDirectoryServiceFunc("title", v->prop[i].value, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + } + + if (!strcasecmp(v->prop[i].name, "org")) { + (void) CtdlDoDirectoryServiceFunc("o", v->prop[i].value, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + } + + 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); + + (void) CtdlDoDirectoryServiceFunc("street", street, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + (void) CtdlDoDirectoryServiceFunc("l", city, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + (void) CtdlDoDirectoryServiceFunc("st", state, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + (void) CtdlDoDirectoryServiceFunc("postalcode", zipcode, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + } + } + + 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) { + (void) CtdlDoDirectoryServiceFunc("telephoneNumber", v->prop[i].value, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + } + /* Subsequent 'tel' properties *add to* the 'telephoneNumber' attribute */ + else { + (void) CtdlDoDirectoryServiceFunc("telephoneNumber", v->prop[i].value, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + } + } + + + 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) { + (void) CtdlDoDirectoryServiceFunc("mail", v->prop[i].value, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + } + /* The second and subsequent email address creates the 'alias' attribute */ + else if (num_emails >= 2) { + (void) CtdlDoDirectoryServiceFunc("alias", v->prop[i].value, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + } + } + + /* 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); + } + } + + } + vcard_free(v); /* Don't need this anymore. */ + + /* "sn" (surname) based on info in vCard */ + (void) CtdlDoDirectoryServiceFunc("sn", sn, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + + /* "givenname" (first name) based on info in vCard */ + if (IsEmptyStr(givenname)) strcpy(givenname, "_"); + if (IsEmptyStr(sn)) strcpy(sn, "_"); + (void) CtdlDoDirectoryServiceFunc("givenname", givenname, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + + /* "uid" is a Kolab compatibility thing. We just do cituser@citnode */ + (void) CtdlDoDirectoryServiceFunc("uid", uid, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + + /* 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) { + (void) CtdlDoDirectoryServiceFunc("cn", msg->cm_fields['A'], &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + } + + /* Add a "calFBURL" attribute if a calendar free/busy URL exists */ + if (!IsEmptyStr(calFBURL)) { + (void) CtdlDoDirectoryServiceFunc("calFBURL", calFBURL, &objectlist, "ldap", DIRECTORY_ATTRIB_ADD); + } + + (void) CtdlDoDirectoryServiceFunc(msg->cm_fields['A'], msg->cm_fields['N'], &objectlist, "ldap", DIRECTORY_SAVE_OBJECT); + + (void) CtdlDoDirectoryServiceFunc(NULL, NULL, &objectlist, "ldap", DIRECTORY_FREE_OBJECT); + lprintf(CTDL_DEBUG, "LDAP write operation complete.\n"); +} + + /* * Callback for vcard_add_to_directory() diff --git a/citadel/serv_extensions.c b/citadel/serv_extensions.c index 7af3da534..9cc062c6c 100644 --- a/citadel/serv_extensions.c +++ b/citadel/serv_extensions.c @@ -25,6 +25,8 @@ #include "modules/crypto/serv_crypto.h" /* Needed until a universal crypto startup hook is implimented for CtdlStartTLS */ +#include "ctdl_module.h" + #ifndef HAVE_SNPRINTF #include #include "snprintf.h" @@ -51,6 +53,19 @@ struct ProtoFunctionHook { } *ProtoHookList = NULL; +struct DirectoryServiceHook { + int (*handler) (char *cn, char *ou, void **object); + int cmd; + char *module; + struct DirectoryServiceHook *next; +} *DirectoryServiceHookList = NULL; + +struct DirectoryObject { + char *module; + void *object; + struct DirectoryObject *next; +}; + #define ERR_PORT (1 << 1) @@ -1043,6 +1058,118 @@ void CtdlRegisterMaintenanceThread(char *name, void *(*thread_proc)(void *arg)) } + +int CtdlRegisterDirectoryServiceFunc(int (*func)(char *cn, char *ou, void **object), int cmd, char *module) +{ + struct DirectoryServiceHook *newfcn; + + newfcn = DirectoryServiceHookList; + while (newfcn) + { + if (newfcn->cmd == cmd && !strcmp(newfcn->module, module)) + { + lprintf(CTDL_ERR, "Directory service function already handled by module %s\n", module); + return -1; + } + newfcn = newfcn->next; + } + + newfcn = (struct DirectoryServiceHook *) malloc (sizeof(struct DirectoryServiceHook)); + newfcn->handler = func; + newfcn->cmd = cmd; + newfcn->module = module; + newfcn->next = DirectoryServiceHookList; + DirectoryServiceHookList = newfcn; + + lprintf(CTDL_INFO, "Registered a new directory service function from module %s\n", module); + return 0; +} + +int CtdlDoDirectoryServiceFunc(char *cn, char *ou, void **object, char *module, int cmd) +{ + struct DirectoryServiceHook *curfcn; + struct DirectoryObject *our_object_list = NULL; + struct DirectoryObject *newobject = NULL; + struct DirectoryObject *oldobject = NULL; + + + curfcn = DirectoryServiceHookList; + if (object) + our_object_list = (struct DirectoryObject *) *object; + + while (curfcn) + { + if (curfcn->cmd == cmd) + { + if (!module) + { + if (cmd == DIRECTORY_CREATE_OBJECT) + { + newobject = (struct DirectoryObject*) malloc (sizeof(struct DirectoryObject)); + newobject->module = curfcn->module; + newobject->object = NULL; + newobject->next = our_object_list; + our_object_list = newobject; + } + if (our_object_list) + { + for(newobject = our_object_list; newobject; newobject=newobject->next) + { + if (!strcmp(newobject->module, curfcn->module)) + (void) curfcn->handler(cn, ou, &newobject->object); + } + } + else + (void) curfcn->handler(cn, ou, NULL); + + continue; + } + else + { + if(!strcmp(curfcn->module, module)) + { + if (cmd == DIRECTORY_CREATE_OBJECT) + { + newobject = (struct DirectoryObject*) malloc (sizeof(struct DirectoryObject)); + newobject->module = module; + newobject->object = NULL; + newobject->next = our_object_list; + our_object_list = newobject; + } + if (our_object_list) + { + for(newobject = our_object_list; newobject; newobject=newobject->next) + { + if (!strcmp(newobject->module, curfcn->module)) + (void) curfcn->handler(cn, ou, &newobject->object); + } + } + else + (void) (curfcn->handler(cn, ou, NULL)); + + break; + } + } + } + curfcn=curfcn->next; + } + if (our_object_list) + { + *object = our_object_list; + if (cmd == DIRECTORY_FREE_OBJECT) + { // The objects pointed to by the list should have been freed by the module that created it + for(newobject = our_object_list; newobject; ) + { + oldobject=newobject; + newobject=newobject->next; + free(oldobject); + } + *object=NULL; + } + } + return 0; +} + /* * Dirty hack until we impliment a hook mechanism for this */ diff --git a/citadel/serv_ldap.h b/citadel/serv_ldap.h index f2595dba9..5f3130d63 100644 --- a/citadel/serv_ldap.h +++ b/citadel/serv_ldap.h @@ -7,9 +7,4 @@ void ctdl_vcard_to_ldap(struct CtdlMessage *msg, int op); -enum { - V2L_WRITE, - V2L_DELETE -}; - #endif /* HAVE_LDAP */ diff --git a/citadel/serv_vcard.h b/citadel/serv_vcard.h index 0533cc8ff..003b8d3d7 100644 --- a/citadel/serv_vcard.h +++ b/citadel/serv_vcard.h @@ -1,5 +1,11 @@ /* - * $Id: $ + * $Id$ */ void extract_inet_email_addrs(char *, size_t, char *, size_t, struct vCard *v, int local_addrs_only); struct vCard *vcard_get_user(struct ctdluser *u); + +enum { + V2L_WRITE, + V2L_DELETE +}; +