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

Generated on Mon Mar 31 07:38:05 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1