aboutsummaryrefslogtreecommitdiffstats
path: root/res/res_smdi.c
diff options
context:
space:
mode:
Diffstat (limited to 'res/res_smdi.c')
-rw-r--r--res/res_smdi.c1134
1 files changed, 844 insertions, 290 deletions
diff --git a/res/res_smdi.c b/res/res_smdi.c
index 1df720e83..ec0b2c4f9 100644
--- a/res/res_smdi.c
+++ b/res/res_smdi.c
@@ -1,9 +1,10 @@
/*
* Asterisk -- A telephony toolkit for Linux.
*
- * Copyright (C) 2005-2006, Digium, Inc.
+ * Copyright (C) 2005-2008, Digium, Inc.
*
* Matthew A. Nicholson <mnicholson@digium.com>
+ * Russell Bryant <russell@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
@@ -20,6 +21,10 @@
* \file
* \brief SMDI support for Asterisk.
* \author Matthew A. Nicholson <mnicholson@digium.com>
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * Here is a useful mailing list post that describes SMDI protocol details:
+ * \ref http://lists.digium.com/pipermail/asterisk-dev/2003-June/000884.html
*/
#include "asterisk.h"
@@ -38,25 +43,120 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/config.h"
#include "asterisk/astobj.h"
#include "asterisk/io.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/channel.h"
/* Message expiry time in milliseconds */
#define SMDI_MSG_EXPIRY_TIME 30000 /* 30 seconds */
static const char config_file[] = "smdi.conf";
-static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *msg);
-static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *msg);
-
-static void *smdi_read(void *iface_p);
-static int smdi_load(int reload);
-
-struct module_symbols *me; /* initialized in load_module() */
+/*! \brief SMDI message desk message queue. */
+struct ast_smdi_md_queue {
+ ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message);
+};
+
+/*! \brief SMDI message waiting indicator message queue. */
+struct ast_smdi_mwi_queue {
+ ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message);
+};
+
+struct ast_smdi_interface {
+ ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1);
+ struct ast_smdi_md_queue md_q;
+ ast_mutex_t md_q_lock;
+ ast_cond_t md_q_cond;
+ struct ast_smdi_mwi_queue mwi_q;
+ ast_mutex_t mwi_q_lock;
+ ast_cond_t mwi_q_cond;
+ FILE *file;
+ int fd;
+ pthread_t thread;
+ struct termios mode;
+ int msdstrip;
+ long msg_expiry;
+};
/*! \brief SMDI interface container. */
struct ast_smdi_interface_container {
ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
} smdi_ifaces;
+/*! \brief A mapping between an SMDI mailbox ID and an Asterisk mailbox */
+struct mailbox_mapping {
+ /*! This is the current state of the mailbox. It is simply on or
+ * off to indicate if there are messages waiting or not. */
+ unsigned int cur_state:1;
+ /*! A Pointer to the appropriate SMDI interface */
+ struct ast_smdi_interface *iface;
+ AST_DECLARE_STRING_FIELDS(
+ /*! The Name of the mailbox for the SMDI link. */
+ AST_STRING_FIELD(smdi);
+ /*! The name of the mailbox on the Asterisk side */
+ AST_STRING_FIELD(mailbox);
+ /*! The name of the voicemail context in use */
+ AST_STRING_FIELD(context);
+ );
+ AST_LIST_ENTRY(mailbox_mapping) entry;
+};
+
+/*! 10 seconds */
+#define DEFAULT_POLLING_INTERVAL 10
+
+/*! \brief Data that gets used by the SMDI MWI monitoring thread */
+static struct {
+ /*! The thread ID */
+ pthread_t thread;
+ ast_mutex_t lock;
+ ast_cond_t cond;
+ /*! A list of mailboxes that need to be monitored */
+ AST_LIST_HEAD_NOLOCK(, mailbox_mapping) mailbox_mappings;
+ /*! Polling Interval for checking mailbox status */
+ unsigned int polling_interval;
+ /*! Set to 1 to tell the polling thread to stop */
+ unsigned int stop:1;
+ /*! The time that the last poll began */
+ struct timeval last_poll;
+} mwi_monitor = {
+ .thread = AST_PTHREADT_NULL,
+};
+
+static void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
+{
+ if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
+ pthread_cancel(iface->thread);
+ pthread_join(iface->thread, NULL);
+ }
+
+ iface->thread = AST_PTHREADT_STOP;
+
+ if (iface->file)
+ fclose(iface->file);
+
+ ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
+ ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
+ ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
+ ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
+
+ ast_mutex_destroy(&iface->md_q_lock);
+ ast_cond_destroy(&iface->md_q_cond);
+
+ ast_mutex_destroy(&iface->mwi_q_lock);
+ ast_cond_destroy(&iface->mwi_q_cond);
+
+ free(iface);
+
+ ast_module_unref(ast_module_info->self);
+}
+
+void ast_smdi_interface_unref(struct ast_smdi_interface *iface)
+{
+ ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+}
+
/*!
* \internal
* \brief Push an SMDI message to the back of an interface's message queue.
@@ -65,7 +165,10 @@ struct ast_smdi_interface_container {
*/
static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
{
+ ast_mutex_lock(&iface->md_q_lock);
ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
+ ast_cond_broadcast(&iface->md_q_cond);
+ ast_mutex_unlock(&iface->md_q_lock);
}
/*!
@@ -76,15 +179,13 @@ static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct as
*/
static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
{
+ ast_mutex_lock(&iface->mwi_q_lock);
ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
+ ast_cond_broadcast(&iface->mwi_q_cond);
+ ast_mutex_unlock(&iface->mwi_q_lock);
}
-/*!
- * \brief Set the MWI indicator for a mailbox.
- * \param iface the interface to use.
- * \param mailbox the mailbox to use.
- */
-int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
+static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on)
{
FILE *file;
int i;
@@ -96,224 +197,263 @@ int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
ASTOBJ_WRLOCK(iface);
- fprintf(file, "OP:MWI ");
+ fprintf(file, "%s:MWI ", on ? "OP" : "RMV");
for (i = 0; i < iface->msdstrip; i++)
fprintf(file, "0");
fprintf(file, "%s!\x04", mailbox);
+
fclose(file);
ASTOBJ_UNLOCK(iface);
ast_debug(1, "Sent MWI set message for %s on %s\n", mailbox, iface->name);
+
return 0;
}
-/*!
- * \brief Unset the MWI indicator for a mailbox.
- * \param iface the interface to use.
- * \param mailbox the mailbox to use.
- */
-int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
+int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
{
- FILE *file;
- int i;
-
- if (!(file = fopen(iface->name, "w"))) {
- ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
- return 1;
- }
-
- ASTOBJ_WRLOCK(iface);
-
- fprintf(file, "RMV:MWI ");
-
- for (i = 0; i < iface->msdstrip; i++)
- fprintf(file, "0");
-
- fprintf(file, "%s!\x04", mailbox);
- fclose(file);
+ return smdi_toggle_mwi(iface, mailbox, 1);
+}
- ASTOBJ_UNLOCK(iface);
- ast_debug(1, "Sent MWI unset message for %s on %s\n", mailbox, iface->name);
- return 0;
+int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
+{
+ return smdi_toggle_mwi(iface, mailbox, 0);
}
-/*!
- * \brief Put an SMDI message back in the front of the queue.
- * \param iface a pointer to the interface to use.
- * \param md_msg a pointer to the message to use.
- *
- * This function puts a message back in the front of the specified queue. It
- * should be used if a message was popped but is not going to be processed for
- * some reason, and the message needs to be returned to the queue.
- */
void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
{
+ ast_mutex_lock(&iface->md_q_lock);
ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
+ ast_cond_broadcast(&iface->md_q_cond);
+ ast_mutex_unlock(&iface->md_q_lock);
}
-/*!
- * \brief Put an SMDI message back in the front of the queue.
- * \param iface a pointer to the interface to use.
- * \param mwi_msg a pointer to the message to use.
- *
- * This function puts a message back in the front of the specified queue. It
- * should be used if a message was popped but is not going to be processed for
- * some reason, and the message needs to be returned to the queue.
- */
void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
{
+ ast_mutex_lock(&iface->mwi_q_lock);
ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
+ ast_cond_broadcast(&iface->mwi_q_cond);
+ ast_mutex_unlock(&iface->mwi_q_lock);
}
-/*!
- * \brief Get the next SMDI message from the queue.
- * \param iface a pointer to the interface to use.
- *
- * This function pulls the first unexpired message from the SMDI message queue
- * on the specified interface. It will purge all expired SMDI messages before
- * returning.
- *
- * \return the next SMDI message, or NULL if there were no pending messages.
- */
-struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
-{
- struct ast_smdi_md_message *md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
- struct timeval now;
- long elapsed = 0;
+enum smdi_message_type {
+ SMDI_MWI,
+ SMDI_MD,
+};
- /* purge old messages */
- now = ast_tvnow();
- while (md_msg) {
- elapsed = ast_tvdiff_ms(now, md_msg->timestamp);
+static inline int lock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
+ switch (type) {
+ case SMDI_MWI:
+ return ast_mutex_lock(&iface->mwi_q_lock);
+ case SMDI_MD:
+ return ast_mutex_lock(&iface->md_q_lock);
+ }
+
+ return -1;
+}
- if (elapsed > iface->msg_expiry) {
- /* found an expired message */
- ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
- ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MD message queue. Message was %ld milliseconds too old.\n",
- iface->name, elapsed - iface->msg_expiry);
- md_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
- }
- else {
- /* good message, return it */
- break;
- }
+static inline int unlock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
+ switch (type) {
+ case SMDI_MWI:
+ return ast_mutex_unlock(&iface->mwi_q_lock);
+ case SMDI_MD:
+ return ast_mutex_unlock(&iface->md_q_lock);
}
- return md_msg;
+ return -1;
}
-/*!
- * \brief Get the next SMDI message from the queue.
- * \param iface a pointer to the interface to use.
- * \param timeout the time to wait before returning in milliseconds.
- *
- * This function pulls a message from the SMDI message queue on the specified
- * interface. If no message is available this function will wait the specified
- * amount of time before returning.
- *
- * \return the next SMDI message, or NULL if there were no pending messages and
- * the timeout has expired.
- */
-extern struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
+static inline void *unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
{
- struct timeval start = ast_tvnow();
- long diff = 0;
- struct ast_smdi_md_message *msg;
+ switch (type) {
+ case SMDI_MWI:
+ return ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
+ case SMDI_MD:
+ return ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
+ }
+ return NULL;
+}
- while (diff < timeout) {
+static inline struct timeval msg_timestamp(void *msg, enum smdi_message_type type)
+{
+ struct ast_smdi_md_message *md_msg = msg;
+ struct ast_smdi_mwi_message *mwi_msg = msg;
+
+ switch (type) {
+ case SMDI_MWI:
+ return mwi_msg->timestamp;
+ case SMDI_MD:
+ return md_msg->timestamp;
+ }
- if ((msg = ast_smdi_md_message_pop(iface)))
- return msg;
+ return ast_tv(0, 0);
+}
- /* check timeout */
- diff = ast_tvdiff_ms(ast_tvnow(), start);
+static inline void unref_msg(void *msg, enum smdi_message_type type)
+{
+ struct ast_smdi_md_message *md_msg = msg;
+ struct ast_smdi_mwi_message *mwi_msg = msg;
+
+ switch (type) {
+ case SMDI_MWI:
+ ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
+ case SMDI_MD:
+ ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
}
-
- return (ast_smdi_md_message_pop(iface));
}
-/*!
- * \brief Get the next SMDI message from the queue.
- * \param iface a pointer to the interface to use.
- *
- * This function pulls the first unexpired message from the SMDI message queue
- * on the specified interface. It will purge all expired SMDI messages before
- * returning.
- *
- * \return the next SMDI message, or NULL if there were no pending messages.
- */
-extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
+static void purge_old_messages(struct ast_smdi_interface *iface, enum smdi_message_type type)
{
- struct ast_smdi_mwi_message *mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
struct timeval now = ast_tvnow();
long elapsed = 0;
+ void *msg;
+
+ lock_msg_q(iface, type);
+ msg = unlink_from_msg_q(iface, type);
+ unlock_msg_q(iface, type);
/* purge old messages */
- while (mwi_msg) {
- elapsed = ast_tvdiff_ms(now, mwi_msg->timestamp);
+ while (msg) {
+ elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type));
if (elapsed > iface->msg_expiry) {
/* found an expired message */
- ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
- ast_log(LOG_NOTICE, "Purged expired message from %s SMDI MWI message queue. Message was %ld milliseconds too old.\n",
- iface->name, elapsed - iface->msg_expiry);
- mwi_msg = ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
- }
- else {
- /* good message, return it */
+ unref_msg(msg, type);
+ ast_log(LOG_NOTICE, "Purged expired message from %s SMDI %s message queue. "
+ "Message was %ld milliseconds too old.\n",
+ iface->name, (type == SMDI_MD) ? "MD" : "MWI",
+ elapsed - iface->msg_expiry);
+
+ lock_msg_q(iface, type);
+ msg = unlink_from_msg_q(iface, type);
+ unlock_msg_q(iface, type);
+ } else {
+ /* good message, put it back and return */
+ switch (type) {
+ case SMDI_MD:
+ ast_smdi_md_message_push(iface, msg);
+ break;
+ case SMDI_MWI:
+ ast_smdi_mwi_message_push(iface, msg);
+ break;
+ }
+ unref_msg(msg, type);
break;
}
}
+}
+
+static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type)
+{
+ void *msg;
+
+ purge_old_messages(iface, type);
+
+ lock_msg_q(iface, type);
+ msg = unlink_from_msg_q(iface, type);
+ unlock_msg_q(iface, type);
- return mwi_msg;
+ return msg;
}
-/*!
- * \brief Get the next SMDI message from the queue.
- * \param iface a pointer to the interface to use.
- * \param timeout the time to wait before returning in milliseconds.
- *
- * This function pulls a message from the SMDI message queue on the specified
- * interface. If no message is available this function will wait the specified
- * amount of time before returning.
- *
- * \return the next SMDI message, or NULL if there were no pending messages and
- * the timeout has expired.
- */
-extern struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
+static void *smdi_msg_find(struct ast_smdi_interface *iface,
+ enum smdi_message_type type, const char *station)
{
- struct timeval start = ast_tvnow();
+ void *msg = NULL;
+
+ purge_old_messages(iface, type);
+
+ switch (type) {
+ case SMDI_MD:
+ msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, station);
+ break;
+ case SMDI_MWI:
+ msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, station);
+ break;
+ }
+
+ return msg;
+}
+
+static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout,
+ enum smdi_message_type type, const char *station)
+{
+ struct timeval start;
long diff = 0;
- struct ast_smdi_mwi_message *msg;
+ void *msg;
while (diff < timeout) {
+ struct timespec ts = { 0, };
+ struct timeval tv;
+
+ lock_msg_q(iface, type);
- if ((msg = ast_smdi_mwi_message_pop(iface)))
+ if ((msg = smdi_msg_find(iface, type, station))) {
+ unlock_msg_q(iface, type);
return msg;
+ }
+
+ tv = ast_tvadd(start, ast_tv(0, timeout));
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+
+ /* If there were no messages in the queue, then go to sleep until one
+ * arrives. */
+
+ ast_cond_timedwait(&iface->md_q_cond, &iface->md_q_lock, &ts);
+
+ if ((msg = smdi_msg_find(iface, type, station))) {
+ unlock_msg_q(iface, type);
+ return msg;
+ }
+
+ unlock_msg_q(iface, type);
/* check timeout */
diff = ast_tvdiff_ms(ast_tvnow(), start);
}
- return (ast_smdi_mwi_message_pop(iface));
+ return NULL;
+}
+
+struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
+{
+ return smdi_msg_pop(iface, SMDI_MD);
}
-/*!
- * \brief Find an SMDI interface with the specified name.
- * \param iface_name the name/port of the interface to search for.
- *
- * \return a pointer to the interface located or NULL if none was found. This
- * actually returns an ASTOBJ reference and should be released using
- * #ASTOBJ_UNREF(iface, ast_smdi_interface_destroy).
- */
-extern struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
+struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
+{
+ return smdi_message_wait(iface, timeout, SMDI_MD, NULL);
+}
+
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
+{
+ return smdi_msg_pop(iface, SMDI_MWI);
+}
+
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
+{
+ return smdi_message_wait(iface, timeout, SMDI_MWI, NULL);
+}
+
+struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout,
+ const char *station)
+{
+ return smdi_message_wait(iface, timeout, SMDI_MWI, station);
+}
+
+struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
{
return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
}
-/*! \brief Read an SMDI message.
+/*!
+ * \internal
+ * \brief Read an SMDI message.
*
* \param iface_p the SMDI interface to read from.
*
@@ -335,122 +475,166 @@ static void *smdi_read(void *iface_p)
/* check if this is the start of a message */
if (!start) {
- if (c == 'M')
+ if (c == 'M') {
+ ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n");
start = 1;
+ }
+ continue;
}
- else { /* Determine if this is a MD or MWI message */
- if(c == 'D') { /* MD message */
- start = 0;
+
+ if (c == 'D') { /* MD message */
+ start = 0;
+
+ ast_log(LOG_DEBUG, "Read a 'D' ... it's an MD message.\n");
+
+ if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
+ ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+ return NULL;
+ }
+
+ ASTOBJ_INIT(md_msg);
+
+ /* read the message desk number */
+ for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
+ md_msg->mesg_desk_num[i] = fgetc(iface->file);
+ ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_num[i]);
+ }
+
+ md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
+
+ ast_log(LOG_DEBUG, "The message desk number is '%s'\n", md_msg->mesg_desk_num);
- if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
- ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
- return NULL;
+ /* read the message desk terminal number */
+ for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) {
+ md_msg->mesg_desk_term[i] = fgetc(iface->file);
+ ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_term[i]);
+ }
+
+ md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';
+
+ ast_log(LOG_DEBUG, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term);
+
+ /* read the message type */
+ md_msg->type = fgetc(iface->file);
+
+ ast_log(LOG_DEBUG, "Message type is '%c'\n", md_msg->type);
+
+ /* read the forwarding station number (may be blank) */
+ cp = &md_msg->fwd_st[0];
+ for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
+ if ((c = fgetc(iface->file)) == ' ') {
+ *cp = '\0';
+ ast_log(LOG_DEBUG, "Read a space, done looking for the forwarding station\n");
+ break;
}
-
- ASTOBJ_INIT(md_msg);
-
- /* read the message desk number */
- for(i = 0; i < SMDI_MESG_DESK_NUM_LEN; i++)
- md_msg->mesg_desk_num[i] = fgetc(iface->file);
-
- md_msg->mesg_desk_num[SMDI_MESG_DESK_NUM_LEN] = '\0';
-
- /* read the message desk terminal number */
- for(i = 0; i < SMDI_MESG_DESK_TERM_LEN; i++)
- md_msg->mesg_desk_term[i] = fgetc(iface->file);
-
- md_msg->mesg_desk_term[SMDI_MESG_DESK_TERM_LEN] = '\0';
-
- /* read the message type */
- md_msg->type = fgetc(iface->file);
-
- /* read the forwarding station number (may be blank) */
- cp = &md_msg->fwd_st[0];
- for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
- if((c = fgetc(iface->file)) == ' ') {
- *cp = '\0';
- break;
- }
- /* store c in md_msg->fwd_st */
- if( i >= iface->msdstrip)
- *cp++ = c;
+ /* store c in md_msg->fwd_st */
+ if (i >= iface->msdstrip) {
+ ast_log(LOG_DEBUG, "Read a '%c' and stored it in the forwarding station buffer\n", c);
+ *cp++ = c;
+ } else {
+ ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the fwd station buffer, because of the msdstrip setting (%d < %d)\n", c, i, iface->msdstrip);
}
+ }
- /* make sure the value is null terminated, even if this truncates it */
- md_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
- cp = NULL;
-
- /* read the calling station number (may be blank) */
- cp = &md_msg->calling_st[0];
- for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
- if (!isdigit((c = fgetc(iface->file)))) {
- *cp = '\0';
- break;
+ /* make sure the value is null terminated, even if this truncates it */
+ md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
+ cp = NULL;
+
+ ast_log(LOG_DEBUG, "The forwarding station is '%s'\n", md_msg->fwd_st);
+
+ /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
+ * up a message on this field */
+ ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));
+
+ /* read the calling station number (may be blank) */
+ cp = &md_msg->calling_st[0];
+ for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
+ if (!isdigit((c = fgetc(iface->file)))) {
+ *cp = '\0';
+ ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the calling station buffer because it's not a digit\n", c);
+ if (c == ' ') {
+ /* Don't break on a space. We may read the space before the calling station
+ * here if the forwarding station buffer filled up. */
+ i--; /* We're still on the same character */
+ continue;
}
+ break;
+ }
- /* store c in md_msg->calling_st */
- if (i >= iface->msdstrip)
- *cp++ = c;
+ /* store c in md_msg->calling_st */
+ if (i >= iface->msdstrip) {
+ ast_log(LOG_DEBUG, "Read a '%c' and stored it in the calling station buffer\n", c);
+ *cp++ = c;
+ } else {
+ ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the calling station buffer, because of the msdstrip setting (%d < %d)\n", c, i, iface->msdstrip);
}
+ }
+
+ /* make sure the value is null terminated, even if this truncates it */
+ md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
+ cp = NULL;
+
+ ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st);
- /* make sure the value is null terminated, even if this truncates it */
- md_msg->calling_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
- cp = NULL;
+ /* add the message to the message queue */
+ md_msg->timestamp = ast_tvnow();
+ ast_smdi_md_message_push(iface, md_msg);
+ ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name);
+
+ ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
- /* add the message to the message queue */
- md_msg->timestamp = ast_tvnow();
- ast_smdi_md_message_push(iface, md_msg);
- ast_debug(1, "Recieved SMDI MD message on %s\n", iface->name);
-
- ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
+ } else if (c == 'W') { /* MWI message */
+ start = 0;
- } else if(c == 'W') { /* MWI message */
- start = 0;
+ ast_log(LOG_DEBUG, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
- if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
- ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
- return NULL;
- }
+ if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
+ ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
+ return NULL;
+ }
- ASTOBJ_INIT(mwi_msg);
-
- /* discard the 'I' (from 'MWI') */
- fgetc(iface->file);
-
- /* read the forwarding station number (may be blank) */
- cp = &mwi_msg->fwd_st[0];
- for (i = 0; i < SMDI_MAX_STATION_NUM_LEN + 1; i++) {
- if ((c = fgetc(iface->file)) == ' ') {
- *cp = '\0';
- break;
- }
+ ASTOBJ_INIT(mwi_msg);
- /* store c in md_msg->fwd_st */
- if (i >= iface->msdstrip)
- *cp++ = c;
+ /* discard the 'I' (from 'MWI') */
+ fgetc(iface->file);
+
+ /* read the forwarding station number (may be blank) */
+ cp = &mwi_msg->fwd_st[0];
+ for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) {
+ if ((c = fgetc(iface->file)) == ' ') {
+ *cp = '\0';
+ break;
}
- /* make sure the station number is null terminated, even if this will truncate it */
- mwi_msg->fwd_st[SMDI_MAX_STATION_NUM_LEN] = '\0';
- cp = NULL;
-
- /* read the mwi failure cause */
- for (i = 0; i < SMDI_MWI_FAIL_CAUSE_LEN; i++)
- mwi_msg->cause[i] = fgetc(iface->file);
-
- mwi_msg->cause[SMDI_MWI_FAIL_CAUSE_LEN] = '\0';
-
- /* add the message to the message queue */
- mwi_msg->timestamp = ast_tvnow();
- ast_smdi_mwi_message_push(iface, mwi_msg);
- ast_debug(1, "Recieved SMDI MWI message on %s\n", iface->name);
-
- ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
- } else {
- ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
- start = 0;
+ /* store c in md_msg->fwd_st */
+ if (i >= iface->msdstrip)
+ *cp++ = c;
}
+
+ /* make sure the station number is null terminated, even if this will truncate it */
+ mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
+ cp = NULL;
+
+ /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
+ * up a message on this field */
+ ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));
+
+ /* read the mwi failure cause */
+ for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++)
+ mwi_msg->cause[i] = fgetc(iface->file);
+
+ mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';
+
+ /* add the message to the message queue */
+ mwi_msg->timestamp = ast_tvnow();
+ ast_smdi_mwi_message_push(iface, mwi_msg);
+ ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name);
+
+ ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
+ } else {
+ ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
+ start = 0;
}
}
@@ -459,38 +643,130 @@ static void *smdi_read(void *iface_p)
return NULL;
}
-/*! \brief ast_smdi_md_message destructor. */
void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
{
ast_free(msg);
}
-/*! \brief ast_smdi_mwi_message destructor. */
void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
{
ast_free(msg);
}
-/*! \brief ast_smdi_interface destructor. */
-void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
+static void destroy_mailbox_mapping(struct mailbox_mapping *mm)
{
- if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
- pthread_cancel(iface->thread);
- pthread_join(iface->thread, NULL);
- }
-
- iface->thread = AST_PTHREADT_STOP;
-
- if(iface->file)
- fclose(iface->file);
+ ast_string_field_free_memory(mm);
+ ASTOBJ_UNREF(mm->iface, ast_smdi_interface_destroy);
+ free(mm);
+}
+
+static void destroy_all_mailbox_mappings(void)
+{
+ struct mailbox_mapping *mm;
+
+ ast_mutex_lock(&mwi_monitor.lock);
+ while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry)))
+ destroy_mailbox_mapping(mm);
+ ast_mutex_unlock(&mwi_monitor.lock);
+}
+
+static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface)
+{
+ struct mailbox_mapping *mm;
+ char *mailbox, *context;
+
+ if (!(mm = ast_calloc(1, sizeof(*mm))))
+ return;
- ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
- ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
- ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
- ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
- ast_free(iface);
+ if (ast_string_field_init(mm, 32)) {
+ free(mm);
+ return;
+ }
- ast_module_unref(ast_module_info->self);
+ ast_string_field_set(mm, smdi, var->name);
+
+ context = ast_strdupa(var->value);
+ mailbox = strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+
+ ast_string_field_set(mm, mailbox, mailbox);
+ ast_string_field_set(mm, context, context);
+
+ mm->iface = ASTOBJ_REF(iface);
+
+ ast_mutex_lock(&mwi_monitor.lock);
+ AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry);
+ ast_mutex_unlock(&mwi_monitor.lock);
+}
+
+/*!
+ * \note Called with the mwi_monitor.lock locked
+ */
+static void poll_mailbox(struct mailbox_mapping *mm)
+{
+ char buf[1024];
+ unsigned int state;
+
+ snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context);
+
+ state = !!ast_app_has_voicemail(mm->mailbox, NULL);
+
+ if (state != mm->cur_state) {
+ if (state)
+ ast_smdi_mwi_set(mm->iface, mm->smdi);
+ else
+ ast_smdi_mwi_unset(mm->iface, mm->smdi);
+
+ mm->cur_state = state;
+ }
+}
+
+static void *mwi_monitor_handler(void *data)
+{
+ while (!mwi_monitor.stop) {
+ struct timespec ts = { 0, };
+ struct timeval tv;
+ struct mailbox_mapping *mm;
+
+ ast_mutex_lock(&mwi_monitor.lock);
+
+ mwi_monitor.last_poll = ast_tvnow();
+
+ AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry)
+ poll_mailbox(mm);
+
+ /* Sleep up to the configured polling interval. Allow unload_module()
+ * to signal us to wake up and exit. */
+ tv = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0));
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+ ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts);
+
+ ast_mutex_unlock(&mwi_monitor.lock);
+ }
+
+ return NULL;
+}
+
+static struct ast_smdi_interface *alloc_smdi_interface(void)
+{
+ struct ast_smdi_interface *iface;
+
+ if (!(iface = ast_calloc(1, sizeof(*iface))))
+ return NULL;
+
+ ASTOBJ_INIT(iface);
+ ASTOBJ_CONTAINER_INIT(&iface->md_q);
+ ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
+
+ ast_mutex_init(&iface->md_q_lock);
+ ast_cond_init(&iface->md_q_cond, NULL);
+
+ ast_mutex_init(&iface->mwi_q_lock);
+ ast_cond_init(&iface->mwi_q_cond, NULL);
+
+ return iface;
}
/*!
@@ -541,11 +817,11 @@ static int smdi_load(int reload)
if (!strcasecmp(v->name, "baudrate")) {
if (!strcasecmp(v->value, "9600"))
baud_rate = B9600;
- else if(!strcasecmp(v->value, "4800"))
+ else if (!strcasecmp(v->value, "4800"))
baud_rate = B4800;
- else if(!strcasecmp(v->value, "2400"))
+ else if (!strcasecmp(v->value, "2400"))
baud_rate = B2400;
- else if(!strcasecmp(v->value, "1200"))
+ else if (!strcasecmp(v->value, "1200"))
baud_rate = B1200;
else {
ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
@@ -605,14 +881,10 @@ static int smdi_load(int reload)
continue;
}
}
-
- if (!(iface = ast_calloc(1, sizeof(*iface))))
+
+ if (!(iface = alloc_smdi_interface()))
continue;
- ASTOBJ_INIT(iface);
- ASTOBJ_CONTAINER_INIT(&iface->md_q);
- ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
-
ast_copy_string(iface->name, v->value, sizeof(iface->name));
iface->thread = AST_PTHREADT_NULL;
@@ -666,7 +938,7 @@ static int smdi_load(int reload)
/* set the message expiry time */
iface->msg_expiry = msg_expiry;
- /* start the listner thread */
+ /* start the listener thread */
ast_verb(3, "Starting SMDI monitor thread for %s\n", iface->name);
if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) {
ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
@@ -681,8 +953,46 @@ static int smdi_load(int reload)
ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
}
}
+
+ destroy_all_mailbox_mappings();
+ mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
+
+ iface = NULL;
+
+ for (v = ast_variable_browse(conf, "mailboxes"); v; v = v->next) {
+ if (!strcasecmp(v->name, "smdiport")) {
+ if (iface)
+ ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+
+ if (!(iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
+ ast_log(LOG_NOTICE, "SMDI interface %s not found\n", iface->name);
+ continue;
+ }
+ } else if (!strcasecmp(v->name, "pollinginterval")) {
+ if (sscanf(v->value, "%u", &mwi_monitor.polling_interval) != 1) {
+ ast_log(LOG_ERROR, "Invalid value for pollinginterval: %s\n", v->value);
+ mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
+ }
+ } else {
+ if (!iface) {
+ ast_log(LOG_ERROR, "Mailbox mapping ignored, no valid SMDI interface specified in mailboxes section\n");
+ continue;
+ }
+ append_mailbox_mapping(v, iface);
+ }
+ }
+
+ if (iface)
+ ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+
ast_config_destroy(conf);
+ if (!AST_LIST_EMPTY(&mwi_monitor.mailbox_mappings) && mwi_monitor.thread == AST_PTHREADT_NULL
+ && ast_pthread_create_background(&mwi_monitor.thread, NULL, mwi_monitor_handler, NULL)) {
+ ast_log(LOG_ERROR, "Failed to start MWI monitoring thread. This module will not operate.\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
/* Prune any interfaces we should no longer monitor. */
if (reload)
ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
@@ -696,6 +1006,231 @@ static int smdi_load(int reload)
return res;
}
+struct smdi_msg_datastore {
+ unsigned int id;
+ struct ast_smdi_interface *iface;
+ struct ast_smdi_md_message *md_msg;
+};
+
+static void smdi_msg_datastore_destroy(void *data)
+{
+ struct smdi_msg_datastore *smd = data;
+
+ if (smd->iface)
+ ASTOBJ_UNREF(smd->iface, ast_smdi_interface_destroy);
+
+ if (smd->md_msg)
+ ASTOBJ_UNREF(smd->md_msg, ast_smdi_md_message_destroy);
+
+ free(smd);
+}
+
+static const struct ast_datastore_info smdi_msg_datastore_info = {
+ .type = "SMDIMSG",
+ .destroy = smdi_msg_datastore_destroy,
+};
+
+static int smdi_msg_id;
+
+/*! In milliseconds */
+#define SMDI_RETRIEVE_TIMEOUT_DEFAULT 3000
+
+static int smdi_msg_retrieve_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_module_user *u;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(port);
+ AST_APP_ARG(station);
+ AST_APP_ARG(timeout);
+ );
+ unsigned int timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
+ int res = -1;
+ char *parse = NULL;
+ struct smdi_msg_datastore *smd = NULL;
+ struct ast_datastore *datastore = NULL;
+ struct ast_smdi_interface *iface = NULL;
+ struct ast_smdi_md_message *md_msg = NULL;
+
+ u = ast_module_user_add(chan);
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE requires an argument\n");
+ goto return_error;
+ }
+
+ if (!chan) {
+ ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE must be used with a channel\n");
+ goto return_error;
+ }
+
+ ast_autoservice_start(chan);
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.port) || ast_strlen_zero(args.station)) {
+ ast_log(LOG_ERROR, "Invalid arguments provided to SMDI_MSG_RETRIEVE\n");
+ goto return_error;
+ }
+
+ if (!(iface = ast_smdi_interface_find(args.port))) {
+ ast_log(LOG_ERROR, "SMDI port '%s' not found\n", args.port);
+ goto return_error;
+ }
+
+ if (!ast_strlen_zero(args.timeout)) {
+ if (sscanf(args.timeout, "%u", &timeout) != 1) {
+ ast_log(LOG_ERROR, "'%s' is not a valid timeout\n", args.timeout);
+ timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
+ }
+ }
+
+ if (!(md_msg = smdi_message_wait(iface, timeout, SMDI_MD, args.station))) {
+ ast_log(LOG_WARNING, "No SMDI message retrieved for station '%s' after "
+ "waiting %u ms.\n", args.station, timeout);
+ goto return_error;
+ }
+
+ if (!(smd = ast_calloc(1, sizeof(*smd))))
+ goto return_error;
+
+ smd->iface = ASTOBJ_REF(iface);
+ smd->md_msg = ASTOBJ_REF(md_msg);
+ smd->id = ast_atomic_fetchadd_int((int *) &smdi_msg_id, 1);
+ snprintf(buf, len, "%u", smd->id);
+
+ if (!(datastore = ast_channel_datastore_alloc(&smdi_msg_datastore_info, buf)))
+ goto return_error;
+
+ datastore->data = smd;
+
+ ast_channel_lock(chan);
+ ast_channel_datastore_add(chan, datastore);
+ ast_channel_unlock(chan);
+
+ res = 0;
+
+return_error:
+ if (iface)
+ ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
+
+ if (md_msg)
+ ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
+
+ if (smd && !datastore)
+ smdi_msg_datastore_destroy(smd);
+
+ if (parse)
+ ast_autoservice_stop(chan);
+
+ ast_module_user_remove(u);
+
+ return res;
+}
+
+static int smdi_msg_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+ struct ast_module_user *u;
+ int res = -1;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(id);
+ AST_APP_ARG(component);
+ );
+ char *parse;
+ struct ast_datastore *datastore = NULL;
+ struct smdi_msg_datastore *smd = NULL;
+
+ u = ast_module_user_add(chan);
+
+ if (!chan) {
+ ast_log(LOG_ERROR, "SMDI_MSG can not be called without a channel\n");
+ goto return_error;
+ }
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SMDI_MSG requires an argument\n");
+ goto return_error;
+ }
+
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.id)) {
+ ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
+ goto return_error;
+ }
+
+ if (ast_strlen_zero(args.component)) {
+ ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
+ goto return_error;
+ }
+
+ ast_channel_lock(chan);
+ datastore = ast_channel_datastore_find(chan, &smdi_msg_datastore_info, args.id);
+ ast_channel_unlock(chan);
+
+ if (!datastore) {
+ ast_log(LOG_WARNING, "No SMDI message found for message ID '%s'\n", args.id);
+ goto return_error;
+ }
+
+ smd = datastore->data;
+
+ if (!strcasecmp(args.component, "station")) {
+ ast_copy_string(buf, smd->md_msg->fwd_st, len);
+ } else if (!strcasecmp(args.component, "callerid")) {
+ ast_copy_string(buf, smd->md_msg->calling_st, len);
+ } else if (!strcasecmp(args.component, "type")) {
+ snprintf(buf, len, "%c", smd->md_msg->type);
+ } else {
+ ast_log(LOG_ERROR, "'%s' is not a valid message component for SMDI_MSG\n",
+ args.component);
+ goto return_error;
+ }
+
+ res = 0;
+
+return_error:
+ ast_module_user_remove(u);
+
+ return 0;
+}
+
+static struct ast_custom_function smdi_msg_retrieve_function = {
+ .name = "SMDI_MSG_RETRIEVE",
+ .synopsis = "Retrieve an SMDI message.",
+ .syntax = "SMDI_MSG_RETRIEVE(<smdi port>,<station>[,timeout])",
+ .desc =
+ " This function is used to retrieve an incoming SMDI message. It returns\n"
+ "an ID which can be used with the SMDI_MSG() function to access details of\n"
+ "the message. Note that this is a destructive function in the sense that\n"
+ "once an SMDI message is retrieved using this function, it is no longer in\n"
+ "the global SMDI message queue, and can not be accessed by any other Asterisk\n"
+ "channels. The timeout for this function is optional, and the default is\n"
+ "3 seconds. When providing a timeout, it should be in milliseconds.\n"
+ "",
+ .read = smdi_msg_retrieve_read,
+};
+
+static struct ast_custom_function smdi_msg_function = {
+ .name = "SMDI_MSG",
+ .synopsis = "Retrieve details about an SMDI message.",
+ .syntax = "SMDI_MSG(<message_id>,<component>)",
+ .desc =
+ " This function is used to access details of an SMDI message that was\n"
+ "pulled from the incoming SMDI message queue using the SMDI_MSG_RETRIEVE()\n"
+ "function.\n"
+ " Valid message components are:\n"
+ " station - The forwarding station\n"
+ " callerid - The callerID of the calling party that was forwarded\n"
+ " type - The call type. The value here is the exact character\n"
+ " that came in on the SMDI link. Typically, example values\n"
+ " are: D - Direct Calls, A - Forward All Calls,\n"
+ " B - Forward Busy Calls, N - Forward No Answer Calls\n"
+ "",
+ .read = smdi_msg_read,
+};
+
static int load_module(void)
{
int res;
@@ -704,6 +1239,12 @@ static int load_module(void)
memset(&smdi_ifaces, 0, sizeof(smdi_ifaces));
ASTOBJ_CONTAINER_INIT(&smdi_ifaces);
+ ast_mutex_init(&mwi_monitor.lock);
+ ast_cond_init(&mwi_monitor.cond, NULL);
+
+ ast_custom_function_register(&smdi_msg_retrieve_function);
+ ast_custom_function_register(&smdi_msg_function);
+
/* load the config and start the listener threads*/
res = smdi_load(0);
if (res < 0) {
@@ -711,8 +1252,9 @@ static int load_module(void)
} else if (res == 1) {
ast_log(LOG_NOTICE, "No SMDI interfaces are available to listen on, not starting SMDI listener.\n");
return AST_MODULE_LOAD_DECLINE;
- } else
- return AST_MODULE_LOAD_SUCCESS;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
@@ -721,6 +1263,18 @@ static int unload_module(void)
ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy);
ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
+ destroy_all_mailbox_mappings();
+
+ ast_mutex_lock(&mwi_monitor.lock);
+ mwi_monitor.stop = 1;
+ ast_cond_signal(&mwi_monitor.cond);
+ ast_mutex_unlock(&mwi_monitor.lock);
+
+ pthread_join(mwi_monitor.thread, NULL);
+
+ ast_custom_function_unregister(&smdi_msg_retrieve_function);
+ ast_custom_function_unregister(&smdi_msg_function);
+
return 0;
}