#include "database.h"
#include "msgbase.h"
#include "serv_ldap.h"
-#include "vcard.h"
#include "tools.h"
/*
* 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;
else if (i != LDAP_SUCCESS) {
lprintf(CTDL_CRIT, "ldap_add_s() failed: %s (%d)\n",
ldap_err2string(i), i);
+ return -1;
}
+ 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.
- */
-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);
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");
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;
}
{
#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();
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#include <sys/wait.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/socket.h>
+#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 <K>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 $";
+}
#include "mime_parser.h"
#include "vcard.h"
#include "serv_vcard.h"
-#include "serv_ldap.h"
#include "ctdl_module.h"
}
+/*
+ * 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()