Mon Mar 19 11:30:30 2012

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

Generated on Mon Mar 19 11:30:30 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7