Thu Apr 28 2011 17:15:25

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