Wed Aug 18 22:33:55 2010

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

Generated on Wed Aug 18 22:33:55 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7