Sat Nov 1 06:28:38 2008

Asterisk developer's documentation


res_smdi.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Copyright (C) 2005-2008, Digium, Inc.
00005  *
00006  * Matthew A. Nicholson <mnicholson@digium.com>
00007  * Russell Bryant <russell@digium.com>
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*!
00021  * \file
00022  * \brief SMDI support for Asterisk.
00023  * \author Matthew A. Nicholson <mnicholson@digium.com>
00024  * \author Russell Bryant <russell@digium.com>
00025  *
00026  * Here is a useful mailing list post that describes SMDI protocol details:
00027  * \ref http://lists.digium.com/pipermail/asterisk-dev/2003-June/000884.html
00028  */
00029 
00030 #include "asterisk.h"
00031 
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 146799 $")
00033 
00034 #include <stdio.h>
00035 #include <stdlib.h>
00036 #include <errno.h>
00037 #include <termios.h>
00038 #include <sys/time.h>
00039 #include <time.h>
00040 #include <ctype.h>
00041 
00042 #include "asterisk/module.h"
00043 #include "asterisk/lock.h"
00044 #include "asterisk/utils.h"
00045 #include "asterisk/smdi.h"
00046 #include "asterisk/config.h"
00047 #include "asterisk/astobj.h"
00048 #include "asterisk/io.h"
00049 #include "asterisk/logger.h"
00050 #include "asterisk/utils.h"
00051 #include "asterisk/options.h"
00052 #include "asterisk/stringfields.h"
00053 #include "asterisk/linkedlists.h"
00054 #include "asterisk/app.h"
00055 #include "asterisk/pbx.h"
00056 
00057 /* Message expiry time in milliseconds */
00058 #define SMDI_MSG_EXPIRY_TIME  30000 /* 30 seconds */
00059 
00060 static const char config_file[] = "smdi.conf";
00061 
00062 /*! \brief SMDI message desk message queue. */
00063 struct ast_smdi_md_queue {
00064    ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_md_message);
00065 };
00066 
00067 /*! \brief SMDI message waiting indicator message queue. */
00068 struct ast_smdi_mwi_queue {
00069    ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_mwi_message);
00070 };
00071 
00072 struct ast_smdi_interface {
00073    ASTOBJ_COMPONENTS_FULL(struct ast_smdi_interface, SMDI_MAX_FILENAME_LEN, 1);
00074    struct ast_smdi_md_queue md_q;
00075    ast_mutex_t md_q_lock;
00076    ast_cond_t md_q_cond;
00077    struct ast_smdi_mwi_queue mwi_q;
00078    ast_mutex_t mwi_q_lock;
00079    ast_cond_t mwi_q_cond;
00080    FILE *file;
00081    int fd;
00082    pthread_t thread;
00083    struct termios mode;
00084    int msdstrip;
00085    long msg_expiry;
00086 };
00087 
00088 /*! \brief SMDI interface container. */
00089 struct ast_smdi_interface_container {
00090    ASTOBJ_CONTAINER_COMPONENTS(struct ast_smdi_interface);
00091 } smdi_ifaces;
00092 
00093 /*! \brief A mapping between an SMDI mailbox ID and an Asterisk mailbox */
00094 struct mailbox_mapping {
00095    /*! This is the current state of the mailbox.  It is simply on or
00096     *  off to indicate if there are messages waiting or not. */
00097    unsigned int cur_state:1;
00098    /*! A Pointer to the appropriate SMDI interface */
00099    struct ast_smdi_interface *iface;
00100    AST_DECLARE_STRING_FIELDS(
00101       /*! The Name of the mailbox for the SMDI link. */
00102       AST_STRING_FIELD(smdi);
00103       /*! The name of the mailbox on the Asterisk side */
00104       AST_STRING_FIELD(mailbox);
00105       /*! The name of the voicemail context in use */
00106       AST_STRING_FIELD(context);
00107    );
00108    AST_LIST_ENTRY(mailbox_mapping) entry;
00109 };
00110 
00111 /*! 10 seconds */
00112 #define DEFAULT_POLLING_INTERVAL 10
00113 
00114 /*! \brief Data that gets used by the SMDI MWI monitoring thread */
00115 static struct {
00116    /*! The thread ID */
00117    pthread_t thread;
00118    ast_mutex_t lock;
00119    ast_cond_t cond;
00120    /*! A list of mailboxes that need to be monitored */
00121    AST_LIST_HEAD_NOLOCK(, mailbox_mapping) mailbox_mappings;
00122    /*! Polling Interval for checking mailbox status */
00123    unsigned int polling_interval;
00124    /*! Set to 1 to tell the polling thread to stop */
00125    unsigned int stop:1;
00126    /*! The time that the last poll began */
00127    struct timeval last_poll;
00128 } mwi_monitor = {
00129    .thread = AST_PTHREADT_NULL,
00130 };
00131 
00132 static void ast_smdi_interface_destroy(struct ast_smdi_interface *iface)
00133 {
00134    if (iface->thread != AST_PTHREADT_NULL && iface->thread != AST_PTHREADT_STOP) {
00135       pthread_cancel(iface->thread);
00136       pthread_join(iface->thread, NULL);
00137    }
00138    
00139    iface->thread = AST_PTHREADT_STOP;
00140    
00141    if (iface->file) 
00142       fclose(iface->file);
00143    
00144    ASTOBJ_CONTAINER_DESTROYALL(&iface->md_q, ast_smdi_md_message_destroy);
00145    ASTOBJ_CONTAINER_DESTROYALL(&iface->mwi_q, ast_smdi_mwi_message_destroy);
00146    ASTOBJ_CONTAINER_DESTROY(&iface->md_q);
00147    ASTOBJ_CONTAINER_DESTROY(&iface->mwi_q);
00148 
00149    ast_mutex_destroy(&iface->md_q_lock);
00150    ast_cond_destroy(&iface->md_q_cond);
00151 
00152    ast_mutex_destroy(&iface->mwi_q_lock);
00153    ast_cond_destroy(&iface->mwi_q_cond);
00154 
00155    free(iface);
00156 
00157    ast_module_unref(ast_module_info->self);
00158 }
00159 
00160 void ast_smdi_interface_unref(struct ast_smdi_interface *iface)
00161 {
00162    ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00163 }
00164 
00165 /*! 
00166  * \internal
00167  * \brief Push an SMDI message to the back of an interface's message queue.
00168  * \param iface a pointer to the interface to use.
00169  * \param md_msg a pointer to the message to use.
00170  */
00171 static void ast_smdi_md_message_push(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
00172 {
00173    ast_mutex_lock(&iface->md_q_lock);
00174    ASTOBJ_CONTAINER_LINK_END(&iface->md_q, md_msg);
00175    ast_cond_broadcast(&iface->md_q_cond);
00176    ast_mutex_unlock(&iface->md_q_lock);
00177 }
00178 
00179 /*!
00180  * \internal
00181  * \brief Push an SMDI message to the back of an interface's message queue.
00182  * \param iface a pointer to the interface to use.
00183  * \param mwi_msg a pointer to the message to use.
00184  */
00185 static void ast_smdi_mwi_message_push(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
00186 {
00187    ast_mutex_lock(&iface->mwi_q_lock);
00188    ASTOBJ_CONTAINER_LINK_END(&iface->mwi_q, mwi_msg);
00189    ast_cond_broadcast(&iface->mwi_q_cond);
00190    ast_mutex_unlock(&iface->mwi_q_lock);
00191 }
00192 
00193 static int smdi_toggle_mwi(struct ast_smdi_interface *iface, const char *mailbox, int on)
00194 {
00195    FILE *file;
00196    int i;
00197    
00198    if (!(file = fopen(iface->name, "w"))) {
00199       ast_log(LOG_ERROR, "Error opening SMDI interface %s (%s) for writing\n", iface->name, strerror(errno));
00200       return 1;
00201    }  
00202 
00203    ASTOBJ_WRLOCK(iface);
00204 
00205    fprintf(file, "%s:MWI ", on ? "OP" : "RMV");
00206 
00207    for (i = 0; i < iface->msdstrip; i++)
00208       fprintf(file, "0");
00209 
00210    fprintf(file, "%s!\x04", mailbox);
00211 
00212    fclose(file);
00213 
00214    ASTOBJ_UNLOCK(iface);
00215 
00216    ast_log(LOG_DEBUG, "Sent MWI %s message for %s on %s\n", on ? "set" : "unset", 
00217       mailbox, iface->name);
00218 
00219    return 0;
00220 }
00221 
00222 int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
00223 {
00224    return smdi_toggle_mwi(iface, mailbox, 1);
00225 }
00226 
00227 int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
00228 {
00229    return smdi_toggle_mwi(iface, mailbox, 0);
00230 }
00231 
00232 void ast_smdi_md_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_md_message *md_msg)
00233 {
00234    ast_mutex_lock(&iface->md_q_lock);
00235    ASTOBJ_CONTAINER_LINK_START(&iface->md_q, md_msg);
00236    ast_cond_broadcast(&iface->md_q_cond);
00237    ast_mutex_unlock(&iface->md_q_lock);
00238 }
00239 
00240 void ast_smdi_mwi_message_putback(struct ast_smdi_interface *iface, struct ast_smdi_mwi_message *mwi_msg)
00241 {
00242    ast_mutex_lock(&iface->mwi_q_lock);
00243    ASTOBJ_CONTAINER_LINK_START(&iface->mwi_q, mwi_msg);
00244    ast_cond_broadcast(&iface->mwi_q_cond);
00245    ast_mutex_unlock(&iface->mwi_q_lock);
00246 }
00247 
00248 enum smdi_message_type {
00249    SMDI_MWI,
00250    SMDI_MD,
00251 };
00252 
00253 static inline int lock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
00254 {
00255    switch (type) {
00256    case SMDI_MWI:
00257       return ast_mutex_lock(&iface->mwi_q_lock);
00258    case SMDI_MD:  
00259       return ast_mutex_lock(&iface->md_q_lock);
00260    }
00261    
00262    return -1;
00263 }
00264 
00265 static inline int unlock_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
00266 {
00267    switch (type) {
00268    case SMDI_MWI:
00269       return ast_mutex_unlock(&iface->mwi_q_lock);
00270    case SMDI_MD:
00271       return ast_mutex_unlock(&iface->md_q_lock);
00272    }
00273 
00274    return -1;
00275 }
00276 
00277 static inline void *unlink_from_msg_q(struct ast_smdi_interface *iface, enum smdi_message_type type)
00278 {
00279    switch (type) {
00280    case SMDI_MWI:
00281       return ASTOBJ_CONTAINER_UNLINK_START(&iface->mwi_q);
00282    case SMDI_MD:
00283       return ASTOBJ_CONTAINER_UNLINK_START(&iface->md_q);
00284    }
00285 
00286    return NULL;
00287 }
00288 
00289 static inline struct timeval msg_timestamp(void *msg, enum smdi_message_type type)
00290 {
00291    struct ast_smdi_md_message *md_msg = msg;
00292    struct ast_smdi_mwi_message *mwi_msg = msg;
00293 
00294    switch (type) {
00295    case SMDI_MWI:
00296       return mwi_msg->timestamp;
00297    case SMDI_MD:
00298       return md_msg->timestamp;
00299    }
00300 
00301    return ast_tv(0, 0);
00302 }
00303 
00304 static inline void unref_msg(void *msg, enum smdi_message_type type)
00305 {
00306    struct ast_smdi_md_message *md_msg = msg;
00307    struct ast_smdi_mwi_message *mwi_msg = msg;
00308 
00309    switch (type) {
00310    case SMDI_MWI:
00311       ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
00312    case SMDI_MD:
00313       ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
00314    }
00315 }
00316 
00317 static void purge_old_messages(struct ast_smdi_interface *iface, enum smdi_message_type type)
00318 {
00319    struct timeval now;
00320    long elapsed = 0;
00321    void *msg;
00322    
00323    lock_msg_q(iface, type);
00324    msg = unlink_from_msg_q(iface, type);
00325    unlock_msg_q(iface, type);
00326 
00327    /* purge old messages */
00328    now = ast_tvnow();
00329    while (msg) {
00330       elapsed = ast_tvdiff_ms(now, msg_timestamp(msg, type));
00331 
00332       if (elapsed > iface->msg_expiry) {
00333          /* found an expired message */
00334          unref_msg(msg, type);
00335          ast_log(LOG_NOTICE, "Purged expired message from %s SMDI %s message queue.  "
00336             "Message was %ld milliseconds too old.\n",
00337             iface->name, (type == SMDI_MD) ? "MD" : "MWI", 
00338             elapsed - iface->msg_expiry);
00339 
00340          lock_msg_q(iface, type);
00341          msg = unlink_from_msg_q(iface, type);
00342          unlock_msg_q(iface, type);
00343       } else {
00344          /* good message, put it back and return */
00345          switch (type) {
00346          case SMDI_MD:
00347             ast_smdi_md_message_push(iface, msg);
00348             break;
00349          case SMDI_MWI:
00350             ast_smdi_mwi_message_push(iface, msg);
00351             break;
00352          }
00353          unref_msg(msg, type);
00354          break;
00355       }
00356    }
00357 }
00358 
00359 static void *smdi_msg_pop(struct ast_smdi_interface *iface, enum smdi_message_type type)
00360 {
00361    void *msg;
00362 
00363    purge_old_messages(iface, type);
00364 
00365    lock_msg_q(iface, type);
00366    msg = unlink_from_msg_q(iface, type);
00367    unlock_msg_q(iface, type);
00368 
00369    return msg;
00370 }
00371 
00372 enum {
00373    OPT_SEARCH_TERMINAL = (1 << 0),
00374    OPT_SEARCH_NUMBER   = (1 << 1),
00375 };
00376 
00377 static void *smdi_msg_find(struct ast_smdi_interface *iface,
00378    enum smdi_message_type type, const char *search_key, struct ast_flags options)
00379 {
00380    void *msg = NULL;
00381 
00382    purge_old_messages(iface, type);
00383 
00384    switch (type) {
00385    case SMDI_MD:
00386       if (ast_test_flag(&options, OPT_SEARCH_TERMINAL)) {
00387          struct ast_smdi_md_message *md_msg = NULL;
00388 
00389          /* Searching by the message desk terminal */
00390 
00391          ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
00392             if (!strcasecmp(iterator->mesg_desk_term, search_key))
00393                md_msg = ASTOBJ_REF(iterator);
00394          } while (0); );
00395 
00396          msg = md_msg;
00397       } else if (ast_test_flag(&options, OPT_SEARCH_NUMBER)) {
00398          struct ast_smdi_md_message *md_msg = NULL;
00399 
00400          /* Searching by the message desk number */
00401 
00402          ASTOBJ_CONTAINER_TRAVERSE(&iface->md_q, !md_msg, do {
00403             if (!strcasecmp(iterator->mesg_desk_num, search_key))
00404                md_msg = ASTOBJ_REF(iterator);
00405          } while (0); );
00406 
00407          msg = md_msg;
00408       } else {
00409          /* Searching by the forwarding station */
00410          msg = ASTOBJ_CONTAINER_FIND(&iface->md_q, search_key);
00411       }
00412       break;
00413    case SMDI_MWI:
00414       msg = ASTOBJ_CONTAINER_FIND(&iface->mwi_q, search_key);
00415       break;
00416    }
00417 
00418    return msg;
00419 }
00420 
00421 static void *smdi_message_wait(struct ast_smdi_interface *iface, int timeout, 
00422    enum smdi_message_type type, const char *search_key, struct ast_flags options)
00423 {
00424    struct timeval start;
00425    long diff = 0;
00426    void *msg;
00427    ast_cond_t *cond = NULL;
00428    ast_mutex_t *lock = NULL;
00429 
00430    switch (type) {
00431    case SMDI_MWI:
00432       cond = &iface->mwi_q_cond;
00433       lock = &iface->mwi_q_lock;
00434       break;
00435    case SMDI_MD:
00436       cond = &iface->md_q_cond;
00437       lock = &iface->md_q_lock;
00438       break;
00439    }
00440 
00441    start = ast_tvnow();
00442    while (diff < timeout) {
00443       struct timespec ts = { 0, };
00444       struct timeval tv;
00445 
00446       lock_msg_q(iface, type);
00447 
00448       if ((msg = smdi_msg_find(iface, type, search_key, options))) {
00449          unlock_msg_q(iface, type);
00450          return msg;
00451       }
00452 
00453       tv = ast_tvadd(start, ast_tv(0, timeout));
00454       ts.tv_sec = tv.tv_sec;
00455       ts.tv_nsec = tv.tv_usec * 1000;
00456 
00457       /* If there were no messages in the queue, then go to sleep until one
00458        * arrives. */
00459 
00460       ast_cond_timedwait(cond, lock, &ts);
00461 
00462       if ((msg = smdi_msg_find(iface, type, search_key, options))) {
00463          unlock_msg_q(iface, type);
00464          return msg;
00465       }
00466 
00467       unlock_msg_q(iface, type);
00468 
00469       /* check timeout */
00470       diff = ast_tvdiff_ms(ast_tvnow(), start);
00471    }
00472 
00473    return NULL;
00474 }
00475 
00476 struct ast_smdi_md_message *ast_smdi_md_message_pop(struct ast_smdi_interface *iface)
00477 {
00478    return smdi_msg_pop(iface, SMDI_MD);
00479 }
00480 
00481 struct ast_smdi_md_message *ast_smdi_md_message_wait(struct ast_smdi_interface *iface, int timeout)
00482 {
00483    struct ast_flags options = { 0 };
00484    return smdi_message_wait(iface, timeout, SMDI_MD, NULL, options);
00485 }
00486 
00487 struct ast_smdi_mwi_message *ast_smdi_mwi_message_pop(struct ast_smdi_interface *iface)
00488 {
00489    return smdi_msg_pop(iface, SMDI_MWI);
00490 }
00491 
00492 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait(struct ast_smdi_interface *iface, int timeout)
00493 {
00494    struct ast_flags options = { 0 };
00495    return smdi_message_wait(iface, timeout, SMDI_MWI, NULL, options);
00496 }
00497 
00498 struct ast_smdi_mwi_message *ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout,
00499    const char *station)
00500 {
00501    struct ast_flags options = { 0 };
00502    return smdi_message_wait(iface, timeout, SMDI_MWI, station, options);
00503 }
00504 
00505 struct ast_smdi_interface *ast_smdi_interface_find(const char *iface_name)
00506 {
00507    return (ASTOBJ_CONTAINER_FIND(&smdi_ifaces, iface_name));
00508 }
00509 
00510 /*! 
00511  * \internal
00512  * \brief Read an SMDI message.
00513  *
00514  * \param iface_p the SMDI interface to read from.
00515  *
00516  * This function loops and reads from and SMDI interface.  It must be stopped
00517  * using pthread_cancel().
00518  */
00519 static void *smdi_read(void *iface_p)
00520 {
00521    struct ast_smdi_interface *iface = iface_p;
00522    struct ast_smdi_md_message *md_msg;
00523    struct ast_smdi_mwi_message *mwi_msg;
00524    char c = '\0';
00525    char *cp = NULL;
00526    int i;
00527    int start = 0;
00528       
00529    /* read an smdi message */
00530    while ((c = fgetc(iface->file))) {
00531 
00532       /* check if this is the start of a message */
00533       if (!start) {
00534          if (c == 'M') {
00535             ast_log(LOG_DEBUG, "Read an 'M' to start an SMDI message\n");
00536             start = 1;
00537          }
00538          continue;
00539       }
00540       
00541       if (c == 'D') { /* MD message */
00542          start = 0;
00543 
00544          ast_log(LOG_DEBUG, "Read a 'D' ... it's an MD message.\n");
00545 
00546          if (!(md_msg = ast_calloc(1, sizeof(*md_msg)))) {
00547             ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00548             return NULL;
00549          }
00550          
00551          ASTOBJ_INIT(md_msg);
00552 
00553          /* read the message desk number */
00554          for (i = 0; i < sizeof(md_msg->mesg_desk_num) - 1; i++) {
00555             md_msg->mesg_desk_num[i] = fgetc(iface->file);
00556             ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_num[i]);
00557          }
00558 
00559          md_msg->mesg_desk_num[sizeof(md_msg->mesg_desk_num) - 1] = '\0';
00560          
00561          ast_log(LOG_DEBUG, "The message desk number is '%s'\n", md_msg->mesg_desk_num);
00562 
00563          /* read the message desk terminal number */
00564          for (i = 0; i < sizeof(md_msg->mesg_desk_term) - 1; i++) {
00565             md_msg->mesg_desk_term[i] = fgetc(iface->file);
00566             ast_log(LOG_DEBUG, "Read a '%c'\n", md_msg->mesg_desk_term[i]);
00567          }
00568 
00569          md_msg->mesg_desk_term[sizeof(md_msg->mesg_desk_term) - 1] = '\0';
00570 
00571          ast_log(LOG_DEBUG, "The message desk terminal is '%s'\n", md_msg->mesg_desk_term);
00572 
00573          /* read the message type */
00574          md_msg->type = fgetc(iface->file);
00575        
00576          ast_log(LOG_DEBUG, "Message type is '%c'\n", md_msg->type);
00577 
00578          /* read the forwarding station number (may be blank) */
00579          cp = &md_msg->fwd_st[0];
00580          for (i = 0; i < sizeof(md_msg->fwd_st) - 1; i++) {
00581             if ((c = fgetc(iface->file)) == ' ') {
00582                *cp = '\0';
00583                ast_log(LOG_DEBUG, "Read a space, done looking for the forwarding station\n");
00584                break;
00585             }
00586 
00587             /* store c in md_msg->fwd_st */
00588             if (i >= iface->msdstrip) {
00589                ast_log(LOG_DEBUG, "Read a '%c' and stored it in the forwarding station buffer\n", c);
00590                *cp++ = c;
00591             } else {
00592                ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the fwd station buffer, because of the msdstrip setting (%d < %d)\n", c, i, iface->msdstrip);
00593             }
00594          }
00595 
00596          /* make sure the value is null terminated, even if this truncates it */
00597          md_msg->fwd_st[sizeof(md_msg->fwd_st) - 1] = '\0';
00598          cp = NULL;
00599 
00600          ast_log(LOG_DEBUG, "The forwarding station is '%s'\n", md_msg->fwd_st);
00601 
00602          /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
00603           * up a message on this field */
00604          ast_copy_string(md_msg->name, md_msg->fwd_st, sizeof(md_msg->name));
00605 
00606          /* read the calling station number (may be blank) */
00607          cp = &md_msg->calling_st[0];
00608          for (i = 0; i < sizeof(md_msg->calling_st) - 1; i++) {
00609             if (!isdigit((c = fgetc(iface->file)))) {
00610                *cp = '\0';
00611                ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the calling station buffer because it's not a digit\n", c);
00612                if (c == ' ') {
00613                   /* Don't break on a space.  We may read the space before the calling station
00614                    * here if the forwarding station buffer filled up. */
00615                   i--; /* We're still on the same character */
00616                   continue;
00617                }
00618                break;
00619             }
00620 
00621             /* store c in md_msg->calling_st */
00622             if (i >= iface->msdstrip) {
00623                ast_log(LOG_DEBUG, "Read a '%c' and stored it in the calling station buffer\n", c);
00624                *cp++ = c;
00625             } else {
00626                ast_log(LOG_DEBUG, "Read a '%c', but didn't store it in the calling station buffer, because of the msdstrip setting (%d < %d)\n", c, i, iface->msdstrip);
00627             }
00628          }
00629 
00630          /* make sure the value is null terminated, even if this truncates it */
00631          md_msg->calling_st[sizeof(md_msg->calling_st) - 1] = '\0';
00632          cp = NULL;
00633 
00634          ast_log(LOG_DEBUG, "The calling station is '%s'\n", md_msg->calling_st);
00635 
00636          /* add the message to the message queue */
00637          md_msg->timestamp = ast_tvnow();
00638          ast_smdi_md_message_push(iface, md_msg);
00639          ast_log(LOG_DEBUG, "Recieved SMDI MD message on %s\n", iface->name);
00640          
00641          ASTOBJ_UNREF(md_msg, ast_smdi_md_message_destroy);
00642 
00643       } else if (c == 'W') { /* MWI message */
00644          start = 0;
00645 
00646          ast_log(LOG_DEBUG, "Read a 'W', it's an MWI message. (No more debug coming for MWI messages)\n");
00647 
00648          if (!(mwi_msg = ast_calloc(1, sizeof(*mwi_msg)))) {
00649             ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00650             return NULL;
00651          }
00652 
00653          ASTOBJ_INIT(mwi_msg);
00654 
00655          /* discard the 'I' (from 'MWI') */
00656          fgetc(iface->file);
00657          
00658          /* read the forwarding station number (may be blank) */
00659          cp = &mwi_msg->fwd_st[0];
00660          for (i = 0; i < sizeof(mwi_msg->fwd_st) - 1; i++) {
00661             if ((c = fgetc(iface->file)) == ' ') {
00662                *cp = '\0';
00663                break;
00664             }
00665 
00666             /* store c in md_msg->fwd_st */
00667             if (i >= iface->msdstrip)
00668                *cp++ = c;
00669          }
00670 
00671          /* make sure the station number is null terminated, even if this will truncate it */
00672          mwi_msg->fwd_st[sizeof(mwi_msg->fwd_st) - 1] = '\0';
00673          cp = NULL;
00674          
00675          /* Put the fwd_st in the name field so that we can use ASTOBJ_FIND to look
00676           * up a message on this field */
00677          ast_copy_string(mwi_msg->name, mwi_msg->fwd_st, sizeof(mwi_msg->name));
00678 
00679          /* read the mwi failure cause */
00680          for (i = 0; i < sizeof(mwi_msg->cause) - 1; i++)
00681             mwi_msg->cause[i] = fgetc(iface->file);
00682 
00683          mwi_msg->cause[sizeof(mwi_msg->cause) - 1] = '\0';
00684 
00685          /* add the message to the message queue */
00686          mwi_msg->timestamp = ast_tvnow();
00687          ast_smdi_mwi_message_push(iface, mwi_msg);
00688          ast_log(LOG_DEBUG, "Recieved SMDI MWI message on %s\n", iface->name);
00689          
00690          ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
00691       } else {
00692          ast_log(LOG_ERROR, "Unknown SMDI message type recieved on %s (M%c).\n", iface->name, c);
00693          start = 0;
00694       }
00695    }
00696 
00697    ast_log(LOG_ERROR, "Error reading from SMDI interface %s, stopping listener thread\n", iface->name);
00698    ASTOBJ_UNREF(iface,ast_smdi_interface_destroy);
00699    return NULL;
00700 }
00701 
00702 void ast_smdi_md_message_destroy(struct ast_smdi_md_message *msg)
00703 {
00704    free(msg);
00705 }
00706 
00707 void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
00708 {
00709    free(msg);
00710 }
00711 
00712 static void destroy_mailbox_mapping(struct mailbox_mapping *mm)
00713 {
00714    ast_string_field_free_memory(mm);
00715    ASTOBJ_UNREF(mm->iface, ast_smdi_interface_destroy);
00716    free(mm);
00717 }
00718 
00719 static void destroy_all_mailbox_mappings(void)
00720 {
00721    struct mailbox_mapping *mm;
00722 
00723    ast_mutex_lock(&mwi_monitor.lock);
00724    while ((mm = AST_LIST_REMOVE_HEAD(&mwi_monitor.mailbox_mappings, entry)))
00725       destroy_mailbox_mapping(mm);
00726    ast_mutex_unlock(&mwi_monitor.lock);
00727 }
00728 
00729 static void append_mailbox_mapping(struct ast_variable *var, struct ast_smdi_interface *iface)
00730 {
00731    struct mailbox_mapping *mm;
00732    char *mailbox, *context;
00733 
00734    if (!(mm = ast_calloc(1, sizeof(*mm))))
00735       return;
00736    
00737    if (ast_string_field_init(mm, 32)) {
00738       free(mm);
00739       return;
00740    }
00741 
00742    ast_string_field_set(mm, smdi, var->name);
00743 
00744    context = ast_strdupa(var->value);
00745    mailbox = strsep(&context, "@");
00746    if (ast_strlen_zero(context))
00747       context = "default";
00748 
00749    ast_string_field_set(mm, mailbox, mailbox);
00750    ast_string_field_set(mm, context, context);
00751 
00752    mm->iface = ASTOBJ_REF(iface);
00753 
00754    ast_mutex_lock(&mwi_monitor.lock);
00755    AST_LIST_INSERT_TAIL(&mwi_monitor.mailbox_mappings, mm, entry);
00756    ast_mutex_unlock(&mwi_monitor.lock);
00757 }
00758 
00759 /*!
00760  * \note Called with the mwi_monitor.lock locked
00761  */
00762 static void poll_mailbox(struct mailbox_mapping *mm)
00763 {
00764    char buf[1024];
00765    unsigned int state;
00766 
00767    snprintf(buf, sizeof(buf), "%s@%s", mm->mailbox, mm->context);
00768 
00769    state = !!ast_app_has_voicemail(mm->mailbox, NULL);
00770 
00771    if (state != mm->cur_state) {
00772       if (state)
00773          ast_smdi_mwi_set(mm->iface, mm->smdi);
00774       else
00775          ast_smdi_mwi_unset(mm->iface, mm->smdi);
00776 
00777       mm->cur_state = state;
00778    }
00779 }
00780 
00781 static void *mwi_monitor_handler(void *data)
00782 {
00783    while (!mwi_monitor.stop) {
00784       struct timespec ts = { 0, };
00785       struct timeval tv;
00786       struct mailbox_mapping *mm;
00787 
00788       ast_mutex_lock(&mwi_monitor.lock);
00789 
00790       mwi_monitor.last_poll = ast_tvnow();
00791 
00792       AST_LIST_TRAVERSE(&mwi_monitor.mailbox_mappings, mm, entry)
00793          poll_mailbox(mm);
00794 
00795       /* Sleep up to the configured polling interval.  Allow unload_module()
00796        * to signal us to wake up and exit. */
00797       tv = ast_tvadd(mwi_monitor.last_poll, ast_tv(mwi_monitor.polling_interval, 0));
00798       ts.tv_sec = tv.tv_sec;
00799       ts.tv_nsec = tv.tv_usec * 1000;
00800       ast_cond_timedwait(&mwi_monitor.cond, &mwi_monitor.lock, &ts);
00801 
00802       ast_mutex_unlock(&mwi_monitor.lock);
00803    }
00804 
00805    return NULL;
00806 }
00807 
00808 static struct ast_smdi_interface *alloc_smdi_interface(void)
00809 {
00810    struct ast_smdi_interface *iface;
00811 
00812    if (!(iface = ast_calloc(1, sizeof(*iface))))
00813       return NULL;
00814 
00815    ASTOBJ_INIT(iface);
00816    ASTOBJ_CONTAINER_INIT(&iface->md_q);
00817    ASTOBJ_CONTAINER_INIT(&iface->mwi_q);
00818 
00819    ast_mutex_init(&iface->md_q_lock);
00820    ast_cond_init(&iface->md_q_cond, NULL);
00821 
00822    ast_mutex_init(&iface->mwi_q_lock);
00823    ast_cond_init(&iface->mwi_q_cond, NULL);
00824 
00825    return iface;
00826 }
00827 
00828 /*!
00829  * \internal
00830  * \brief Load and reload SMDI configuration.
00831  * \param reload this should be 1 if we are reloading and 0 if not.
00832  *
00833  * This function loads/reloads the SMDI configuration and starts and stops
00834  * interfaces accordingly.
00835  *
00836  * \return zero on success, -1 on failure, and 1 if no smdi interfaces were started.
00837  */
00838 static int smdi_load(int reload)
00839 {
00840    struct ast_config *conf;
00841    struct ast_variable *v;
00842    struct ast_smdi_interface *iface = NULL;
00843    int res = 0;
00844 
00845    /* Config options */
00846    speed_t baud_rate = B9600;     /* 9600 baud rate */
00847    tcflag_t paritybit = PARENB;   /* even parity checking */
00848    tcflag_t charsize = CS7;       /* seven bit characters */
00849    int stopbits = 0;              /* One stop bit */
00850    
00851    int msdstrip = 0;              /* strip zero digits */
00852    long msg_expiry = SMDI_MSG_EXPIRY_TIME;
00853    
00854    conf = ast_config_load(config_file);
00855 
00856    if (!conf) {
00857       if (reload)
00858          ast_log(LOG_NOTICE, "Unable to reload config %s: SMDI untouched\n", config_file);
00859       else
00860          ast_log(LOG_NOTICE, "Unable to load config %s: SMDI disabled\n", config_file);
00861       return 1;
00862    }
00863 
00864    /* Mark all interfaces that we are listening on.  We will unmark them
00865     * as we find them in the config file, this way we know any interfaces
00866     * still marked after we have finished parsing the config file should
00867     * be stopped.
00868     */
00869    if (reload)
00870       ASTOBJ_CONTAINER_MARKALL(&smdi_ifaces);
00871 
00872    for (v = ast_variable_browse(conf, "interfaces"); v; v = v->next) {
00873       if (!strcasecmp(v->name, "baudrate")) {
00874          if (!strcasecmp(v->value, "9600"))
00875             baud_rate = B9600;
00876          else if (!strcasecmp(v->value, "4800"))
00877             baud_rate = B4800;
00878          else if (!strcasecmp(v->value, "2400"))
00879             baud_rate = B2400;
00880          else if (!strcasecmp(v->value, "1200"))
00881             baud_rate = B1200;
00882          else {
00883             ast_log(LOG_NOTICE, "Invalid baud rate '%s' specified in %s (line %d), using default\n", v->value, config_file, v->lineno);
00884             baud_rate = B9600;
00885          }
00886       } else if (!strcasecmp(v->name, "msdstrip")) {
00887          if (!sscanf(v->value, "%d", &msdstrip)) {
00888             ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
00889             msdstrip = 0;
00890          } else if (0 > msdstrip || msdstrip > 9) {
00891             ast_log(LOG_NOTICE, "Invalid msdstrip value in %s (line %d), using default\n", config_file, v->lineno);
00892             msdstrip = 0;
00893          }
00894       } else if (!strcasecmp(v->name, "msgexpirytime")) {
00895          if (!sscanf(v->value, "%ld", &msg_expiry)) {
00896             ast_log(LOG_NOTICE, "Invalid msgexpirytime value in %s (line %d), using default\n", config_file, v->lineno);
00897             msg_expiry = SMDI_MSG_EXPIRY_TIME;
00898          }
00899       } else if (!strcasecmp(v->name, "paritybit")) {
00900          if (!strcasecmp(v->value, "even"))
00901             paritybit = PARENB;
00902          else if (!strcasecmp(v->value, "odd"))
00903             paritybit = PARENB | PARODD;
00904          else if (!strcasecmp(v->value, "none"))
00905             paritybit = ~PARENB;
00906          else {
00907             ast_log(LOG_NOTICE, "Invalid parity bit setting in %s (line %d), using default\n", config_file, v->lineno);
00908             paritybit = PARENB;
00909          }
00910       } else if (!strcasecmp(v->name, "charsize")) {
00911          if (!strcasecmp(v->value, "7"))
00912             charsize = CS7;
00913          else if (!strcasecmp(v->value, "8"))
00914             charsize = CS8;
00915          else {
00916             ast_log(LOG_NOTICE, "Invalid character size setting in %s (line %d), using default\n", config_file, v->lineno);
00917             charsize = CS7;
00918          }
00919       } else if (!strcasecmp(v->name, "twostopbits")) {
00920          stopbits = ast_true(v->name);
00921       } else if (!strcasecmp(v->name, "smdiport")) {
00922          if (reload) {
00923             /* we are reloading, check if we are already
00924              * monitoring this interface, if we are we do
00925              * not want to start it again.  This also has
00926              * the side effect of not updating different
00927              * setting for the serial port, but it should
00928              * be trivial to rewrite this section so that
00929              * options on the port are changed without
00930              * restarting the interface.  Or the interface
00931              * could be restarted with out emptying the
00932              * queue. */
00933             if ((iface = ASTOBJ_CONTAINER_FIND(&smdi_ifaces, v->value))) {
00934                ast_log(LOG_NOTICE, "SMDI interface %s already running, not restarting\n", iface->name);
00935                ASTOBJ_UNMARK(iface);
00936                ASTOBJ_UNREF(iface, ast_smdi_interface_destroy);
00937                continue;
00938