Tue Apr 28 22:50:11 2009

Asterisk developer's documentation


res_smdi.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Copyright (C) 2005-2008, Digium, Inc.
00005  *
00006  * Matthew A. Nicholson <mnicholson@digium.com>
00007  * Russell Bryant <russell@digium.com>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*!
00021  * \file
00022  * \brief SMDI support for Asterisk.
00023  * \author Matthew A. Nicholson <mnicholson@digium.com>
00024  * \author Russell Bryant <russell@digium.com>
00025  *
00026  * Here is a useful mailing list post that describes SMDI protocol details:
00027  * \ref http://lists.digium.com/pipermail/asterisk-dev/2003-June/000884.html
00028  */
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 146799 $")
00033 
00034 #include <stdio.h>
00035 #include <stdlib.h>
00036 #include <errno.h>
00037 #include <termios.h>
00038 #include <sys/time.h>
00039 #include <time.h>
00040 #include <ctype.h>
00041 
00042 #include "asterisk/module.h"
00043 #include "asterisk/lock.h"
00044 #include "asterisk/utils.h"
00045 #include "asterisk/smdi.h"
00046 #include "asterisk/config.h"
00047 #include "asterisk/astobj.h"
00048 #include "asterisk/io.h"
00049 #include "asterisk/logger.h"
00050 #include "asterisk/utils.h"
00051 #include "asterisk/options.h"
00052 #include "asterisk/stringfields.h"
00053 #include "asterisk/linkedlists.h"
00054 #include "asterisk/app.h"
00055 #include "asterisk/pbx.h"
00056 
00057 /* Message expiry time in milliseconds */
00058 #define SMDI_MSG_EXPIRY_TIME  30000 /* 30 seconds */
00059 
00060 static const char config_file[] = "smdi.conf";
00061 
00062 /*! \brief SMDI message desk message queue. */
00063 struct ast_smdi_md_queue {
00064    ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message);
00065 };
00066 
00067 /*! \brief SMDI message waiting indicator message queue. */
00068 struct ast_smdi_mwi_queue {
00069    ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message);
00070 };
00071 
00072 struct ast_smdi_interface {
00073    ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1);
00074    struct ast_smdi_md_queue md_q;
00075    ast_mutex_t md_q_lock;
00076    ast_cond_t md_q_cond;
00077    struct ast_smdi_mwi_queue mwi_q;
00078    ast_mutex_t mwi_q_lock;
00079    ast_cond_t mwi_q_cond;
00080    FILE *file;
00081    int fd;
00082    pthread_t thread;
00083    struct termios mode;
00084    int msdstrip;
00085    long msg_expiry;
00086 };
00087 
00088 /*! \brief SMDI interface container. */
00089 struct ast_smdi_interface_container {
00090    ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
00091 } smdi_ifaces;
00092 
00093 /*! \brief A mapping between an SMDI mailbox ID and an Asterisk mailbox */
00094 struct mailbox_mapping {
00095    /*! This is the current state of the mailbox.  It is simply on or
00096     *  off to indicate if there are messages waiting or not. */
00097    unsigned int cur_state:1;
00098    /*! A Pointer to the appropriate SMDI interface */
00099    struct ast_smdi_interface *iface;
00100    AST_DECLARE_STRING_FIELDS(
00101       /*! The Name of the mailbox for the SMDI link. */
00102       AST_STRING_FIELD(smdi);
00103       /*! The name of the mailbox on the Asterisk side */
00104       AST_STRING_FIELD(mailbox);
00105       /*! The name of the voicemail context in use */
00106       AST_STRING_FIELD(context);
00107    );
00108    AST_LIST_ENTRY(mailbox_mapping) entry;
00109 };
00110 
00111 /*! 10 seconds */
00112 #define DEFAULT_POLLING_INTERVAL 10
00113 
00114 /*! \brief Data that gets used by the SMDI MWI monitoring thread */
00115 static struct {
00116    /*! The thread ID */
00117    pthread_t thread;
00118    ast_mutex_t lock;
00119    ast_cond_t cond;
00120    /*! A list of mailboxes that need to be monitored */
00121    AST_LIST_HEAD_NOLOCK(, mailbox_mapping) mailbox_mappings;
00122    /*! Polling Interval for checking mailbox status */
00123    unsigned int polling_interval;
00124    /*! Set to 1 to tell the polling thread to stop */
00125    unsigned int stop:1;
00126    /*! The time that the last poll began */
00127    struct timeval last_poll;
00128 } mwi_monitor = {
00129    .thread = AST_PTHREADT_NULL,
00130 };
00131 
00132 static void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
00133 {
00134    if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
00135       pthread_cancel(iface->thread);
00136       pthread_join(iface->thread, NULL);
00137    }
00138    
00139    iface->thread = AST_PTHREADT_STOP;
00140    
00141    if (iface->file) 
00142       fclose(iface->file);
00143    
00144    ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
00145    ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
00146    ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
00147    ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
00148 
00149    ast_mutex_destroy(&iface->md_q_lock);
00150    ast_cond_destroy(&iface->md_q_cond);
00151 
00152    ast_mutex_destroy(&iface->mwi_q_lock);
00153    ast_cond_destroy(&iface->mwi_q_cond);
00154 
00155    free(iface);
00156 
00157    ast_module_unref(ast_module_info->self);
00158 }
00159 
00160 void ast_smdi_interface_unref(struct ast_smdi_interface *iface)
00161 {
00162    ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00163 }
00164 
00165 /*! 
00166  * \internal
00167  * \brief Push an SMDI message to the back of an interface's message queue.
00168  * \param iface a pointer to the interface to use.
00169  * \param md_msg a pointer to the message to use.
00170  */
00171 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
00172 {
00173    ast_mutex_lock(&iface->md_q_lock);
00174    ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
00175    ast_cond_broadcast(&iface->md_q_cond);
00176    ast_mutex_unlock(&iface->md_q_lock);
00177 }
00178 
00179 /*!
00180  * \internal
00181  * \brief Push an SMDI message to the back of an interface's message queue.
00182  * \param iface a pointer to the interface to use.
00183  * \param mwi_msg a pointer to the message to use.
00184  */
00185 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
00186 {
00187    ast_mutex_lock(&iface->mwi_q_lock);
00188    ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
00189    ast_cond_broadcast(&iface->mwi_q_cond);
00190    ast_mutex_unlock(&iface->mwi_q_lock);
00191 }
00192 
00193 static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on)
00194 {
00195    FILE *file;
00196    int i;
00197    
00198    if (!(file = fopen(iface->name, "w"))) {
00199       ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
00200       return 1;
00201    }  
00202 
00203    ASTOBJ_WRLOCK(iface);
00204 
00205    fprintf(file, "%s:MWI ", on ? "OP" : "RMV");
00206 
00207    for (i = 0; i < iface->msdstrip; i++)
00208       fprintf(file, "0");
00209 
00210    fprintf(file, "%s!\x04", mailbox);
00211 
00212    fclose(file);
00213 
00214    ASTOBJ_UNLOCK(iface);
00215 
00216    ast_log(LOG_DEBUG, "Sent MWI %s message for %s on %s\n", on ? "set" : "unset", 
00217       mailbox, iface->name);
00218 
00219    return 0;
00220 }
00221 
00222 int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
00223 {
00224    return smdi_toggle_mwi(iface, mailbox, 1);
00225 }
00226 
00227 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
00228 {
00229    return smdi_toggle_mwi(iface, mailbox, 0);
00230 }
00231 
00232 void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
00233 {
00234    ast_mutex_lock(&iface->md_q_lock);
00235    ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
00236    ast_cond_broadcast(&iface->md_q_cond);
00237    ast_mutex_unlock(&iface->md_q_lock);
00238 }
00239 
00240 void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
00241 {
00242    ast_mutex_lock(&iface->mwi_q_lock);
00243    ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
00244    ast_cond_broadcast(&iface->mwi_q_cond);
00245    ast_mutex_unlock(&iface->mwi_q_lock);
00246 }
00247 
00248 enum smdi_message_type {
00249    SMDI_MWI,
00250    SMDI_MD,
00251 };
00252 
00253 static inline int lock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
00254 {
00255    switch (type) {
00256    case SMDI_MWI:
00257       return ast_mutex_lock(&iface->mwi_q_lock);
00258    case SMDI_MD:  
00259       return ast_mutex_lock(&iface->md_q_lock);
00260    }
00261    
00262    return -1;
00263 }
00264 
00265 static inline int unlock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
00266 {
00267    switch (type) {
00268    case SMDI_MWI:
00269       return ast_mutex_unlock(&iface->mwi_q_lock);
00270    case SMDI_MD:
00271       return ast_mutex_unlock(&iface->md_q_lock);
00272    }
00273 
00274    return -1;
00275 }
00276 
00277 static inline void *unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
00278 {
00279    switch (type) {
00280    case SMDI_MWI:
00281       return ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
00282    case SMDI_MD:
00283       return ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
00284    }
00285 
00286    return NULL;
00287 }
00288 
00289 static inline struct timeval msg_timestamp(void *msg, enum smdi_message_type type)
00290 {
00291    struct ast_smdi_md_message *md_msg = msg;
00292    struct ast_smdi_mwi_message *mwi_msg = msg;
00293 
00294    switch (type) {
00295    case SMDI_MWI:
00296       return mwi_msg->timestamp;
00297    case SMDI_MD:
00298       return md_msg->timestamp;
00299    }
00300 
00301    return ast_tv(0, 0);
00302 }
00303 
00304 static inline void unref_msg(void *msg, enum smdi_message_type type)
00305 {
00306    struct ast_smdi_md_message *md_msg = msg;
00307    struct ast_smdi_mwi_message *mwi_msg = msg;
00308 
00309    switch (type) {
00310    case SMDI_MWI:
00311       ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
00312    case SMDI_MD:
00313       ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
00314    }
00315 }
00316 
00317 static void purge_old_messages(struct ast_smdi_interface *iface, enum smdi_message_type type)
00318 {
00319    struct timeval now;
00320    long elapsed = 0;
00321    void *msg;
00322    
00323    lock_msg_q(iface, type);
00324    msg = unlink_from_msg_q(iface, type);
00325    unlock_msg_q(iface, type);
00326 
00327    /* purge old messages */
00328    now = ast_tvnow();
00329    while (msg) {
00330       elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type));
00331 
00332       if (elapsed > iface->msg_expiry) {
00333          /* found an expired message */
00334          unref_msg(msg, type);
00335          ast_log(LOG_NOTICE, "Purged expired message from %s SMDI %s message queue.  "
00336             "Message was %ld milliseconds too old.\n",
00337             iface->name, (type == SMDI_MD) ? "MD" : "MWI", 
00338             elapsed - iface->msg_expiry);
00339 
00340          lock_msg_q(iface, type);
00341          msg = unlink_from_msg_q(iface, type);
00342          unlock_msg_q(iface, type);
00343       } else {
00344          /* good message, put it back and return */
00345          switch (type) {
00346          case SMDI_MD:
00347             ast_smdi_md_message_push(iface, msg);
00348             break;
00349          case SMDI_MWI:
00350             ast_smdi_mwi_message_push(iface, msg);
00351             break;
00352          }
00353          unref_msg(msg, type);
00354          break;
00355       }
00356    }
00357 }
00358 
00359 static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type)
00360 {
00361    void *msg;
00362 
00363    purge_old_messages(iface, type);
00364 
00365    lock_msg_q(iface, type);
00366    msg = unlink_from_msg_q(iface, type);
00367    unlock_msg_q(iface, type);
00368 
00369    return msg;
00370 }
00371 
00372 enum {
00373    OPT_SEARCH_TERMINAL = (1 << 0),
00374    OPT_SEARCH_NUMBER   = (1 << 1),
00375 };
00376 
00377 static void *smdi_msg_find(struct ast_smdi_interface *iface,
00378    enum smdi_message_type type, const char *search_key, struct ast_flags options)
00379 {
00380    void *msg = NULL;
00381 
00382    purge_old_messages(iface, type);
00383 
00384    switch (type) {
00385    case SMDI_MD:
00386       if (ast_test_flag(&options, OPT_SEARCH_TERMINAL)) {
00387          struct ast_smdi_md_message *md_msg = NULL;
00388 
00389          /* Searching by the message desk terminal */
00390 
00391          ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
00392             if (!strcasecmp(iterator->mesg_desk_term, search_key))
00393                md_msg = ASTOBJ_REF(iterator);
00394          } while (0); );
00395 
00396          msg = md_msg;
00397       } else if (ast_test_flag(&options, OPT_SEARCH_NUMBER)) {
00398          struct ast_smdi_md_message *md_msg = NULL;
00399 
00400          /* Searching by the message desk number */
00401 
00402          ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
00403             if (!strcasecmp(iterator->mesg_desk_num, search_key))
00404                md_msg = ASTOBJ_REF(iterator);
00405          } while (0); );
00406 
00407          msg = md_msg;
00408       } else {
00409          /* Searching by the forwarding station */
00410          msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, search_key);
00411       }
00412       break;
00413    case SMDI_MWI:
00414       msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, search_key);
00415       break;
00416    }
00417 
00418    return msg;
00419 }
00420 
00421 static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout, 
00422    enum smdi_message_type type, const char *search_key, struct ast_flags options)
00423 {
00424    struct timeval start;
00425    long diff = 0;
00426    void *msg;
00427    ast_cond_t *cond = NULL;
00428    ast_mutex_t *lock = NULL;
00429 
00430    switch (type) {
00431    case SMDI_MWI:
00432       cond = &iface->mwi_q_cond;
00433       lock = &iface->mwi_q_lock;
00434       break;
00435    case SMDI_MD:
00436       cond = &iface->md_q_cond;
00437       lock = &iface->md_q_lock;
00438       break;
00439    }
00440 
00441    start = ast_tvnow();
00442    while (diff < timeout) {
00443       struct timespec ts = { 0, };
00444       struct timeval tv;
00445 
00446       lock_msg_q(iface, type);
00447 
00448       if ((msg = smdi_msg_find(iface, type, search_key, options))) {
00449          unlock_msg_q(iface, type);
00450          return msg;
00451       }
00452 
00453       tv = ast_tvadd(start, ast_tv(0, timeout));
00454       ts.tv_sec = tv.tv_sec;
00455       ts.tv_nsec = tv.tv_usec * 1000;
00456 
00457       /* If there were no messages in the queue, then go to sleep until one
00458        * arrives. */
00459 
00460       ast_cond_timedwait(cond, lock, &ts);
00461 
00462       if ((msg = smdi_msg_find(iface, type, search_key, options))) {
00463          unlock_msg_q(iface, type);
00464          return msg;
00465       }
00466 
00467       unlock_msg_q(iface, type);
00468 
00469       /* check timeout */
00470       diff = ast_tvdiff_ms(ast_tvnow(), start);
00471    }
00472 
00473    return NULL;
00474 }
00475 
00476 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
00477 {
00478    return smdi_msg_pop(iface, SMDI_MD);
00479 }
00480 
00481 struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
00482 {
00483    struct ast_flags options = { 0 };
00484    return smdi_message_wait(iface, timeout, SMDI_MD, NULL, options);
00485 }
00486 
00487 struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
00488 {
00489    return smdi_msg_pop(iface, SMDI_MWI);
00490 }
00491 
00492 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
00493 {
00494    struct ast_flags options = { 0 };
00495    return smdi_message_wait(iface, timeout, SMDI_MWI, NULL, options);
00496 }
00497 
00498 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout,
00499    const char *station)
00500 {
00501    struct ast_flags options = { 0 };
00502    return smdi_message_wait(iface, timeout, SMDI_MWI, station, options);
00503 }
00504 
00505 struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
00506 {
00507    return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
00508 }
00509 
00510 /*! 
00511  * \internal
00512  * \brief Read an SMDI message.
00513  *
00514  * \param iface_p the SMDI interface to read from.
00515  *
00516  * This function loops and reads from and SMDI interface.  It must be stopped
00517  * using pthread_cancel().
00518  */
00519 static void *smdi_read(void *iface_p)
00520 {
00521    struct ast_smdi_interface *iface = iface_p;
00522    struct ast_smdi_md_message *md_msg;
00523    struct ast_smdi_mwi_message *mwi_msg;
00524    char c = '\0';
00525    char *cp = NULL;
00526    int i;
00527    int start = 0;
00528       
00529    /* read an smdi message */
00530    while ((c = fgetc(iface->file))) {
00531 
00532       /* check if this is the start of a message */
00533       if (!start) {
00534          if (c == 'M') {
00535             ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n");
00536             start = 1;
00537          }
00538          continue;
00539       }
00540       
00541       if (c == 'D') { /* MD message */
00542          start = 0;
00543 
00544          ast_log(LOG_DEBUG, "Read a 'D' ... it's an MD message.\n");
00545 
00546          if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
00547             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00548             return NULL;
00549          }
00550          
00551          ASTOBJ_INIT(md_msg);
00552 
00553          /* read the message desk number */
00554          for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
00555             md_msg->mesg_desk_num[i] = fgetc(iface->file);
00556             ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_num[i]);
00557          }
00558 
00559          md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
00560          
00561          ast_log(LOG_DEBUG, "The message desk number is '%s'\n", md_msg->mesg_desk_num);
00562 
00563          /* read the message desk terminal number */
00564          for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) {
00565             md_msg->mesg_desk_term[i] = fgetc(iface->file);
00566             ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_term[i]);
00567          }
00568 
00569          md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';
00570 
00571          ast_log(LOG_DEBUG, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term);
00572 
00573          /* read the message type */
00574          md_msg->type = fgetc(iface->file);
00575        
00576          ast_log(LOG_DEBUG, "Message type is '%c'\n", md_msg->type);
00577 
00578          /* read the forwarding station number (may be blank) */
00579          cp = &md_msg->fwd_st[0];
00580          for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
00581             if ((c = fgetc(iface->file)) == ' ') {
00582                *cp = '\0';
00583                ast_log(LOG_DEBUG, "Read a space, done looking for the forwarding station\n");
00584                break;
00585             }
00586 
00587             /* store c in md_msg->fwd_st */
00588             if (i >= iface->msdstrip) {
00589                ast_log(LOG_DEBUG, "Read a '%c' and stored it in the forwarding station buffer\n", c);
00590                *cp++ = c;
00591             } else {
00592                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);
00593             }
00594          }
00595 
00596          /* make sure the value is null terminated, even if this truncates it */
00597          md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
00598          cp = NULL;
00599 
00600          ast_log(LOG_DEBUG, "The forwarding station is '%s'\n", md_msg->fwd_st);
00601 
00602          /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
00603           * up a message on this field */
00604          ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));
00605 
00606          /* read the calling station number (may be blank) */
00607          cp = &md_msg->calling_st[0];
00608          for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
00609             if (!isdigit((c = fgetc(iface->file)))) {
00610                *cp = '\0';
00611                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);
00612                if (c == ' ') {
00613                   /* Don't break on a space.  We may read the space before the calling station
00614                    * here if the forwarding station buffer filled up. */
00615                   i--; /* We're still on the same character */
00616                   continue;
00617                }
00618                break;
00619             }
00620 
00621             /* store c in md_msg->calling_st */
00622             if (i >= iface->msdstrip) {
00623                ast_log(LOG_DEBUG, "Read a '%c' and stored it in the calling station buffer\n", c);
00624                *cp++ = c;
00625             } else {
00626                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);
00627             }
00628          }
00629 
00630          /* make sure the value is null terminated, even if this truncates it */
00631          md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
00632          cp = NULL;
00633 
00634          ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st);
00635 
00636          /* add the message to the message queue */
00637          md_msg->timestamp = ast_tvnow();
00638          ast_smdi_md_message_push(iface, md_msg);
00639          ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name);
00640          
00641          ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
00642 
00643       } else if (c == 'W') { /* MWI message */
00644          start = 0;
00645 
00646          ast_log(LOG_DEBUG, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
00647 
00648          if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
00649             ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00650             return NULL;
00651          }
00652 
00653          ASTOBJ_INIT(mwi_msg);
00654 
00655          /* discard the 'I' (from 'MWI') */
00656          fgetc(iface->file);
00657          
00658          /* read the forwarding station number (may be blank) */
00659          cp = &mwi_msg->fwd_st[0];
00660          for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) {
00661             if ((c = fgetc(iface->file)) == ' ') {
00662                *cp = '\0';
00663                break;
00664             }
00665 
00666             /* store c in md_msg->fwd_st */
00667             if (i >= iface->msdstrip)
00668                *cp++ = c;
00669          }
00670 
00671          /* make sure the station number is null terminated, even if this will truncate it */
00672          mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
00673          cp = NULL;
00674          
00675          /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
00676           * up a message on this field */
00677          ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));
00678 
00679          /* read the mwi failure cause */
00680          for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++)
00681             mwi_msg->cause[i] = fgetc(iface->file);
00682 
00683          mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';
00684 
00685          /* add the message to the message queue */
00686          mwi_msg->timestamp = ast_tvnow();
00687          ast_smdi_mwi_message_push(iface, mwi_msg);
00688          ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name);
00689          
00690          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
00691       } else {
00692          ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
00693          start = 0;
00694       }
00695    }
00696 
00697    ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
00698    ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00699    return NULL;
00700 }
00701 
00702 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
00703 {
00704    free(msg);
00705 }
00706 
00707 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
00708 {
00709    free(msg);
00710 }
00711 
00712 static void destroy_mailbox_mapping(struct mailbox_mapping *mm)
00713 {
00714    ast_string_field_free_memory(mm);
00715    ASTOBJ_UNREF(mm->iface, ast_smdi_interface_destroy);
00716    free(mm);
00717 }
00718 
00719 static void destroy_all_mailbox_mappings(void)
00720 {
00721    struct mailbox_mapping *mm;
00722 
00723    ast_mutex_lock(&mwi_monitor.lock);
00724    while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry)))
00725       destroy_mailbox_mapping(mm);
00726    ast_mutex_unlock(&mwi_monitor.lock);
00727 }
00728 
00729 static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface)
00730 {
00731    struct mailbox_mapping *mm;
00732    char *mailbox, *context;
00733 
00734    if (!(mm = ast_calloc(1, sizeof(*mm))))
00735       return;
00736    
00737    if (ast_string_field_init(mm, 32)) {
00738       free(mm);
00739       return;
00740    }
00741 
00742    ast_string_field_set(mm, smdi, var->name);
00743 
00744    context = ast_strdupa(var->value);
00745    mailbox = strsep(&context, "@");
00746    if (ast_strlen_zero(context))
00747       context = "default";
00748 
00749    ast_string_field_set(mm, mailbox, mailbox);
00750    ast_string_field_set(mm, context, context);
00751 
00752    mm->iface = ASTOBJ_REF(iface);
00753 
00754    ast_mutex_lock(&mwi_monitor.lock);
00755    AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry);
00756    ast_mutex_unlock(&mwi_monitor.lock);
00757 }
00758 
00759 /*!
00760  * \note Called with the mwi_monitor.lock locked
00761  */
00762 static void poll_mailbox(struct mailbox_mapping *mm)
00763 {
00764    char buf[1024];
00765    unsigned int state;
00766 
00767    snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context);
00768 
00769    state = !!ast_app_has_voicemail(mm->mailbox, NULL);
00770 
00771    if (state != mm->cur_state) {
00772       if (state)
00773          ast_smdi_mwi_set(mm->iface, mm->smdi);
00774       else
00775          ast_smdi_mwi_unset(mm->iface, mm->smdi);
00776 
00777       mm->cur_state = state;
00778    }
00779 }
00780 
00781 static void *mwi_monitor_handler(void *data)
00782 {
00783    while (!mwi_monitor.stop) {
00784       struct timespec ts = { 0, };
00785       struct timeval tv;
00786       struct mailbox_mapping *mm;
00787 
00788       ast_mutex_lock(&mwi_monitor.lock);
00789 
00790       mwi_monitor.last_poll = ast_tvnow();
00791 
00792       AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry)
00793          poll_mailbox(mm);
00794 
00795       /* Sleep up to the configured polling interval.  Allow unload_module()
00796        * to signal us to wake up and exit. */
00797       tv = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0));
00798       ts.tv_sec = tv.tv_sec;
00799       ts.tv_nsec = tv.tv_usec * 1000;
00800       ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts);
00801 
00802       ast_mutex_unlock(&mwi_monitor.lock);
00803    }
00804 
00805    return NULL;
00806 }
00807 
00808 static struct ast_smdi_interface *alloc_smdi_interface(void)
00809 {
00810    struct ast_smdi_interface *iface;
00811 
00812    if (!(iface = ast_calloc(1, sizeof(*iface))))
00813       return NULL;
00814 
00815    ASTOBJ_INIT(iface);
00816    ASTOBJ_CONTAINER_INIT(&iface->md_q);
00817    ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
00818 
00819    ast_mutex_init(&iface->md_q_lock);
00820    ast_cond_init(&iface->md_q_cond, NULL);
00821 
00822    ast_mutex_init(&iface->mwi_q_lock);
00823    ast_cond_init(&iface->mwi_q_cond, NULL);
00824 
00825    return iface;
00826 }
00827 
00828 /*!
00829  * \internal
00830  * \brief Load and reload SMDI configuration.
00831  * \param reload this should be 1 if we are reloading and 0 if not.
00832  *
00833  * This function loads/reloads the SMDI configuration and starts and stops
00834  * interfaces accordingly.
00835  *
00836  * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started.
00837  */
00838 static int smdi_load(int reload)
00839 {
00840    struct ast_config *conf;
00841    struct ast_variable *v;
00842    struct ast_smdi_interface *iface = NULL;
00843    int res = 0;
00844 
00845    /* Config options */
00846    speed_t baud_rate = B9600;     /* 9600 baud rate */
00847    tcflag_t paritybit = PARENB;   /* even parity checking */
00848    tcflag_t charsize = CS7;       /* seven bit characters */
00849    int stopbits = 0;              /* One stop bit */
00850    
00851    int msdstrip = 0;              /* strip zero digits */
00852    long msg_expiry = SMDI_MSG_EXPIRY_TIME;
00853    
00854    conf = ast_config_load(config_file);
00855 
00856    if (!conf) {
00857       if (reload)
00858          ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
00859       else
00860          ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
00861       return 1;
00862    }
00863 
00864    /* Mark all interfaces that we are listening on.  We will unmark them
00865     * as we find them in the config file, this way we know any interfaces
00866     * still marked after we have finished parsing the config file should
00867     * be stopped.
00868     */
00869    if (reload)
00870       ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
00871 
00872    for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
00873       if (!strcasecmp(v->name, "baudrate")) {
00874          if (!strcasecmp(v->value, "9600"))
00875             baud_rate = B9600;
00876          else if (!strcasecmp(v->value, "4800"))
00877             baud_rate = B4800;
00878          else if (!strcasecmp(v->value, "2400"))
00879             baud_rate = B2400;
00880          else if (!strcasecmp(v->value, "1200"))
00881             baud_rate = B1200;
00882          else {
00883             ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
00884             baud_rate = B9600;
00885          }
00886       } else if (!strcasecmp(v->name, "msdstrip")) {
00887          if (!sscanf(v->value, "%d", &msdstrip)) {
00888             ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
00889             msdstrip = 0;
00890          } else if (0 > msdstrip || msdstrip > 9) {
00891             ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
00892             msdstrip = 0;
00893          }
00894       } else if (!strcasecmp(v->name, "msgexpirytime")) {
00895          if (!sscanf(v->value, "%ld", &msg_expiry)) {
00896             ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
00897             msg_expiry = SMDI_MSG_EXPIRY_TIME;
00898          }
00899       } else if (!strcasecmp(v->name, "paritybit")) {
00900          if (!strcasecmp(v->value, "even"))
00901             paritybit = PARENB;
00902          else if (!strcasecmp(v->value, "odd"))
00903             paritybit = PARENB | PARODD;
00904          else if (!strcasecmp(v->value, "none"))
00905             paritybit = ~PARENB;
00906          else {
00907             ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
00908             paritybit = PARENB;
00909          }
00910       } else if (!strcasecmp(v->name, "charsize")) {
00911          if (!strcasecmp(v->value, "7"))
00912             charsize = CS7;
00913          else if (!strcasecmp(v->value, "8"))
00914             charsize = CS8;
00915          else {
00916             ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
00917             charsize = CS7;
00918          }
00919       } else if (!strcasecmp(v->name, "twostopbits")) {
00920          stopbits = ast_true(v->name);
00921       } else if (!strcasecmp(v->name, "smdiport")) {
00922          if (reload) {
00923             /* we are reloading, check if we are already
00924              * monitoring this interface, if we are we do
00925              * not want to start it again.  This also has
00926              * the side effect of not updating different
00927              * setting for the serial port, but it should
00928              * be trivial to rewrite this section so that
00929              * options on the port are changed without
00930              * restarting the interface.  Or the interface
00931              * could be restarted with out emptying the
00932              * queue. */
00933             if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
00934                ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
00935                ASTOBJ_UNMARK(iface);
00936                ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00937                continue;
00938             }
00939          }
00940          
00941          if (!(iface = alloc_smdi_interface()))
00942             continue;
00943 
00944          ast_copy_string(iface->name, v->value, sizeof(iface->name));
00945 
00946          iface->thread = AST_PTHREADT_NULL;
00947 
00948          if (!(iface->file = fopen(iface->name, "r"))) {
00949             ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
00950             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00951             continue;
00952          }
00953 
00954          iface->fd = fileno(iface->file);
00955 
00956          /* Set the proper attributes for our serial port. */
00957 
00958          /* get the current attributes from the port */
00959          if (tcgetattr(iface->fd, &iface->mode)) {
00960             ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
00961             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00962             continue;
00963          }
00964 
00965          /* set the desired speed */
00966          if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
00967             ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
00968             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00969             continue;
00970          }
00971          
00972          /* set the stop bits */
00973          if (stopbits)
00974             iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB;   /* set two stop bits */
00975          else
00976             iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB;  /* set one stop bit */
00977 
00978          /* set the parity */
00979          iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
00980 
00981          /* set the character size */
00982          iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
00983          
00984          /* commit the desired attributes */
00985          if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
00986             ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
00987             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00988             continue;
00989          }
00990 
00991          /* set the msdstrip */
00992          iface->msdstrip = msdstrip;
00993 
00994          /* set the message expiry time */
00995          iface->msg_expiry = msg_expiry;
00996 
00997          /* start the listener thread */
00998          if (option_verbose > 2)
00999             ast_verbose(VERBOSE_PREFIX_3 "Starting SMDI monitor thread for %s\n", iface->name);
01000          if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) {
01001             ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
01002             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01003             continue;
01004          }
01005 
01006          ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
01007          ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01008          ast_module_ref(ast_module_info->self);
01009       } else {
01010          ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
01011       }
01012    }
01013 
01014    destroy_all_mailbox_mappings();
01015    mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
01016    
01017    iface = NULL;
01018 
01019    for (v = ast_variable_browse(conf, "mailboxes"); v; v = v->next) {
01020       if (!strcasecmp(v->name, "smdiport")) {
01021          if (iface)
01022             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01023 
01024          if (!(iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
01025             ast_log(LOG_NOTICE, "SMDI interface %s not found\n", iface->name);
01026             continue;
01027          }
01028       } else if (!strcasecmp(v->name, "pollinginterval")) {
01029          if (sscanf(v->value, "%u", &mwi_monitor.polling_interval) != 1) {
01030             ast_log(LOG_ERROR, "Invalid value for pollinginterval: %s\n", v->value);
01031             mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
01032          }
01033       } else {
01034          if (!iface) {
01035             ast_log(LOG_ERROR, "Mailbox mapping ignored, no valid SMDI interface specified in mailboxes section\n");
01036             continue;
01037          }
01038          append_mailbox_mapping(v, iface);
01039       }
01040    }
01041 
01042    if (iface)
01043       ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01044 
01045    ast_config_destroy(conf);
01046 
01047    if (!AST_LIST_EMPTY(&mwi_monitor.mailbox_mappings) && mwi_monitor.thread == AST_PTHREADT_NULL
01048       && ast_pthread_create_background(&mwi_monitor.thread, NULL, mwi_monitor_handler, NULL)) {
01049       ast_log(LOG_ERROR, "Failed to start MWI monitoring thread.  This module will not operate.\n");
01050       return AST_MODULE_LOAD_FAILURE;
01051    }
01052 
01053    /* Prune any interfaces we should no longer monitor. */
01054    if (reload)
01055       ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
01056    
01057    ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces);
01058    /* TODO: this is bad, we need an ASTOBJ method for this! */
01059    if (!smdi_ifaces.head)
01060       res = 1;
01061    ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
01062          
01063    return res;
01064 }
01065 
01066 struct smdi_msg_datastore {
01067    unsigned int id;
01068    struct ast_smdi_interface *iface;
01069    struct ast_smdi_md_message *md_msg;
01070 };
01071 
01072 static void smdi_msg_datastore_destroy(void *data)
01073 {
01074    struct smdi_msg_datastore *smd = data;
01075 
01076    if (smd->iface)
01077       ASTOBJ_UNREF(smd->iface, ast_smdi_interface_destroy);
01078 
01079    if (smd->md_msg)
01080       ASTOBJ_UNREF(smd->md_msg, ast_smdi_md_message_destroy);
01081 
01082    free(smd);
01083 }
01084 
01085 static const struct ast_datastore_info smdi_msg_datastore_info = {
01086    .type = "SMDIMSG",
01087    .destroy = smdi_msg_datastore_destroy,
01088 };
01089 
01090 static int smdi_msg_id;
01091 
01092 /*! In milliseconds */
01093 #define SMDI_RETRIEVE_TIMEOUT_DEFAULT 3000
01094 
01095 AST_APP_OPTIONS(smdi_msg_ret_options, BEGIN_OPTIONS
01096    AST_APP_OPTION('t', OPT_SEARCH_TERMINAL),
01097    AST_APP_OPTION('n', OPT_SEARCH_NUMBER),
01098 END_OPTIONS );
01099 
01100 static int smdi_msg_retrieve_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
01101 {
01102    struct ast_module_user *u;
01103    AST_DECLARE_APP_ARGS(args,
01104       AST_APP_ARG(port);
01105       AST_APP_ARG(search_key);
01106       AST_APP_ARG(timeout);
01107       AST_APP_ARG(options);
01108    );
01109    struct ast_flags options = { 0 };
01110    unsigned int timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
01111    int res = -1;
01112    char *parse = NULL;
01113    struct smdi_msg_datastore *smd = NULL;
01114    struct ast_datastore *datastore = NULL;
01115    struct ast_smdi_interface *iface = NULL;
01116    struct ast_smdi_md_message *md_msg = NULL;
01117 
01118    u = ast_module_user_add(chan);
01119 
01120    if (ast_strlen_zero(data)) {
01121       ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE requires an argument\n");
01122       goto return_error;
01123    }
01124 
01125    if (!chan) {
01126       ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE must be used with a channel\n");
01127       goto return_error;
01128    }
01129 
01130    ast_autoservice_start(chan);
01131 
01132    parse = ast_strdupa(data);
01133    AST_STANDARD_APP_ARGS(args, parse);
01134 
01135    if (ast_strlen_zero(args.port) || ast_strlen_zero(args.search_key)) {
01136       ast_log(LOG_ERROR, "Invalid arguments provided to SMDI_MSG_RETRIEVE\n");
01137       goto return_error;
01138    }
01139 
01140    if (!(iface = ast_smdi_interface_find(args.port))) {
01141       ast_log(LOG_ERROR, "SMDI port '%s' not found\n", args.port);
01142       goto return_error;
01143    }
01144 
01145    if (!ast_strlen_zero(args.options)) {
01146       ast_app_parse_options(smdi_msg_ret_options, &options, NULL, args.options);
01147    }
01148 
01149    if (!ast_strlen_zero(args.timeout)) {
01150       if (sscanf(args.timeout, "%u", &timeout) != 1) {
01151          ast_log(LOG_ERROR, "'%s' is not a valid timeout\n", args.timeout);
01152          timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
01153       }
01154    }
01155 
01156    if (!(md_msg = smdi_message_wait(iface, timeout, SMDI_MD, args.search_key, options))) {
01157       ast_log(LOG_WARNING, "No SMDI message retrieved for search key '%s' after "
01158          "waiting %u ms.\n", args.search_key, timeout);
01159       goto return_error;
01160    }
01161 
01162    if (!(smd = ast_calloc(1, sizeof(*smd))))
01163       goto return_error;
01164 
01165    smd->iface = ASTOBJ_REF(iface);
01166    smd->md_msg = ASTOBJ_REF(md_msg);
01167    smd->id = ast_atomic_fetchadd_int((int *) &smdi_msg_id, 1);
01168    snprintf(buf, len, "%u", smd->id);
01169 
01170    if (!(datastore = ast_channel_datastore_alloc(&smdi_msg_datastore_info, buf)))
01171       goto return_error;
01172 
01173    datastore->data = smd;
01174 
01175    ast_channel_lock(chan);
01176    ast_channel_datastore_add(chan, datastore);
01177    ast_channel_unlock(chan);
01178 
01179    res = 0;
01180 
01181 return_error:
01182    if (iface)
01183       ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01184 
01185    if (md_msg)
01186       ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
01187 
01188    if (smd && !datastore)
01189       smdi_msg_datastore_destroy(smd);
01190 
01191    if (parse)
01192       ast_autoservice_stop(chan);
01193 
01194    ast_module_user_remove(u);
01195 
01196    return res;
01197 }
01198 
01199 static int smdi_msg_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
01200 {
01201    struct ast_module_user *u;
01202    int res = -1;
01203    AST_DECLARE_APP_ARGS(args,
01204       AST_APP_ARG(id);
01205       AST_APP_ARG(component);
01206    );
01207    char *parse;
01208    struct ast_datastore *datastore = NULL;
01209    struct smdi_msg_datastore *smd = NULL;
01210 
01211    u = ast_module_user_add(chan);
01212 
01213    if (!chan) {
01214       ast_log(LOG_ERROR, "SMDI_MSG can not be called without a channel\n");
01215       goto return_error;
01216    }
01217 
01218    if (ast_strlen_zero(data)) {
01219       ast_log(LOG_WARNING, "SMDI_MSG requires an argument\n");
01220       goto return_error;
01221    }
01222 
01223    parse = ast_strdupa(data);
01224    AST_STANDARD_APP_ARGS(args, parse);
01225 
01226    if (ast_strlen_zero(args.id)) {
01227       ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
01228       goto return_error;
01229    }
01230 
01231    if (ast_strlen_zero(args.component)) {
01232       ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
01233       goto return_error;
01234    }
01235 
01236    ast_channel_lock(chan);
01237    datastore = ast_channel_datastore_find(chan, &smdi_msg_datastore_info, args.id);
01238    ast_channel_unlock(chan);
01239    
01240    if (!datastore) {
01241       ast_log(LOG_WARNING, "No SMDI message found for message ID '%s'\n", args.id);
01242       goto return_error;
01243    }
01244 
01245    smd = datastore->data;
01246 
01247    if (!strcasecmp(args.component, "number")) {
01248       ast_copy_string(buf, smd->md_msg->mesg_desk_num, len);
01249    } else if (!strcasecmp(args.component, "terminal")) {
01250       ast_copy_string(buf, smd->md_msg->mesg_desk_term, len);
01251    } else if (!strcasecmp(args.component, "station")) {
01252       ast_copy_string(buf, smd->md_msg->fwd_st, len);
01253    } else if (!strcasecmp(args.component, "callerid")) {
01254       ast_copy_string(buf, smd->md_msg->calling_st, len);
01255    } else if (!strcasecmp(args.component, "type")) {
01256       snprintf(buf, len, "%c", smd->md_msg->type);
01257    } else {
01258       ast_log(LOG_ERROR, "'%s' is not a valid message component for SMDI_MSG\n",
01259          args.component);
01260       goto return_error;
01261    }
01262 
01263    res = 0;
01264 
01265 return_error:
01266    ast_module_user_remove(u);
01267 
01268    return res;
01269 }
01270 
01271 static struct ast_custom_function smdi_msg_retrieve_function = {
01272    .name = "SMDI_MSG_RETRIEVE",
01273    .synopsis = "Retrieve an SMDI message.",
01274    .syntax = "SMDI_MSG_RETRIEVE(<smdi port>,<search key>[,timeout[,options]])",
01275    .desc = 
01276    "   This function is used to retrieve an incoming SMDI message.  It returns\n"
01277    "an ID which can be used with the SMDI_MSG() function to access details of\n"
01278    "the message.  Note that this is a destructive function in the sense that\n"
01279    "once an SMDI message is retrieved using this function, it is no longer in\n"
01280    "the global SMDI message queue, and can not be accessed by any other Asterisk\n"
01281    "channels.  The timeout for this function is optional, and the default is\n"
01282    "3 seconds.  When providing a timeout, it should be in milliseconds.\n"
01283    "   The default search is done on the forwarding station ID.  However, if\n"
01284    "you set one of the search key options in the options field, you can change\n"
01285    "this behavior.\n"
01286    "   Options:\n"
01287    "     t - Instead of searching on the forwarding station, search on the message\n"
01288    "         desk terminal.\n"
01289    "     n - Instead of searching on the forwarding station, search on the message\n"
01290    "         desk number.\n"
01291    "",
01292    .read = smdi_msg_retrieve_read,
01293 };
01294 
01295 static struct ast_custom_function smdi_msg_function = {
01296    .name = "SMDI_MSG",
01297    .synopsis = "Retrieve details about an SMDI message.",
01298    .syntax = "SMDI_MSG(<message_id>,<component>)",
01299    .desc = 
01300    "   This function is used to access details of an SMDI message that was\n"
01301    "pulled from the incoming SMDI message queue using the SMDI_MSG_RETRIEVE()\n"
01302    "function.\n"
01303    "   Valid message components are:\n"
01304    "      number   - The message desk number\n"
01305    "      terminal - The message desk terminal\n"
01306    "      station  - The forwarding station\n"
01307    "      callerid - The callerID of the calling party that was forwarded\n"
01308    "      type     - The call type.  The value here is the exact character\n"
01309    "                 that came in on the SMDI link.  Typically, example values\n"
01310    "                 are: D - Direct Calls, A - Forward All Calls,\n"
01311    "                      B - Forward Busy Calls, N - Forward No Answer Calls\n"
01312    "",
01313    .read = smdi_msg_read,
01314 };
01315 
01316 static int load_module(void)
01317 {
01318    int res;
01319 
01320    /* initialize our containers */
01321    memset(&smdi_ifaces, 0, sizeof(smdi_ifaces));
01322    ASTOBJ_CONTAINER_INIT(&smdi_ifaces);
01323 
01324    ast_mutex_init(&mwi_monitor.lock);
01325    ast_cond_init(&mwi_monitor.cond, NULL);
01326 
01327    ast_custom_function_register(&smdi_msg_retrieve_function);
01328    ast_custom_function_register(&smdi_msg_function);
01329 
01330    /* load the config and start the listener threads*/
01331    res = smdi_load(0);
01332    if (res < 0) {
01333       return res;
01334    } else if (res == 1) {
01335       ast_log(LOG_WARNING, "No SMDI interfaces are available to listen on, not starting SMDI listener.\n");
01336       return AST_MODULE_LOAD_DECLINE;
01337    }
01338 
01339    return 0;
01340 }
01341 
01342 static int unload_module(void)
01343 {
01344    /* this destructor stops any running smdi_read threads */
01345    ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy);
01346    ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
01347 
01348    destroy_all_mailbox_mappings();
01349 
01350    ast_mutex_lock(&mwi_monitor.lock);
01351    mwi_monitor.stop = 1;
01352    ast_cond_signal(&mwi_monitor.cond);
01353    ast_mutex_unlock(&mwi_monitor.lock);
01354 
01355    if (mwi_monitor.thread != AST_PTHREADT_NULL) {
01356       pthread_join(mwi_monitor.thread, NULL);
01357    }
01358 
01359    ast_custom_function_unregister(&smdi_msg_retrieve_function);
01360    ast_custom_function_unregister(&smdi_msg_function);
01361 
01362    return 0;
01363 }
01364 
01365 static int reload(void)
01366 {
01367    int res;
01368 
01369    res = smdi_load(1);
01370 
01371    if (res < 0) {
01372       return res;
01373    } else if (res == 1) {
01374       ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
01375       return 0;
01376    } else
01377       return 0;
01378 }
01379 
01380 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Simplified Message Desk Interface (SMDI) Resource",
01381       .load = load_module,
01382       .unload = unload_module,
01383       .reload = reload,
01384           );

Generated on Tue Apr 28 22:50:12 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7