Mon Sep 20 2010 00:23:45

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