Wed Oct 28 11:45:42 2009

Asterisk developer's documentation


res_smdi.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Copyright (C) 2005-2008, Digium, Inc.
00005  *
00006  * Matthew A. Nicholson <mnicholson@digium.com>
00007  * Russell Bryant <russell@digium.com>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*!
00021  * \file
00022  * \brief SMDI support for Asterisk.
00023  * \author Matthew A. Nicholson <mnicholson@digium.com>
00024  * \author Russell Bryant <russell@digium.com>
00025  *
00026  * Here is a useful mailing list post that describes SMDI protocol details:
00027  * \ref http://lists.digium.com/pipermail/asterisk-dev/2003-June/000884.html
00028  *
00029  * \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: 211551 $")
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 static void *smdi_msg_find(struct ast_smdi_interface *iface,
00370    enum smdi_message_type type, const char *station)
00371 {
00372    void *msg = NULL;
00373 
00374    purge_old_messages(iface, type);
00375 
00376    switch (type) {
00377    case SMDI_MD:
00378       msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, station);
00379       break;
00380    case SMDI_MWI:
00381       msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, station);
00382       break;
00383    }
00384 
00385    return msg;
00386 }
00387 
00388 static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout, 
00389    enum smdi_message_type type, const char *station)
00390 {
00391    struct timeval start;
00392    long diff = 0;
00393    void *msg;
00394    ast_cond_t *cond = NULL;
00395    ast_mutex_t *lock = NULL;
00396 
00397    switch (type) {
00398    case SMDI_MWI:
00399       cond = &iface->mwi_q_cond;
00400       lock = &iface->mwi_q_lock;
00401       break;
00402    case SMDI_MD:
00403       cond = &iface->md_q_cond;
00404       lock = &iface->md_q_lock;
00405       break;
00406    }
00407 
00408    start = ast_tvnow();
00409 
00410    while (diff < timeout) {
00411       struct timespec ts = { 0, };
00412       struct timeval tv;
00413 
00414       lock_msg_q(iface, type);
00415 
00416       if ((msg = smdi_msg_find(iface, type, station))) {
00417          unlock_msg_q(iface, type);
00418          return msg;
00419       }
00420 
00421       tv = ast_tvadd(start, ast_tv(0, timeout));
00422       ts.tv_sec = tv.tv_sec;
00423       ts.tv_nsec = tv.tv_usec * 1000;
00424 
00425       /* If there were no messages in the queue, then go to sleep until one
00426        * arrives. */
00427 
00428       ast_cond_timedwait(cond, lock, &ts);
00429 
00430       if ((msg = smdi_msg_find(iface, type, station))) {
00431          unlock_msg_q(iface, type);
00432          return msg;
00433       }
00434 
00435       unlock_msg_q(iface, type);
00436 
00437       /* check timeout */
00438       diff = ast_tvdiff_ms(ast_tvnow(), start);
00439    }
00440 
00441    return NULL;
00442 }
00443 
00444 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
00445 {
00446    return smdi_msg_pop(iface, SMDI_MD);
00447 }
00448 
00449 struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
00450 {
00451    return smdi_message_wait(iface, timeout, SMDI_MD, NULL);
00452 }
00453 
00454 struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
00455 {
00456    return smdi_msg_pop(iface, SMDI_MWI);
00457 }
00458 
00459 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
00460 {
00461    return smdi_message_wait(iface, timeout, SMDI_MWI, NULL);
00462 }
00463 
00464 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout,
00465    const char *station)
00466 {
00467    return smdi_message_wait(iface, timeout, SMDI_MWI, station);
00468 }
00469 
00470 struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
00471 {
00472    return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
00473 }
00474 
00475 /*! 
00476  * \internal
00477  * \brief Read an SMDI message.
00478  *
00479  * \param iface_p the SMDI interface to read from.
00480  *
00481  * This function loops and reads from and SMDI interface.  It must be stopped
00482  * using pthread_cancel().
00483  */
00484 static void *smdi_read(void *iface_p)
00485 {
00486    struct ast_smdi_interface *iface = iface_p;
00487    struct ast_smdi_md_message *md_msg;
00488    struct ast_smdi_mwi_message *mwi_msg;
00489    char c = '\0';
00490    char *cp = NULL;
00491    int i;
00492    int start = 0;
00493       
00494    /* read an smdi message */
00495    while ((c = fgetc(iface->file))) {
00496 
00497       /* check if this is the start of a message */
00498       if (!start) {
00499          if (c == 'M') {
00500             ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n");
00501             start = 1;
00502          }
00503          continue;
00504       }
00505       
00506       if (c == 'D') { /* MD message */
00507          start = 0;
00508 
00509          ast_log(LOG_DEBUG, "Read a 'D' ... it's an MD message.\n");
00510 
00511          if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
00512             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00513             return NULL;
00514          }
00515          
00516          ASTOBJ_INIT(md_msg);
00517 
00518          /* read the message desk number */
00519          for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
00520             md_msg->mesg_desk_num[i] = fgetc(iface->file);
00521             ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_num[i]);
00522          }
00523 
00524          md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
00525          
00526          ast_log(LOG_DEBUG, "The message desk number is '%s'\n", md_msg->mesg_desk_num);
00527 
00528          /* read the message desk terminal number */
00529          for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) {
00530             md_msg->mesg_desk_term[i] = fgetc(iface->file);
00531             ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_term[i]);
00532          }
00533 
00534          md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';
00535 
00536          ast_log(LOG_DEBUG, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term);
00537 
00538          /* read the message type */
00539          md_msg->type = fgetc(iface->file);
00540        
00541          ast_log(LOG_DEBUG, "Message type is '%c'\n", md_msg->type);
00542 
00543          /* read the forwarding station number (may be blank) */
00544          cp = &md_msg->fwd_st[0];
00545          for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
00546             if ((c = fgetc(iface->file)) == ' ') {
00547                *cp = '\0';
00548                ast_log(LOG_DEBUG, "Read a space, done looking for the forwarding station\n");
00549                break;
00550             }
00551 
00552             /* store c in md_msg->fwd_st */
00553             if (i >= iface->msdstrip) {
00554                ast_log(LOG_DEBUG, "Read a '%c' and stored it in the forwarding station buffer\n", c);
00555                *cp++ = c;
00556             } else {
00557                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);
00558             }
00559          }
00560 
00561          /* make sure the value is null terminated, even if this truncates it */
00562          md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
00563          cp = NULL;
00564 
00565          ast_log(LOG_DEBUG, "The forwarding station is '%s'\n", md_msg->fwd_st);
00566 
00567          /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
00568           * up a message on this field */
00569          ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));
00570 
00571          /* read the calling station number (may be blank) */
00572          cp = &md_msg->calling_st[0];
00573          for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
00574             if (!isdigit((c = fgetc(iface->file)))) {
00575                *cp = '\0';
00576                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);
00577                if (c == ' ') {
00578                   /* Don't break on a space.  We may read the space before the calling station
00579                    * here if the forwarding station buffer filled up. */
00580                   i--; /* We're still on the same character */
00581                   continue;
00582                }
00583                break;
00584             }
00585 
00586             /* store c in md_msg->calling_st */
00587             if (i >= iface->msdstrip) {
00588                ast_log(LOG_DEBUG, "Read a '%c' and stored it in the calling station buffer\n", c);
00589                *cp++ = c;
00590             } else {
00591                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);
00592             }
00593          }
00594 
00595          /* make sure the value is null terminated, even if this truncates it */
00596          md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
00597          cp = NULL;
00598 
00599          ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st);
00600 
00601          /* add the message to the message queue */
00602          md_msg->timestamp = ast_tvnow();
00603          ast_smdi_md_message_push(iface, md_msg);
00604          ast_log(LOG_DEBUG, "Received SMDI MD message on %s\n", iface->name);
00605          
00606          ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
00607 
00608       } else if (c == 'W') { /* MWI message */
00609          start = 0;
00610 
00611          ast_log(LOG_DEBUG, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
00612 
00613          if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
00614             ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00615             return NULL;
00616          }
00617 
00618          ASTOBJ_INIT(mwi_msg);
00619 
00620          /* discard the 'I' (from 'MWI') */
00621          fgetc(iface->file);
00622          
00623          /* read the forwarding station number (may be blank) */
00624          cp = &mwi_msg->fwd_st[0];
00625          for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) {
00626             if ((c = fgetc(iface->file)) == ' ') {
00627                *cp = '\0';
00628                break;
00629             }
00630 
00631             /* store c in md_msg->fwd_st */
00632             if (i >= iface->msdstrip)
00633                *cp++ = c;
00634          }
00635 
00636          /* make sure the station number is null terminated, even if this will truncate it */
00637          mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
00638          cp = NULL;
00639          
00640          /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
00641           * up a message on this field */
00642          ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));
00643 
00644          /* read the mwi failure cause */
00645          for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++)
00646             mwi_msg->cause[i] = fgetc(iface->file);
00647 
00648          mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';
00649 
00650          /* add the message to the message queue */
00651          mwi_msg->timestamp = ast_tvnow();
00652          ast_smdi_mwi_message_push(iface, mwi_msg);
00653          ast_log(LOG_DEBUG, "Received SMDI MWI message on %s\n", iface->name);
00654          
00655          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
00656       } else {
00657          ast_log(LOG_ERROR, "Unknown SMDI message type received on %s (M%c).\n", iface->name, c);
00658          start = 0;
00659       }
00660    }
00661 
00662    ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
00663    ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00664    return NULL;
00665 }
00666 
00667 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
00668 {
00669    ast_free(msg);
00670 }
00671 
00672 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
00673 {
00674    ast_free(msg);
00675 }
00676 
00677 static void destroy_mailbox_mapping(struct mailbox_mapping *mm)
00678 {
00679    ast_string_field_free_memory(mm);
00680    ASTOBJ_UNREF(mm->iface, ast_smdi_interface_destroy);
00681    free(mm);
00682 }
00683 
00684 static void destroy_all_mailbox_mappings(void)
00685 {
00686    struct mailbox_mapping *mm;
00687 
00688    ast_mutex_lock(&mwi_monitor.lock);
00689    while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry)))
00690       destroy_mailbox_mapping(mm);
00691    ast_mutex_unlock(&mwi_monitor.lock);
00692 }
00693 
00694 static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface)
00695 {
00696    struct mailbox_mapping *mm;
00697    char *mailbox, *context;
00698 
00699    if (!(mm = ast_calloc(1, sizeof(*mm))))
00700       return;
00701    
00702    if (ast_string_field_init(mm, 32)) {
00703       free(mm);
00704       return;
00705    }
00706 
00707    ast_string_field_set(mm, smdi, var->name);
00708 
00709    context = ast_strdupa(var->value);
00710    mailbox = strsep(&context, "@");
00711    if (ast_strlen_zero(context))
00712       context = "default";
00713 
00714    ast_string_field_set(mm, mailbox, mailbox);
00715    ast_string_field_set(mm, context, context);
00716 
00717    mm->iface = ASTOBJ_REF(iface);
00718 
00719    ast_mutex_lock(&mwi_monitor.lock);
00720    AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry);
00721    ast_mutex_unlock(&mwi_monitor.lock);
00722 }
00723 
00724 /*!
00725  * \note Called with the mwi_monitor.lock locked
00726  */
00727 static void poll_mailbox(struct mailbox_mapping *mm)
00728 {
00729    char buf[1024];
00730    unsigned int state;
00731 
00732    snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context);
00733 
00734    state = !!ast_app_has_voicemail(mm->mailbox, NULL);
00735 
00736    if (state != mm->cur_state) {
00737       if (state)
00738          ast_smdi_mwi_set(mm->iface, mm->smdi);
00739       else
00740          ast_smdi_mwi_unset(mm->iface, mm->smdi);
00741 
00742       mm->cur_state = state;
00743    }
00744 }
00745 
00746 static void *mwi_monitor_handler(void *data)
00747 {
00748    while (!mwi_monitor.stop) {
00749       struct timespec ts = { 0, };
00750       struct timeval tv;
00751       struct mailbox_mapping *mm;
00752 
00753       ast_mutex_lock(&mwi_monitor.lock);
00754 
00755       mwi_monitor.last_poll = ast_tvnow();
00756 
00757       AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry)
00758          poll_mailbox(mm);
00759 
00760       /* Sleep up to the configured polling interval.  Allow unload_module()
00761        * to signal us to wake up and exit. */
00762       tv = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0));
00763       ts.tv_sec = tv.tv_sec;
00764       ts.tv_nsec = tv.tv_usec * 1000;
00765       ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts);
00766 
00767       ast_mutex_unlock(&mwi_monitor.lock);
00768    }
00769 
00770    return NULL;
00771 }
00772 
00773 static struct ast_smdi_interface *alloc_smdi_interface(void)
00774 {
00775    struct ast_smdi_interface *iface;
00776 
00777    if (!(iface = ast_calloc(1, sizeof(*iface))))
00778       return NULL;
00779 
00780    ASTOBJ_INIT(iface);
00781    ASTOBJ_CONTAINER_INIT(&iface->md_q);
00782    ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
00783 
00784    ast_mutex_init(&iface->md_q_lock);
00785    ast_cond_init(&iface->md_q_cond, NULL);
00786 
00787    ast_mutex_init(&iface->mwi_q_lock);
00788    ast_cond_init(&iface->mwi_q_cond, NULL);
00789 
00790    return iface;
00791 }
00792 
00793 /*!
00794  * \internal
00795  * \brief Load and reload SMDI configuration.
00796  * \param reload this should be 1 if we are reloading and 0 if not.
00797  *
00798  * This function loads/reloads the SMDI configuration and starts and stops
00799  * interfaces accordingly.
00800  *
00801  * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started.
00802  */
00803 static int smdi_load(int reload)
00804 {
00805    struct ast_config *conf;
00806    struct ast_variable *v;
00807    struct ast_smdi_interface *iface = NULL;
00808    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
00809    int res = 0;
00810 
00811    /* Config options */
00812    speed_t baud_rate = B9600;     /* 9600 baud rate */
00813    tcflag_t paritybit = PARENB;   /* even parity checking */
00814    tcflag_t charsize = CS7;       /* seven bit characters */
00815    int stopbits = 0;              /* One stop bit */
00816    
00817    int msdstrip = 0;              /* strip zero digits */
00818    long msg_expiry = SMDI_MSG_EXPIRY_TIME;
00819 
00820    if (!(conf = ast_config_load(config_file, config_flags))) {
00821       if (reload)
00822          ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
00823       else
00824          ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
00825       return 1;
00826    } else if (conf == CONFIG_STATUS_FILEUNCHANGED)
00827       return 0;
00828 
00829    /* Mark all interfaces that we are listening on.  We will unmark them
00830     * as we find them in the config file, this way we know any interfaces
00831     * still marked after we have finished parsing the config file should
00832     * be stopped.
00833     */
00834    if (reload)
00835       ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
00836 
00837    for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
00838       if (!strcasecmp(v->name, "baudrate")) {
00839          if (!strcasecmp(v->value, "9600"))
00840             baud_rate = B9600;
00841          else if (!strcasecmp(v->value, "4800"))
00842             baud_rate = B4800;
00843          else if (!strcasecmp(v->value, "2400"))
00844             baud_rate = B2400;
00845          else if (!strcasecmp(v->value, "1200"))
00846             baud_rate = B1200;
00847          else {
00848             ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
00849             baud_rate = B9600;
00850          }
00851       } else if (!strcasecmp(v->name, "msdstrip")) {
00852          if (!sscanf(v->value, "%30d", &msdstrip)) {
00853             ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
00854             msdstrip = 0;
00855          } else if (0 > msdstrip || msdstrip > 9) {
00856             ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
00857             msdstrip = 0;
00858          }
00859       } else if (!strcasecmp(v->name, "msgexpirytime")) {
00860          if (!sscanf(v->value, "%30ld", &msg_expiry)) {
00861             ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
00862             msg_expiry = SMDI_MSG_EXPIRY_TIME;
00863          }
00864       } else if (!strcasecmp(v->name, "paritybit")) {
00865          if (!strcasecmp(v->value, "even"))
00866             paritybit = PARENB;
00867          else if (!strcasecmp(v->value, "odd"))
00868             paritybit = PARENB | PARODD;
00869          else if (!strcasecmp(v->value, "none"))
00870             paritybit = ~PARENB;
00871          else {
00872             ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
00873             paritybit = PARENB;
00874          }
00875       } else if (!strcasecmp(v->name, "charsize")) {
00876          if (!strcasecmp(v->value, "7"))
00877             charsize = CS7;
00878          else if (!strcasecmp(v->value, "8"))
00879             charsize = CS8;
00880          else {
00881             ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
00882             charsize = CS7;
00883          }
00884       } else if (!strcasecmp(v->name, "twostopbits")) {
00885          stopbits = ast_true(v->name);
00886       } else if (!strcasecmp(v->name, "smdiport")) {
00887          if (reload) {
00888             /* we are reloading, check if we are already
00889              * monitoring this interface, if we are we do
00890              * not want to start it again.  This also has
00891              * the side effect of not updating different
00892              * setting for the serial port, but it should
00893              * be trivial to rewrite this section so that
00894              * options on the port are changed without
00895              * restarting the interface.  Or the interface
00896              * could be restarted with out emptying the
00897              * queue. */
00898             if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
00899                ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
00900                ASTOBJ_UNMARK(iface);
00901                ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00902                continue;
00903             }
00904          }
00905          
00906          if (!(iface = alloc_smdi_interface()))
00907             continue;
00908 
00909          ast_copy_string(iface->name, v->value, sizeof(iface->name));
00910 
00911          iface->thread = AST_PTHREADT_NULL;
00912 
00913          if (!(iface->file = fopen(iface->name, "r"))) {
00914             ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s)\n", iface->name, strerror(errno));
00915             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00916             continue;
00917          }
00918 
00919          iface->fd = fileno(iface->file);
00920 
00921          /* Set the proper attributes for our serial port. */
00922 
00923          /* get the current attributes from the port */
00924          if (tcgetattr(iface->fd, &iface->mode)) {
00925             ast_log(LOG_ERROR, "Error getting atributes of %s (%s)\n", iface->name, strerror(errno));
00926             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00927             continue;
00928          }
00929 
00930          /* set the desired speed */
00931          if (cfsetispeed(&iface->mode, baud_rate) || cfsetospeed(&iface->mode, baud_rate)) {
00932             ast_log(LOG_ERROR, "Error setting baud rate on %s (%s)\n", iface->name, strerror(errno));
00933             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00934             continue;
00935          }
00936          
00937          /* set the stop bits */
00938          if (stopbits)
00939             iface->mode.c_cflag = iface->mode.c_cflag | CSTOPB;   /* set two stop bits */
00940          else
00941             iface->mode.c_cflag = iface->mode.c_cflag & ~CSTOPB;  /* set one stop bit */
00942          
00943          /* set the parity */
00944          iface->mode.c_cflag = (iface->mode.c_cflag & ~PARENB & ~PARODD) | paritybit;
00945          
00946          /* set the character size */
00947          iface->mode.c_cflag = (iface->mode.c_cflag & ~CSIZE) | charsize;
00948          
00949          /* commit the desired attributes */
00950          if (tcsetattr(iface->fd, TCSAFLUSH, &iface->mode)) {
00951             ast_log(LOG_ERROR, "Error setting attributes on %s (%s)\n", iface->name, strerror(errno));
00952             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00953             continue;
00954          }
00955 
00956          /* set the msdstrip */
00957          iface->msdstrip = msdstrip;
00958 
00959          /* set the message expiry time */
00960          iface->msg_expiry = msg_expiry;
00961 
00962          /* start the listener thread */
00963          ast_verb(3, "Starting SMDI monitor thread for %s\n", iface->name);
00964          if (ast_pthread_create_background(&iface->thread, NULL, smdi_read, iface)) {
00965             ast_log(LOG_ERROR, "Error starting SMDI monitor thread for %s\n", iface->name);
00966             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00967             continue;
00968          }
00969 
00970          ASTOBJ_CONTAINER_LINK(&smdi_ifaces, iface);
00971          ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00972          ast_module_ref(ast_module_info->self);
00973       } else {
00974          ast_log(LOG_NOTICE, "Ignoring unknown option %s in %s\n", v->name, config_file);
00975       }
00976    }
00977 
00978    destroy_all_mailbox_mappings();
00979    mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
00980    
00981    iface = NULL;
00982 
00983    for (v = ast_variable_browse(conf, "mailboxes"); v; v = v->next) {
00984       if (!strcasecmp(v->name, "smdiport")) {
00985          if (iface)
00986             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00987 
00988          if (!(iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
00989             ast_log(LOG_NOTICE, "SMDI interface %s not found\n", iface->name);
00990             continue;
00991          }
00992       } else if (!strcasecmp(v->name, "pollinginterval")) {
00993          if (sscanf(v->value, "%30u", &mwi_monitor.polling_interval) != 1) {
00994             ast_log(LOG_ERROR, "Invalid value for pollinginterval: %s\n", v->value);
00995             mwi_monitor.polling_interval = DEFAULT_POLLING_INTERVAL;
00996          }
00997       } else {
00998          if (!iface) {
00999             ast_log(LOG_ERROR, "Mailbox mapping ignored, no valid SMDI interface specified in mailboxes section\n");
01000             continue;
01001          }
01002          append_mailbox_mapping(v, iface);
01003       }
01004    }
01005 
01006    if (iface)
01007       ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01008 
01009    ast_config_destroy(conf);
01010    
01011    if (!AST_LIST_EMPTY(&mwi_monitor.mailbox_mappings) && mwi_monitor.thread == AST_PTHREADT_NULL
01012       && ast_pthread_create_background(&mwi_monitor.thread, NULL, mwi_monitor_handler, NULL)) {
01013       ast_log(LOG_ERROR, "Failed to start MWI monitoring thread.  This module will not operate.\n");
01014       return AST_MODULE_LOAD_FAILURE;
01015    }
01016 
01017    /* Prune any interfaces we should no longer monitor. */
01018    if (reload)
01019       ASTOBJ_CONTAINER_PRUNE_MARKED(&smdi_ifaces, ast_smdi_interface_destroy);
01020    
01021    ASTOBJ_CONTAINER_RDLOCK(&smdi_ifaces);
01022    /* TODO: this is bad, we need an ASTOBJ method for this! */
01023    if (!smdi_ifaces.head)
01024       res = 1;
01025    ASTOBJ_CONTAINER_UNLOCK(&smdi_ifaces);
01026    
01027    return res;
01028 }
01029 
01030 struct smdi_msg_datastore {
01031    unsigned int id;
01032    struct ast_smdi_interface *iface;
01033    struct ast_smdi_md_message *md_msg;
01034 };
01035 
01036 static void smdi_msg_datastore_destroy(void *data)
01037 {
01038    struct smdi_msg_datastore *smd = data;
01039 
01040    if (smd->iface)
01041       ASTOBJ_UNREF(smd->iface, ast_smdi_interface_destroy);
01042 
01043    if (smd->md_msg)
01044       ASTOBJ_UNREF(smd->md_msg, ast_smdi_md_message_destroy);
01045 
01046    free(smd);
01047 }
01048 
01049 static const struct ast_datastore_info smdi_msg_datastore_info = {
01050    .type = "SMDIMSG",
01051    .destroy = smdi_msg_datastore_destroy,
01052 };
01053 
01054 static int smdi_msg_id;
01055 
01056 /*! In milliseconds */
01057 #define SMDI_RETRIEVE_TIMEOUT_DEFAULT 3000
01058 
01059 static int smdi_msg_retrieve_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01060 {
01061    struct ast_module_user *u;
01062    AST_DECLARE_APP_ARGS(args,
01063       AST_APP_ARG(port);
01064       AST_APP_ARG(station);
01065       AST_APP_ARG(timeout);
01066    );
01067    unsigned int timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
01068    int res = -1;
01069    char *parse = NULL;
01070    struct smdi_msg_datastore *smd = NULL;
01071    struct ast_datastore *datastore = NULL;
01072    struct ast_smdi_interface *iface = NULL;
01073    struct ast_smdi_md_message *md_msg = NULL;
01074 
01075    u = ast_module_user_add(chan);
01076 
01077    if (ast_strlen_zero(data)) {
01078       ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE requires an argument\n");
01079       goto return_error;
01080    }
01081 
01082    if (!chan) {
01083       ast_log(LOG_ERROR, "SMDI_MSG_RETRIEVE must be used with a channel\n");
01084       goto return_error;
01085    }
01086 
01087    ast_autoservice_start(chan);
01088 
01089    parse = ast_strdupa(data);
01090    AST_STANDARD_APP_ARGS(args, parse);
01091 
01092    if (ast_strlen_zero(args.port) || ast_strlen_zero(args.station)) {
01093       ast_log(LOG_ERROR, "Invalid arguments provided to SMDI_MSG_RETRIEVE\n");
01094       goto return_error;
01095    }
01096 
01097    if (!(iface = ast_smdi_interface_find(args.port))) {
01098       ast_log(LOG_ERROR, "SMDI port '%s' not found\n", args.port);
01099       goto return_error;
01100    }
01101 
01102    if (!ast_strlen_zero(args.timeout)) {
01103       if (sscanf(args.timeout, "%30u", &timeout) != 1) {
01104          ast_log(LOG_ERROR, "'%s' is not a valid timeout\n", args.timeout);
01105          timeout = SMDI_RETRIEVE_TIMEOUT_DEFAULT;
01106       }
01107    }
01108 
01109    if (!(md_msg = smdi_message_wait(iface, timeout, SMDI_MD, args.station))) {
01110       ast_log(LOG_WARNING, "No SMDI message retrieved for station '%s' after "
01111          "waiting %u ms.\n", args.station, timeout);
01112       goto return_error;
01113    }
01114 
01115    if (!(smd = ast_calloc(1, sizeof(*smd))))
01116       goto return_error;
01117 
01118    smd->iface = ASTOBJ_REF(iface);
01119    smd->md_msg = ASTOBJ_REF(md_msg);
01120    smd->id = ast_atomic_fetchadd_int((int *) &smdi_msg_id, 1);
01121    snprintf(buf, len, "%u", smd->id);
01122 
01123    if (!(datastore = ast_channel_datastore_alloc(&smdi_msg_datastore_info, buf)))
01124       goto return_error;
01125 
01126    datastore->data = smd;
01127 
01128    ast_channel_lock(chan);
01129    ast_channel_datastore_add(chan, datastore);
01130    ast_channel_unlock(chan);
01131 
01132    res = 0;
01133 
01134 return_error:
01135    if (iface)
01136       ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
01137 
01138    if (md_msg)
01139       ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
01140 
01141    if (smd && !datastore)
01142       smdi_msg_datastore_destroy(smd);
01143 
01144    if (parse)
01145       ast_autoservice_stop(chan);
01146 
01147    ast_module_user_remove(u);
01148 
01149    return res;
01150 }
01151 
01152 static int smdi_msg_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
01153 {
01154    struct ast_module_user *u;
01155    int res = -1;
01156    AST_DECLARE_APP_ARGS(args,
01157       AST_APP_ARG(id);
01158       AST_APP_ARG(component);
01159    );
01160    char *parse;
01161    struct ast_datastore *datastore = NULL;
01162    struct smdi_msg_datastore *smd = NULL;
01163 
01164    u = ast_module_user_add(chan);
01165 
01166    if (!chan) {
01167       ast_log(LOG_ERROR, "SMDI_MSG can not be called without a channel\n");
01168       goto return_error;
01169    }
01170 
01171    if (ast_strlen_zero(data)) {
01172       ast_log(LOG_WARNING, "SMDI_MSG requires an argument\n");
01173       goto return_error;
01174    }
01175 
01176    parse = ast_strdupa(data);
01177    AST_STANDARD_APP_ARGS(args, parse);
01178 
01179    if (ast_strlen_zero(args.id)) {
01180       ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
01181       goto return_error;
01182    }
01183 
01184    if (ast_strlen_zero(args.component)) {
01185       ast_log(LOG_WARNING, "ID must be supplied to SMDI_MSG\n");
01186       goto return_error;
01187    }
01188 
01189    ast_channel_lock(chan);
01190    datastore = ast_channel_datastore_find(chan, &smdi_msg_datastore_info, args.id);
01191    ast_channel_unlock(chan);
01192    
01193    if (!datastore) {
01194       ast_log(LOG_WARNING, "No SMDI message found for message ID '%s'\n", args.id);
01195       goto return_error;
01196    }
01197 
01198    smd = datastore->data;
01199 
01200    if (!strcasecmp(args.component, "station")) {
01201       ast_copy_string(buf, smd->md_msg->fwd_st, len);
01202    } else if (!strcasecmp(args.component, "callerid")) {
01203       ast_copy_string(buf, smd->md_msg->calling_st, len);
01204    } else if (!strcasecmp(args.component, "type")) {
01205       snprintf(buf, len, "%c", smd->md_msg->type);
01206    } else {
01207       ast_log(LOG_ERROR, "'%s' is not a valid message component for SMDI_MSG\n",
01208          args.component);
01209       goto return_error;
01210    }
01211 
01212    res = 0;
01213 
01214 return_error:
01215    ast_module_user_remove(u);
01216 
01217    return 0;
01218 }
01219 
01220 static struct ast_custom_function smdi_msg_retrieve_function = {
01221    .name = "SMDI_MSG_RETRIEVE",
01222    .synopsis = "Retrieve an SMDI message.",
01223    .syntax = "SMDI_MSG_RETRIEVE(<smdi port>,<station>[,timeout])",
01224    .desc = 
01225    "   This function is used to retrieve an incoming SMDI message.  It returns\n"
01226    "an ID which can be used with the SMDI_MSG() function to access details of\n"
01227    "the message.  Note that this is a destructive function in the sense that\n"
01228    "once an SMDI message is retrieved using this function, it is no longer in\n"
01229    "the global SMDI message queue, and can not be accessed by any other Asterisk\n"
01230    "channels.  The timeout for this function is optional, and the default is\n"
01231    "3 seconds.  When providing a timeout, it should be in milliseconds.\n"
01232    "",
01233    .read = smdi_msg_retrieve_read,
01234 };
01235 
01236 static struct ast_custom_function smdi_msg_function = {
01237    .name = "SMDI_MSG",
01238    .synopsis = "Retrieve details about an SMDI message.",
01239    .syntax = "SMDI_MSG(<message_id>,<component>)",
01240    .desc = 
01241    "   This function is used to access details of an SMDI message that was\n"
01242    "pulled from the incoming SMDI message queue using the SMDI_MSG_RETRIEVE()\n"
01243    "function.\n"
01244    "   Valid message components are:\n"
01245    "      station  - The forwarding station\n"
01246    "      callerid - The callerID of the calling party that was forwarded\n"
01247    "      type     - The call type.  The value here is the exact character\n"
01248    "                 that came in on the SMDI link.  Typically, example values\n"
01249    "                 are: D - Direct Calls, A - Forward All Calls,\n"
01250    "                      B - Forward Busy Calls, N - Forward No Answer Calls\n"
01251    "",
01252    .read = smdi_msg_read,
01253 };
01254 
01255 static int unload_module(void);
01256 
01257 static int load_module(void)
01258 {
01259    int res;
01260    
01261    /* initialize our containers */
01262    memset(&smdi_ifaces, 0, sizeof(smdi_ifaces));
01263    ASTOBJ_CONTAINER_INIT(&smdi_ifaces);
01264    
01265    ast_mutex_init(&mwi_monitor.lock);
01266    ast_cond_init(&mwi_monitor.cond, NULL);
01267 
01268    ast_custom_function_register(&smdi_msg_retrieve_function);
01269    ast_custom_function_register(&smdi_msg_function);
01270 
01271    /* load the config and start the listener threads*/
01272    res = smdi_load(0);
01273    if (res < 0) {
01274       unload_module();
01275       return res;
01276    } else if (res == 1) {
01277       unload_module();
01278       ast_log(LOG_NOTICE, "No SMDI interfaces are available to listen on, not starting SMDI listener.\n");
01279       return AST_MODULE_LOAD_DECLINE;
01280    }
01281    
01282    return AST_MODULE_LOAD_SUCCESS;
01283 }
01284 
01285 static int unload_module(void)
01286 {
01287    /* this destructor stops any running smdi_read threads */
01288    ASTOBJ_CONTAINER_DESTROYALL(&smdi_ifaces, ast_smdi_interface_destroy);
01289    ASTOBJ_CONTAINER_DESTROY(&smdi_ifaces);
01290 
01291    destroy_all_mailbox_mappings();
01292 
01293    ast_mutex_lock(&mwi_monitor.lock);
01294    mwi_monitor.stop = 1;
01295    ast_cond_signal(&mwi_monitor.cond);
01296    ast_mutex_unlock(&mwi_monitor.lock);
01297 
01298    if (mwi_monitor.thread != AST_PTHREADT_NULL) {
01299       pthread_join(mwi_monitor.thread, NULL);
01300    }
01301 
01302    ast_custom_function_unregister(&smdi_msg_retrieve_function);
01303    ast_custom_function_unregister(&smdi_msg_function);
01304 
01305    return 0;
01306 }
01307 
01308 static int reload(void)
01309 {
01310    int res;
01311 
01312    res = smdi_load(1);
01313 
01314    if (res < 0) {
01315       return res;
01316    } else if (res == 1) {
01317       ast_log(LOG_WARNING, "No SMDI interfaces were specified to listen on, not starting SDMI listener.\n");
01318       return 0;
01319    } else
01320       return 0;
01321 }
01322 
01323 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Simplified Message Desk Interface (SMDI) Resource",
01324       .load = load_module,
01325       .unload = unload_module,
01326       .reload = reload,
01327           );

Generated on Wed Oct 28 11:45:42 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6