/*
- * \file extnotify_main.c
- * @author Mathew McBride
+ * extnotify_main.c
+ * Mathew McBride
*
* This module implements an external pager hook for when notifcation
* of a new email is wanted.
+ *
* Based on bits of serv_funambol
* Contact: <matt@mcbridematt.dhs.org> / <matt@comalies>
+ *
+ * Copyright (c) 2008-2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdep.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 "ctdl_module.h"
+
+
+void ExtNotify_PutErrorMessage(NotifyContext *Ctx, StrBuf *ErrMsg)
+{
+ int nNext;
+ if (Ctx->NotifyErrors == NULL)
+ Ctx->NotifyErrors = NewHash(1, Flathash);
+
+ nNext = GetCount(Ctx->NotifyErrors) + 1;
+ Put(Ctx->NotifyErrors,
+ (char*)&nNext,
+ sizeof(int),
+ ErrMsg,
+ HFreeStrBuf);
+}
+
+
+
+
+StrBuf* GetNHBuf(int i, int allocit, StrBuf **NotifyHostList)
+{
+ if ((NotifyHostList[i] == NULL) && (allocit != 0))
+ NotifyHostList[i] = NewStrBuf();
+ return NotifyHostList[i];
+}
+
+
+StrBuf** GetNotifyHosts(void)
+{
+ char NotifyHostsBuf[SIZ];
+ StrBuf *Host;
+ StrBuf *File;
+ StrBuf *NotifyBuf;
+ int notify;
+ const char *pchs, *pche;
+ const char *NextHost = NULL;
+ StrBuf **NotifyHostList;
+ int num_notify;
+
+ /* See if we have any Notification Hosts configured */
+ num_notify = get_hosts(NotifyHostsBuf, "notify");
+ if (num_notify < 1)
+ return(NULL);
+
+ NotifyHostList = malloc(sizeof(StrBuf*) * 2 * (num_notify + 1));
+ memset(NotifyHostList, 0, sizeof(StrBuf*) * 2 * (num_notify + 1));
+
+ NotifyBuf = NewStrBufPlain(NotifyHostsBuf, -1);
+ /* get all configured notifiers's */
+ for (notify=0; notify<num_notify; notify++) {
+
+ Host = GetNHBuf(notify * 2, 1, NotifyHostList);
+ StrBufExtract_NextToken(Host, NotifyBuf, &NextHost, '|');
+ pchs = ChrPtr(Host);
+ pche = strchr(pchs, ':');
+ if (pche == NULL) {
+ CtdlLogPrintf(CTDL_ERR,
+ __FILE__": filename not found in %s.\n",
+ pchs);
+ continue;
+ }
+ File = GetNHBuf(notify * 2 + 1, 1, NotifyHostList);
+ StrBufPlain(File, pchs, pche - pchs);
+ StrBufCutLeft(Host, pche - pchs + 1);
+ }
+ return NotifyHostList;
+}
+
+
/*! \brief Create the notify message queue. We use the exact same room
* as the Funambol module.
*
* and sets as system room.
*/
void create_extnotify_queue(void) {
- struct ctdlroom qrbuf;
+ struct ctdlroom qrbuf;
- create_room(FNBL_QUEUE_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
+ CtdlCreateRoom(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);
- }
+ /*
+ * 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 (CtdlGetRoomLock(&qrbuf, FNBL_QUEUE_ROOM) == 0) {
+ qrbuf.QRflags2 |= QR2_SYSTEM;
+ CtdlPutRoomLock(&qrbuf);
+ }
}
/*!
* \brief Run through the pager room queue
*/
-void do_extnotify_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;
+void do_extnotify_queue(void)
+{
+ NotifyContext Ctx;
+ static int doing_queue = 0;
+ int i = 0;
- /*
- * Go ahead and run the queue
- */
- CtdlLogPrintf(CTDL_DEBUG, "serv_extnotify: processing notify queue\n");
+ /*
+ * 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;
- if (getroom(&CC->room, FNBL_QUEUE_ROOM) != 0) {
- CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", FNBL_QUEUE_ROOM);
- return;
- }
- CtdlForEachMessage(MSGS_ALL, 0L, NULL,
- SPOOLMIME, NULL, process_notify, NULL);
+ /*
+ * Go ahead and run the queue
+ */
+ CtdlLogPrintf(CTDL_DEBUG, "serv_extnotify: processing notify queue\n");
- CtdlLogPrintf(CTDL_DEBUG, "serv_extnotify: queue run completed\n");
- doing_queue = 0;
+ memset(&Ctx, 0, sizeof(NotifyContext));
+ Ctx.NotifyHostList = GetNotifyHosts();
+ if (CtdlGetRoom(&CC->room, FNBL_QUEUE_ROOM) != 0) {
+ CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", FNBL_QUEUE_ROOM);
+ return;
+ }
+ CtdlForEachMessage(MSGS_ALL, 0L, NULL,
+ SPOOLMIME, NULL, process_notify, &Ctx);
+
+ while ((Ctx.NotifyHostList != NULL) && (Ctx.NotifyHostList[i] != NULL))
+ FreeStrBuf(&Ctx.NotifyHostList[i]);
+
+ if (Ctx.NotifyErrors != NULL)
+ {
+ long len;
+ const char *Key;
+ HashPos *It;
+ void *vErr;
+ StrBuf *ErrMsg;
+
+ It = GetNewHashPos(Ctx.NotifyErrors, 0);
+ while (GetNextHashPos(Ctx.NotifyErrors, It, &len, &Key, &vErr) &&
+ (vErr != NULL)) {
+ ErrMsg = (StrBuf*) vErr;
+ quickie_message("Citadel", NULL, NULL, AIDEROOM, ChrPtr(ErrMsg), FMT_FIXED,
+ "Failed to notify external service about inbound mail");
+ }
+
+ DeleteHashPos(&It);
+ DeleteHash(&Ctx.NotifyErrors);
+ }
+
+ CtdlLogPrintf(CTDL_DEBUG, "serv_extnotify: queue run completed\n");
+ doing_queue = 0;
}
/*!
* \brief Process messages in the external notification queue
*/
-void process_notify(long msgnum, void *usrdata) {
- struct CtdlMessage *msg;
- msg = CtdlFetchMessage(msgnum, 1);
- if ( msg->cm_fields['W'] == NULL) {
- goto nuke;
- }
-
- long configMsgNum = extNotify_getConfigMessage(msg->cm_fields['W']);
- char configMsg[SIZ];
+void process_notify(long NotifyMsgnum, void *usrdata)
+{
+ NotifyContext *Ctx;
+ long msgnum = 0;
+ long todelete[1];
+ int fnblAllowed;
+ int extPagerAllowedHttp;
+ int extPagerAllowedSystem;
+ char *pch;
+ long configMsgNum;
+ char configMsg[SIZ];
+ struct CtdlMessage *msg;
+
+ Ctx = (NotifyContext*) usrdata;
+
+ msg = CtdlFetchMessage(NotifyMsgnum, 1);
+ if ( msg->cm_fields['W'] == NULL) {
+ goto nuke;
+ }
- extNotify_getPrefs(configMsgNum, &configMsg[0]);
+ configMsgNum = extNotify_getConfigMessage(msg->cm_fields['W']);
- /* Check to see if:
- * 1. The user has configured paging / They have and disabled it
- * AND 2. There is an external pager program
- * 3. A Funambol server has been entered
- *
- */
- if ((configMsgNum == -1) ||
- ((strncasecmp(configMsg, "none", 4) == 0) &&
- IsEmptyStr(config.c_pager_program) &&
- IsEmptyStr(config.c_funambol_host))) {
- CtdlLogPrintf(CTDL_DEBUG, "No external notifiers configured on system/user");
- goto nuke;
- }
- // Can Funambol take the message?
- int fnblAllowed = strncasecmp(configMsg, FUNAMBOL_CONFIG_TEXT, strlen(FUNAMBOL_CONFIG_TEXT));
- int extPagerAllowed = strncasecmp(configMsg, PAGER_CONFIG_TEXT, strlen(PAGER_CONFIG_TEXT));
- if (fnblAllowed == 0) {
- notify_funambol_server(msg->cm_fields['W'],
- msg->cm_fields['I']);
- } else if (extPagerAllowed == 0) {
- char *number = strtok(configMsg, "textmessage\n");
- int commandSiz = sizeof(config.c_pager_program) + strlen(number) + strlen(msg->cm_fields['W']) + 5;
- char *command = malloc(commandSiz);
- snprintf(command, commandSiz, "%s %s -u %s", config.c_pager_program, number, msg->cm_fields['W']);
- system(command);
- free(command);
- }
+ extNotify_getPrefs(configMsgNum, &configMsg[0]);
+
+ /* Check to see if:
+ * 1. The user has configured paging / They have and disabled it
+ * AND 2. There is an external pager program
+ * 3. A Funambol server has been entered
+ *
+ */
+ if ((configMsgNum == -1) ||
+ ((strncasecmp(configMsg, "none", 4) == 0) &&
+ IsEmptyStr(config.c_pager_program) &&
+ IsEmptyStr(config.c_funambol_host))) {
+ CtdlLogPrintf(CTDL_DEBUG, "No external notifiers configured on system/user");
+ goto nuke;
+ }
+
+ // Can Funambol take the message?
+ pch = strchr(configMsg, '\n');
+ if (*pch == '\n')
+ *pch = '\0';
+ fnblAllowed = strncasecmp(configMsg, HKEY(FUNAMBOL_CONFIG_TEXT));
+ extPagerAllowedHttp = strncasecmp(configMsg, HKEY(PAGER_CONFIG_HTTP));
+ extPagerAllowedSystem = strncasecmp(configMsg, HKEY(PAGER_CONFIG_SYSTEM));
+ pch = strstr(msg->cm_fields['M'], "msgid|");
+ if (pch != NULL)
+ msgnum = atol(pch + sizeof("msgid"));
+ if (fnblAllowed == 0) {
+ char remoteurl[SIZ];
+ snprintf(remoteurl, SIZ, "http://%s@%s:%d/%s",
+ config.c_funambol_auth,
+ config.c_funambol_host,
+ config.c_funambol_port,
+ FUNAMBOL_WS);
+ notify_http_server(remoteurl,
+ file_funambol_msg,
+ strlen(file_funambol_msg),/*GNA*/
+ msg->cm_fields['W'],
+ msg->cm_fields['I'],
+ msgnum,
+ Ctx);
+ } else if (extPagerAllowedHttp == 0) {
+ int i = 0;
+ StrBuf *URL;
+ char URLBuf[SIZ];
+ StrBuf *File;
+ StrBuf *FileBuf = NewStrBuf();
+
+ while(1)
+ {
+
+ URL = GetNHBuf(i*2, 0, Ctx->NotifyHostList);
+ if (URL==NULL) break;
+ File = GetNHBuf(i*2 + 1, 0, Ctx->NotifyHostList);
+ if (File==NULL) break;
+
+ if (StrLength(File)>0)
+ StrBufPrintf(FileBuf, "%s/%s",
+ ctdl_shared_dir,
+ ChrPtr(File));
+ else
+ FlushStrBuf(FileBuf);
+ memcpy(URLBuf, ChrPtr(URL), StrLength(URL) + 1);
+
+ notify_http_server(URLBuf,
+ ChrPtr(FileBuf),
+ StrLength(FileBuf),
+ msg->cm_fields['W'],
+ msg->cm_fields['I'],
+ msgnum,
+ Ctx);
+ i++;
+ }
+ FreeStrBuf(&FileBuf);
+ }
+ else if (extPagerAllowedSystem == 0) {
+ char *number;
+ int commandSiz;
+ char *command;
+
+ number = strtok(configMsg, "textmessage\n");
+ commandSiz = sizeof(config.c_pager_program) + strlen(number) + strlen(msg->cm_fields['W']) + 5;
+ command = malloc(commandSiz);
+ snprintf(command, commandSiz, "%s %s -u %s", config.c_pager_program, number, msg->cm_fields['W']);
+ system(command);
+ free(command);
+ }
nuke:
- CtdlFreeMessage(msg);
- memset(configMsg, 0, sizeof(configMsg));
- long todelete[1];
- todelete[0] = msgnum;
- CtdlDeleteMessages(FNBL_QUEUE_ROOM, todelete, 1, "");
+ CtdlFreeMessage(msg);
+ memset(configMsg, 0, sizeof(configMsg));
+ todelete[0] = NotifyMsgnum;
+ CtdlDeleteMessages(FNBL_QUEUE_ROOM, todelete, 1, "");
}
/*! \brief Checks to see what notification option the user has set
*
*/
-void extNotify_getPrefs(long configMsgNum, char *configMsg) {
- // Do a simple string search to see if 'funambol' is selected as the
- // type. This string would be at the very top of the message contents.
- if (configMsgNum == -1) {
- CtdlLogPrintf(CTDL_ERR, "extNotify_isAllowedByPrefs was passed a non-existant config message id\n");
- return;
- }
- struct CtdlMessage *prefMsg;
- prefMsg = CtdlFetchMessage(configMsgNum, 1);
- strncpy(configMsg, prefMsg->cm_fields['M'], strlen(prefMsg->cm_fields['M']));
- CtdlFreeMessage(prefMsg);
+void extNotify_getPrefs(long configMsgNum, char *configMsg)
+{
+ struct CtdlMessage *prefMsg;
+ // Do a simple string search to see if 'funambol' is selected as the
+ // type. This string would be at the very top of the message contents.
+ if (configMsgNum == -1) {
+ CtdlLogPrintf(CTDL_ERR, "extNotify_isAllowedByPrefs was passed a non-existant config message id\n");
+ return;
+ }
+ prefMsg = CtdlFetchMessage(configMsgNum, 1);
+ strncpy(configMsg, prefMsg->cm_fields['M'], strlen(prefMsg->cm_fields['M']));
+ CtdlFreeMessage(prefMsg);
}
+
/*! \brief Get configuration message for pager/funambol system from the
* users "My Citadel Config" room
*/
long extNotify_getConfigMessage(char *username) {
- struct ctdlroom qrbuf; // scratch for room
- struct ctdluser user; // ctdl user instance
- char configRoomName[ROOMNAMELEN];
- struct CtdlMessage *msg;
- struct cdbdata *cdbfr;
- long *msglist = NULL;
- int num_msgs = 0;
- long confMsgNum = -1;
- // Get the user
- getuser(&user, username);
+ struct ctdlroom qrbuf; // scratch for room
+ struct ctdluser user; // ctdl user instance
+ char configRoomName[ROOMNAMELEN];
+ struct CtdlMessage *msg;
+ struct cdbdata *cdbfr;
+ long *msglist = NULL;
+ int num_msgs = 0;
+ long confMsgNum = -1;
+ int a;
+
+ // Get the user
+ getuser(&user, username);
- MailboxName(configRoomName, sizeof configRoomName, &user, USERCONFIGROOM);
- // Fill qrbuf
- 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 {
- CtdlLogPrintf(CTDL_DEBUG, "extNotify_getConfigMessage: No config messages found\n");
- 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;
+ MailboxName(configRoomName, sizeof configRoomName, &user, USERCONFIGROOM);
+ // Fill qrbuf
+ CtdlGetRoom(&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 {
+ CtdlLogPrintf(CTDL_DEBUG, "extNotify_getConfigMessage: No config messages found\n");
+ return -1; /* No messages at all? No further action. */
+ }
+ 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;
}
+
CTDL_MODULE_INIT(extnotify)
{
if (!threading)