Merge a large set of updates to the Asterisk indications API.
authorRussell Bryant <russell@russellbryant.com>
Tue, 17 Feb 2009 20:41:24 +0000 (20:41 +0000)
committerRussell Bryant <russell@russellbryant.com>
Tue, 17 Feb 2009 20:41:24 +0000 (20:41 +0000)
This patch includes a number of changes to the indications API.  The primary
motivation for this work was to improve stability.  The object management
in this API was significantly flawed, and a number of trivial situations could
cause crashes.

The changes included are:

1) Remove the module res_indications.  This included the critical functionality
   that actually loaded the indications configuration.  I have seen many people
   have Asterisk problems because they accidentally did not have an
   indications.conf present and loaded.  Now, this code is in the core,
   and Asterisk will fail to start without indications configuration.

   There was one part of res_indications, the dialplan applications, which did
   belong in a module, and have been moved to a new module, app_playtones.

2) Object management has been significantly changed.  Tone zones are now
   managed using astobj2, and it is no longer possible to crash Asterisk by
   issuing a reload that destroys tone zones while they are in use.

3) The API documentation has been filled out.

4) The API has been updated to follow our naming conventions.

5) Various bits of code throughout the tree have been updated to account
   for the API update.

6) Configuration parsing has been mostly re-written.

7) "Code cleanup"

The code is from svn/asterisk/team/russell/indications/.

Review: http://reviewboard.digium.com/r/149/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@176627 65c4cc65-6c06-0410-ace0-fbb531ad65f3

21 files changed:
UPGRADE.txt
apps/app_disa.c
apps/app_playtones.c [new file with mode: 0644]
apps/app_read.c
apps/app_readexten.c
channels/chan_misdn.c
channels/chan_skinny.c
channels/chan_unistim.c
configs/indications.conf.sample
funcs/func_channel.c
include/asterisk/_private.h
include/asterisk/channel.h
include/asterisk/indications.h
main/app.c
main/asterisk.c
main/channel.c
main/indications.c
main/loader.c
main/pbx.c
res/res_indications.c [deleted file]
res/snmp/agent.c

index ccfe1c5..2479ed1 100644 (file)
 ===
 ===========================================================
 
+From 1.6.1 to 1.6.2:
+
+* The res_indications module has been removed.  Its functionality was important
+  enough that most of it has been moved into the Asterisk core.
+  Two applications previously provided by res_indications, PlayTones and
+  StopPlayTones, have been moved into a new module, app_playtones.
+
 From 1.6.0.1 to 1.6.1:
 
 * The ast_agi_register_multiple() and ast_agi_unregister_multiple()
index e5814dd..691fa94 100644 (file)
@@ -124,15 +124,20 @@ AST_APP_OPTIONS(app_opts, {
 
 static void play_dialtone(struct ast_channel *chan, char *mailbox)
 {
-       const struct tone_zone_sound *ts = NULL;
-       if(ast_app_has_voicemail(mailbox, NULL))
+       struct ast_tone_zone_sound *ts = NULL;
+
+       if (ast_app_has_voicemail(mailbox, NULL)) {
                ts = ast_get_indication_tone(chan->zone, "dialrecall");
-       else
+       } else {
                ts = ast_get_indication_tone(chan->zone, "dial");
-       if (ts)
+       }
+
+       if (ts) {
                ast_playtones_start(chan, 0, ts->data, 0);
-       else
+               ts = ast_tone_zone_sound_unref(ts);
+       } else {
                ast_tonepair_start(chan, 350, 440, 0, 0);
+       }
 }
 
 static int disa_exec(struct ast_channel *chan, void *data)
diff --git a/apps/app_playtones.c b/apps/app_playtones.c
new file mode 100644 (file)
index 0000000..6aff168
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Russell Bryant <russell@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Playtones application
+ *
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/channel.h"
+#include "asterisk/indications.h"
+
+static const char playtones_app[] = "PlayTones";
+static const char stopplaytones_app[] = "StopPlayTones";
+
+/*** DOCUMENTATION
+       <application name="PlayTones" language="en_US">
+               <synopsis>
+                       Play a tone list.
+               </synopsis>
+               <syntax>
+                       <parameter name="arg" required="true">
+                               <para>Arg is either the tone name defined in the <filename>indications.conf</filename>
+                               configuration file, or a directly specified list of frequencies and durations.</para>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>Plays a tone list. Execution will continue with the next step in the dialplan
+                       immediately while the tones continue to play.</para>
+                       <para>See the sample <filename>indications.conf</filename> for a description of the
+                       specification of a tonelist.</para>
+               </description>
+               <see-also>
+                       <ref type="application">StopPlayTones</ref>
+               </see-also>
+       </application>
+       <application name="StopPlayTones" language="en_US">
+               <synopsis>
+                       Stop playing a tone list.
+               </synopsis>
+               <syntax />
+               <description>
+                       <para>Stop playing a tone list, initiated by PlayTones().</para>
+               </description>
+               <see-also>
+                       <ref type="application">PlayTones</ref>
+               </see-also>
+       </application>
+ ***/
+
+static int handle_playtones(struct ast_channel *chan, void *data)
+{
+       struct ast_tone_zone_sound *ts;
+       int res;
+       const char *str = data;
+
+       if (ast_strlen_zero(str)) {
+               ast_log(LOG_NOTICE,"Nothing to play\n");
+               return -1;
+       }
+
+       ts = ast_get_indication_tone(chan->zone, str);
+
+       if (ts) {
+               res = ast_playtones_start(chan, 0, ts->data, 0);
+               ts = ast_tone_zone_sound_unref(ts);
+       } else {
+               res = ast_playtones_start(chan, 0, str, 0);
+       }
+
+       if (res) {
+               ast_log(LOG_NOTICE, "Unable to start playtones\n");
+       }
+
+       return res;
+}
+
+static int handle_stopplaytones(struct ast_channel *chan, void *data)
+{
+       ast_playtones_stop(chan);
+
+       return 0;
+}
+
+static int unload_module(void)
+{
+       int res;
+
+       res = ast_unregister_application(playtones_app);
+       res |= ast_unregister_application(stopplaytones_app);
+
+       return res;
+}
+
+static int load_module(void)
+{
+       int res;
+
+       res = ast_register_application_xml(playtones_app, handle_playtones);
+       res |= ast_register_application_xml(stopplaytones_app, handle_stopplaytones);
+
+       return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Playtones Application");
index 6fb00b7..6b4088c 100644 (file)
@@ -132,7 +132,7 @@ static int read_exec(struct ast_channel *chan, void *data)
        int tries = 1, to = 0, x = 0;
        double tosec;
        char *argcopy = NULL;
-       struct tone_zone_sound *ts = NULL;
+       struct ast_tone_zone_sound *ts = NULL;
        struct ast_flags flags = {0};
        const char *status = "ERROR";
 
@@ -188,7 +188,7 @@ static int read_exec(struct ast_channel *chan, void *data)
                return 0;
        }
        if (ast_test_flag(&flags, OPT_INDICATION)) {
-               if (! ast_strlen_zero(arglist.filename)) {
+               if (!ast_strlen_zero(arglist.filename)) {
                        ts = ast_get_indication_tone(chan->zone, arglist.filename);
                }
        }
@@ -258,6 +258,10 @@ static int read_exec(struct ast_channel *chan, void *data)
                }
        }
 
+       if (ts) {
+               ts = ast_tone_zone_sound_unref(ts);
+       }
+
        if (ast_check_hangup(chan))
                status = "HANGUP";
        pbx_builtin_setvar_helper(chan, "READSTATUS", status);
index 5649a34..d5075ed 100644 (file)
@@ -132,7 +132,7 @@ static int readexten_exec(struct ast_channel *chan, void *data)
        int maxdigits = sizeof(exten) - 1;
        int timeout = 0, digit_timeout = 0, x = 0;
        char *argcopy = NULL, *status = "";
-       struct tone_zone_sound *ts = NULL;
+       struct ast_tone_zone_sound *ts = NULL;
        struct ast_flags flags = {0};
 
         AST_DECLARE_APP_ARGS(arglist,
@@ -179,8 +179,9 @@ static int readexten_exec(struct ast_channel *chan, void *data)
        if (digit_timeout <= 0)
                digit_timeout = chan->pbx ? chan->pbx->dtimeoutms : 5000;
 
-       if (ast_test_flag(&flags, OPT_INDICATION) && !ast_strlen_zero(arglist.filename))
+       if (ast_test_flag(&flags, OPT_INDICATION) && !ast_strlen_zero(arglist.filename)) {
                ts = ast_get_indication_tone(chan->zone, arglist.filename);
+       }
 
        do {
                if (chan->_state != AST_STATE_UP) {
@@ -250,6 +251,10 @@ static int readexten_exec(struct ast_channel *chan, void *data)
                }
        } while (0);
 
+       if (ts) {
+               ts = ast_tone_zone_sound_unref(ts);
+       }
+
        pbx_builtin_setvar_helper(chan, "READEXTENSTATUS", status);
 
        return status[0] == 'H' ? -1 : 0;
index 7fab5ba..a1df2b7 100644 (file)
@@ -379,7 +379,7 @@ struct chan_list {
         * \brief Tone zone sound used for dialtone generation.
         * \note Used as a boolean.  Non-NULL to prod generation if enabled. 
         */
-       const struct tone_zone_sound *ts;
+       struct ast_tone_zone_sound *ts;
        
        /*!
         * \brief Enables overlap dialing for the set amount of seconds.  (0 = Disabled)
@@ -3357,7 +3357,6 @@ static enum ast_bridge_result  misdn_bridge (struct ast_channel *c0,
 
 static int dialtone_indicate(struct chan_list *cl)
 {
-       const struct tone_zone_sound *ts = NULL;
        struct ast_channel *ast = cl->ast;
        int nd = 0;
 
@@ -3374,14 +3373,14 @@ static int dialtone_indicate(struct chan_list *cl)
        }
        
        chan_misdn_log(3, cl->bc->port, " --> Dial\n");
-       ts = ast_get_indication_tone(ast->zone, "dial");
-       cl->ts = ts;    
+
+       cl->ts = ast_get_indication_tone(ast->zone, "dial");
        
-       if (ts) {
+       if (cl->ts) {
                cl->notxtone = 0;
                cl->norxtone = 0;
                /* This prods us in misdn_write */
-               ast_playtones_start(ast, 0, ts->data, 0);
+               ast_playtones_start(ast, 0, cl->ts->data, 0);
        }
 
        return 0;
@@ -3406,8 +3405,9 @@ static int stop_indicate(struct chan_list *cl)
        misdn_lib_tone_generator_stop(cl->bc);
        ast_playtones_stop(ast);
 
-       cl->ts = NULL;
-       /*ast_deactivate_generator(ast);*/
+       if (cl->ts) {
+               cl->ts = ast_tone_zone_sound_unref(cl->ts);
+       }
 
        return 0;
 }
index fddd781..d9e9163 100644 (file)
@@ -3751,7 +3751,7 @@ static int skinny_transfer(struct skinny_subchannel *sub)
 {
        struct skinny_subchannel *xferor; /* the sub doing the transferring */
        struct skinny_subchannel *xferee; /* the sub being transferred */
-       const struct tone_zone_sound *ts = NULL;
+       struct ast_tone_zone_sound *ts = NULL;
                
        if (ast_bridged_channel(sub->owner) || ast_bridged_channel(sub->related->owner)) {
                if (sub->xferor) {
@@ -3774,8 +3774,10 @@ static int skinny_transfer(struct skinny_subchannel *sub)
                        }
                        if (xferor->owner->_state == AST_STATE_RING) {
                                /* play ringing inband */
-                               ts = ast_get_indication_tone(xferor->owner->zone, "ring");
-                               ast_playtones_start(xferor->owner, 0, ts->data, 1);
+                               if ((ts = ast_get_indication_tone(xferor->owner->zone, "ring"))) {
+                                       ast_playtones_start(xferor->owner, 0, ts->data, 1);
+                                       ts = ast_tone_zone_sound_unref(ts);
+                               }
                        }
                        if (skinnydebug)
                                ast_debug(1, "Transfer Masquerading %s to %s\n",
@@ -3789,8 +3791,10 @@ static int skinny_transfer(struct skinny_subchannel *sub)
                        ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
                        if (xferor->owner->_state == AST_STATE_RING) {
                                /* play ringing inband */
-                               ts = ast_get_indication_tone(xferor->owner->zone, "ring");
-                               ast_playtones_start(xferor->owner, 0, ts->data, 1);
+                               if ((ts = ast_get_indication_tone(xferor->owner->zone, "ring"))) {
+                                       ast_playtones_start(xferor->owner, 0, ts->data, 1);
+                                       ts = ast_tone_zone_sound_unref(ts);
+                               }
                        }
                        if (skinnydebug)
                                ast_debug(1, "Transfer Masquerading %s to %s\n",
index 48d11e4..8229c9a 100644 (file)
@@ -439,7 +439,7 @@ static struct unistim_device {
        char datetimeformat;        /*!< format used for displaying time/date */
        char contrast;                    /*!< contrast */
        char country[3];                        /*!< country used for dial tone frequency */
-       struct tone_zone *tz;          /*!< Tone zone for res_indications (ring, busy, congestion) */
+       struct ast_tone_zone *tz;              /*!< Tone zone for res_indications (ring, busy, congestion) */
        char ringvolume;                        /*!< Ring volume */
        char ringstyle;                  /*!< Ring melody */
        int rtp_port;                      /*!< RTP port used by the phone */
@@ -4057,17 +4057,17 @@ static char *control2str(int ind)
        return "UNKNOWN";
 }
 
-static void in_band_indication(struct ast_channel *ast, const struct tone_zone *tz,
+static void in_band_indication(struct ast_channel *ast, const struct ast_tone_zone *tz,
        const char *indication)
 {
-       const struct tone_zone_sound *ts = NULL;
+       struct ast_tone_zone_sound *ts = NULL;
 
-       ts = ast_get_indication_tone(tz, indication);
-
-       if (ts && ts->data[0])
+       if ((ts = ast_get_indication_tone(tz, indication))) {
                ast_playtones_start(ast, 0, ts->data, 1);
-       else
+               ts = ast_tone_zone_sound_unref(ts);
+       } else {
                ast_log(LOG_WARNING, "Unable to get indication tone for %s\n", indication);
+       }
 }
 
 static int unistim_indicate(struct ast_channel *ast, int ind, const void *data, 
@@ -5223,6 +5223,9 @@ static struct unistim_device *build_device(const char *cat, const struct ast_var
                ast_log(LOG_ERROR, "An Unistim device must have at least one line!\n");
                ast_mutex_destroy(&l->lock);
                ast_free(l);
+               if (d->tz) {
+                       d->tz = ast_tone_zone_unref(d->tz);
+               }
                ast_free(d);
                return NULL;
        }
@@ -5240,6 +5243,9 @@ static struct unistim_device *build_device(const char *cat, const struct ast_var
                        ast_log(LOG_ERROR, "You must specify the mac address with device=\n");
                        ast_mutex_destroy(&l->lock);
                        ast_free(l);
+                       if (d->tz) {
+                               d->tz = ast_tone_zone_unref(d->tz);
+                       }
                        ast_free(d);
                        return NULL;
                } else
@@ -5461,6 +5467,9 @@ static int reload_config(void)
                                        d2 = d2->next;
                                }
                        }
+                       if (d->tz) {
+                               d->tz = ast_tone_zone_unref(d->tz);
+                       }
                        ast_free(d);
                        d = devices;
                        continue;
index d7088db..239dcd1 100644 (file)
@@ -1,6 +1,9 @@
+;
 ; indications.conf
+;
 ; Configuration file for location specific tone indications
-; used by the pbx_indications module.
+;
+
 ;
 ; NOTE:
 ;    When adding countries to this file, please keep them in alphabetical
@@ -9,7 +12,7 @@
 ; The [general] category is for certain global variables.
 ; All other categories are interpreted as location specific indications
 ;
-;
+
 [general]
 country=us             ; default location
 
@@ -17,9 +20,6 @@ country=us            ; default location
 ; [example]
 ; description = string
 ;      The full name of your country, in English.
-; alias = iso[,iso]*
-;      List of other countries 2-letter iso codes, which have the same
-;      tone indications.
 ; ringcadence = num[,num]*
 ;      List of durations the physical bell rings.
 ; dial = tonelist
@@ -56,8 +56,6 @@ country=us            ; default location
 ;   element = [!]freq[+|*freq2][/duration]
 ;   tonelist = element[,element]*
 ;
-; Please note that SPACES ARE NOT ALLOWED in tone lists!
-;
 
 [at]
 description = Austria
index 959f332..ff8eeb2 100644 (file)
@@ -296,12 +296,19 @@ static int func_channel_write(struct ast_channel *chan, const char *function,
        }
 #endif
        else if (!strcasecmp(data, "tonezone")) {
-               struct tone_zone *new_zone;
+               struct ast_tone_zone *new_zone;
                if (!(new_zone = ast_get_indication_zone(value))) {
                        ast_log(LOG_ERROR, "Unknown country code '%s' for tonezone. Check indications.conf for available country codes.\n", value);
                        ret = -1;       
-               } else 
-                       chan->zone = new_zone;
+               } else {
+                       ast_channel_lock(chan);
+                       if (chan->zone) {
+                               chan->zone = ast_tone_zone_unref(chan->zone);
+                       }
+                       chan->zone = ast_tone_zone_ref(new_zone);
+                       ast_channel_unlock(chan);
+                       new_zone = ast_tone_zone_unref(new_zone);
+               }
        } else if (!strcasecmp(data, "callgroup"))
                chan->callgroup = ast_get_group(value);
        else if (!strcasecmp(data, "txgain")) {
index 9885702..34c87c7 100644 (file)
@@ -39,6 +39,8 @@ int ast_http_init(void);              /*!< Provided by http.c */
 int ast_http_reload(void);             /*!< Provided by http.c */
 int ast_tps_init(void);                /*!< Provided by taskprocessor.c */
 int ast_timing_init(void);             /*!< Provided by timing.c */
+int ast_indications_init(void); /*!< Provided by indications.c */
+int ast_indications_reload(void);/*!< Provided by indications.c */
 
 /*!
  * \brief Reload asterisk modules.
index 183a36b..04769e7 100644 (file)
@@ -424,7 +424,7 @@ struct ast_channel {
        struct ast_trans_pvt *readtrans;                /*!< Read translation path */
        struct ast_audiohook_list *audiohooks;
        struct ast_cdr *cdr;                            /*!< Call Detail Record */
-       struct tone_zone *zone;                 /*!< Tone zone as set in indications.conf or
+       struct ast_tone_zone *zone;                     /*!< Tone zone as set in indications.conf or
                                                             in the CHANNEL dialplan function */
        struct ast_channel_monitor *monitor;            /*!< Channel monitoring */
 #ifdef HAVE_EPOLL
index b32fe50..bf2f323 100644 (file)
 /*
  * Asterisk -- An open source telephony toolkit.
  *
+ * Copyright (C) 2002, Pauline Middelink
+ * Copyright (C) 2009, Digium, Inc.
+ *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
  * any of the maintainers of this project for assistance;
  * the project provides a web site, mailing lists and IRC
  * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
  */
 
-/*! \file
- * \brief BSD Telephony Of Mexico "Tormenta" Tone Zone Support 2/22/01
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
- *
- * Primary Author: Pauline Middelink <middelink@polyware.nl>
+/*!
+ * \file
+ * \brief Tone Indication Support
  *
+ * \author Pauline Middelink <middelink@polyware.nl>
+ * \author Russell Bryant <russell@digium.com>
  */
 
 #ifndef _ASTERISK_INDICATIONS_H
 #define _ASTERISK_INDICATIONS_H
 
-#include "asterisk/lock.h"
-
-/*! \brief Description is a series of tones of the format:
-          [!]freq1[+freq2][/duration] separated by commas.  There
-          are no spaces.  The sequence is repeated back to the 
-          first tone description not preceeded by !. Duration is
-          specified in milliseconds */
-struct tone_zone_sound {
-       const char *name;                       /*!< Identifing name */
-       const char *data;                       /*!< Actual zone description */
-       AST_LIST_ENTRY(tone_zone_sound) list;
+#include "asterisk/astobj2.h"
+
+/*!
+ * \brief Description of a tone
+ */
+struct ast_tone_zone_sound {
+       /*! \brief Name of the tone.  For example, "busy". */
+       const char *name;
+       /*!
+        * \brief Description of a tone
+        *
+        * The format is a comma separated list of tone parts in the following format:
+        *
+        * Format: [!][M]freq[<+|*>freq2][/duration]
+        *  - '!' - means that the element is NOT repeated
+        *  - 'M' - interpret the frequencies as midi notes instead of frequencies
+        *  - freq - The first frequency
+        *  - freq2 - The second frequency (optional)
+        *  - '*' - modulate freq by freq2 at a fixed depth of 90%
+        *  - '+' - combine the frequencies
+        *  - duration - the length of the tone part (optional, forever if not specified)
+        */
+       const char *data;
+       /*! \brief Linked list fields for including in the list on an ast_tone_zone */
+       AST_LIST_ENTRY(ast_tone_zone_sound) entry;
+       /*! \brief Flags only used internally */
+       union {
+               uint32_t __padding;
+               struct {
+                       unsigned int killme:1;
+               };
+       };
+};
+
+/*!
+ * \brief A set of tones for a given locale
+ *
+ * \note If a reference to this tone zone is held, then the country
+ *       is guaranteed not to change.  It is safe to read it without
+ *       locking the tone zone.  This is not the case for any other
+ *       field.
+ */
+struct ast_tone_zone {
+       /*! \brief Country code that this set of tones is for */
+       char country[5];
+       /*! 
+        * \brief Text description of the given country.
+        *
+        * This is for nothing more than friendly display to a human.
+        */
+       char description[40];
+       /*! \brief Number of ring cadence elements in the ringcadence array */
+       unsigned int  nrringcadence;
+       /*! 
+        * \brief Array of ring cadence parts
+        *
+        * Each element is an amount of time in milliseconds.  The first element
+        * is for time on, and from there it alternates between on and off.
+        */
+       int *ringcadence;
+       /*! \brief A list of tones for this locale */
+       AST_LIST_HEAD_NOLOCK(, ast_tone_zone_sound) tones;
+       /*! \brief Flags only used internally */
+       union {
+               uint32_t __padding;
+               struct {
+                       unsigned int killme:1;
+               };
+       };
 };
 
-struct tone_zone {
-       AST_RWLIST_ENTRY(tone_zone) list;
-       char country[5];                                /*!< Country code */
-       char alias[5];                                  /*!< is this an alias? */
-       char description[40];                           /*!< Description */
-       int  nrringcadence;                             /*!< # registered ringcadence elements */
-       int *ringcadence;                               /*!< Ring cadence */
-       AST_LIST_HEAD_NOLOCK(, tone_zone_sound) tones;          /*!< The known tones for this zone */
+/*!
+ * \brief A description of a part of a tone
+ *
+ * The elements in this structure map to the format described for the data
+ * part of the ast_tone_zone_sound struct.
+ */
+struct ast_tone_zone_part {
+       unsigned int freq1;
+       unsigned int freq2;
+       unsigned int time;
+       unsigned int modulate:1;
+       unsigned int midinote:1;
 };
 
-/*! \brief set the default tone country */
-int ast_set_indication_country(const char *country);
-
-/*! \brief locate tone_zone, given the country. if country == NULL, use the default country */
-struct tone_zone *ast_get_indication_zone(const char *country);
-/*! \brief locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */
-struct tone_zone_sound *ast_get_indication_tone(const struct tone_zone *zone, const char *indication);
-/*! \brief deallocate the passed tone zone */
-void ast_destroy_indication_zone(struct tone_zone *zone);
-
-/*! \brief add a new country, if country exists, it will be replaced. */
-int ast_register_indication_country(struct tone_zone *country);
-/*! \brief remove an existing country and all its indications, country must exist */
-int ast_unregister_indication_country(const char *country);
-/*! \brief add a new indication to a tone_zone. tone_zone must exist. if the indication already
- * exists, it will be replaced. */
-int ast_register_indication(struct tone_zone *zone, const char *indication, const char *tonelist);
-/*! \brief remove an existing tone_zone's indication. tone_zone must exist */
-int ast_unregister_indication(struct tone_zone *zone, const char *indication);
-
-/*! \brief Start a tone-list going */
-int ast_playtones_start(struct ast_channel *chan, int vol, const char* tonelist, int interruptible);
-/*! \brief Stop the tones from playing */
+/*!
+ * \brief Parse a tone part
+ *
+ * \param s The part of a tone to parse.  This should be in the form described for
+ *        the data part of ast_tone_zone_sound.  '!' should be removed if present.
+ * \param tone_data An output parameter that contains the result of the parsing.
+ *
+ * \retval 0 success
+ * \retval -1 failure, and the contents of tone_data are undefined
+ */
+int ast_tone_zone_part_parse(const char *s, struct ast_tone_zone_part *tone_data);
+
+/*!
+ * \brief locate ast_tone_zone
+ *
+ * \param country country to find.  If NULL is provided, get the default.
+ *
+ * \return a reference to the specified country if found or NULL if not found
+ */
+struct ast_tone_zone *ast_get_indication_zone(const char *country);
+
+/*!
+ * \brief Locate a tone zone sound
+ *
+ * \param zone Zone to look in for a sound, if NULL, the default will be used
+ * \param indication Sound to look for, such as "busy"
+ *
+ * \return a reference to the specified sound if it exists, NULL if not
+ */
+struct ast_tone_zone_sound *ast_get_indication_tone(const struct ast_tone_zone *zone, const char *indication);
+
+/*!
+ * \brief Start playing a list of tones on a channel
+ *
+ * \param chan the channel to play tones on
+ * \param vol volume
+ * \param tonelist the list of tones to play, comma separated
+ * \param interruptible whether or not this tone can be interrupted
+ *
+ * \retval 0 success
+ * \retval non-zero failure
+ */
+int ast_playtones_start(struct ast_channel *chan, int vol, const char *tonelist, int interruptible);
+
+/*!
+ * \brief Stop playing tones on a channel
+ *
+ * \param chan the channel to stop tones on
+ */
 void ast_playtones_stop(struct ast_channel *chan);
 
-/*! \brief support for walking through a list of indications */
-struct tone_zone *ast_walk_indications(const struct tone_zone *cur);
+/*!
+ * \brief Get the number of registered tone zones
+ *
+ * \return the total number of registered tone zones
+ */
+int ast_tone_zone_count(void);
+
+/*!
+ * \brief Get an iterator for the available tone zones
+ *
+ * Use ao2_iterator_next() to iterate the tone zones.
+ *
+ * \return an initialized iterator
+ */
+struct ao2_iterator ast_tone_zone_iterator_init(void);
+
+extern struct ast_tone_zone __fake_tone_zone;
+extern struct ast_tone_zone_sound __fake_tone_zone_sound;
+
+#define AST_CHECK_TONE_ZONE(tz) do { \
+       (void) ((tz) == (&__fake_tone_zone)); \
+} while (0)
+
+#define AST_CHECK_TONE_ZONE_SOUND(ts) do { \
+       (void) ((ts) == (&__fake_tone_zone_sound)); \
+} while (0)
+
+/*!
+ * \brief Lock an ast_tone_zone
+ */
+#define ast_tone_zone_lock(tz) ao2_lock(tz)
+
+/*!
+ * \brief Unlock an ast_tone_zone
+ */
+#define ast_tone_zone_unlock(tz) ao2_unlock(tz)
 
-#if 0
-extern struct tone_zone *tone_zones;
-extern ast_mutex_t tzlock;
-#endif
+/*!
+ * \brief Trylock an ast_tone_zone
+ */
+#define ast_tone_zone_trylock(tz) ao2_trylock(tz)
+
+/*!
+ * \brief Release a reference to an ast_tone_zone
+ *
+ * \return NULL
+ */
+#define ast_tone_zone_unref(tz) ({  \
+       AST_CHECK_TONE_ZONE(tz); \
+       ao2_ref(tz, -1); \
+       (NULL); \
+})
+
+/*!
+ * \brief Increase the reference count on an ast_tone_zone
+ *
+ * \return The tone zone provided as an argument
+ */
+#define ast_tone_zone_ref(tz) ({ \
+       AST_CHECK_TONE_ZONE(tz); \
+       ao2_ref(tz, +1); \
+       (tz); \
+})
+
+/*!
+ * \brief Release a reference to an ast_tone_zone_sound
+ *
+ * \return NULL
+ */
+#define ast_tone_zone_sound_unref(ts) ({ \
+       AST_CHECK_TONE_ZONE_SOUND(ts); \
+       ao2_ref(ts, -1); \
+       (NULL); \
+})
+
+/*!
+ * \brief Increase the reference count on an ast_tone_zone_sound
+ *
+ * \return The tone zone sound provided as an argument
+ */
+#define ast_tone_zone_sound_ref(ts) ({ \
+       AST_CHECK_TONE_ZONE_SOUND(ts); \
+       ao2_ref(ts, +1); \
+       (ts); \
+})
 
 #endif /* _ASTERISK_INDICATIONS_H */
index 789425f..83939c5 100644 (file)
@@ -75,21 +75,24 @@ static AST_RWLIST_HEAD_STATIC(groups, ast_group_info);
 */
 int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout) 
 {
-       struct tone_zone_sound *ts;
+       struct ast_tone_zone_sound *ts;
        int res = 0, x = 0;
 
        if (maxlen > size)
                maxlen = size;
        
-       if (!timeout && chan->pbx)
+       if (!timeout && chan->pbx) {
                timeout = chan->pbx->dtimeoutms / 1000.0;
-       else if (!timeout)
+       } else if (!timeout) {
                timeout = 5;
+       }
 
-       if ((ts = ast_get_indication_tone(chan->zone, "dial")) && ts->data[0])
+       if ((ts = ast_get_indication_tone(chan->zone, "dial"))) {
                res = ast_playtones_start(chan, 0, ts->data, 0);
-       else 
+               ts = ast_tone_zone_sound_unref(ts);
+       } else {
                ast_log(LOG_NOTICE, "Huh....? no dial for indications?\n");
+       }
        
        for (x = strlen(collect); x < maxlen; ) {
                res = ast_waitfordigit(chan, timeout);
index 3567fba..d7808a6 100644 (file)
@@ -3566,6 +3566,11 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
+       if (ast_indications_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
+
        ast_features_init();
 
        if (init_framer()) {
index f094333..344b3a9 100644 (file)
@@ -1403,7 +1403,11 @@ void ast_channel_free(struct ast_channel *chan)
                ast_cdr_discard(chan->cdr);
                chan->cdr = NULL;
        }
-       
+
+       if (chan->zone) {
+               chan->zone = ast_tone_zone_unref(chan->zone);
+       }
+
        ast_mutex_destroy(&chan->lock_dont_use);
 
        ast_string_field_free_memory(chan);
@@ -2910,7 +2914,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
        /* By using an enum, we'll get compiler warnings for values not handled 
         * in switch statements. */
        enum ast_control_frame_type condition = _condition;
-       const struct tone_zone_sound *ts = NULL;
+       struct ast_tone_zone_sound *ts = NULL;
        int res = -1;
 
        ast_channel_lock(chan);
@@ -2981,10 +2985,11 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
                break;
        }
 
-       if (ts && ts->data[0]) {
+       if (ts) {
                /* We have a tone to play, yay. */
                ast_debug(1, "Driver for channel '%s' does not support indication %d, emulating it\n", chan->name, condition);
                ast_playtones_start(chan, 0, ts->data, 1);
+               ts = ast_tone_zone_sound_unref(ts);
                res = 0;
                chan->visible_indication = condition;
        }
index c96dc41..1fe01f1 100644 (file)
@@ -2,7 +2,7 @@
  * Asterisk -- An open source telephony toolkit.
  *
  * Copyright (C) 2002, Pauline Middelink
- *
+ * Copyright (C) 2009, Digium, Inc.
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
  * at the top of the source tree.
  */
 
-/*! \file
+/*!
+ * \file
+ * \brief Indication Tone Handling
  *
- * \brief Tone Management
- * 
  * \author Pauline Middelink <middelink@polyware.nl>
- *
- * This set of function allow us to play a list of tones on a channel.
- * Each element has two frequencies, which are mixed together and a
- * duration. For silence both frequencies can be set to 0.
- * The playtones can be given as a comma separated string.
- *
+ * \author Russell Bryant <russell@digium.com>
  */
 
 #include "asterisk.h"
@@ -40,22 +35,39 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/frame.h"
 #include "asterisk/channel.h"
 #include "asterisk/utils.h"
+#include "asterisk/cli.h"
+#include "asterisk/module.h"
+#include "asterisk/astobj2.h"
+
+#include "asterisk/_private.h" /* _init(), _reload() */
+
+/* Globals */
+static const char config[] = "indications.conf";
+
+static const int midi_tohz[128] = {
+       8,     8,     9,     9,     10,    10,    11,    12,    12,    13,
+       14,    15,    16,    17,    18,    19,    20,    21,    23,    24,
+       25,    27,    29,    30,    32,    34,    36,    38,    41,    43,
+       46,    48,    51,    55,    58,    61,    65,    69,    73,    77,
+       82,    87,    92,    97,    103,   110,   116,   123,   130,   138,
+       146,   155,   164,   174,   184,   195,   207,   220,   233,   246,
+       261,   277,   293,   311,   329,   349,   369,   391,   415,   440,
+       466,   493,   523,   554,   587,   622,   659,   698,   739,   783,
+       830,   880,   932,   987,   1046,  1108,  1174,  1244,  1318,  1396,
+       1479,  1567,  1661,  1760,  1864,  1975,  2093,  2217,  2349,  2489,
+       2637,  2793,  2959,  3135,  3322,  3520,  3729,  3951,  4186,  4434,
+       4698,  4978,  5274,  5587,  5919,  6271,  6644,  7040,  7458,  7902,
+       8372,  8869,  9397,  9956,  10548, 11175, 11839, 12543
+};
+
+static struct ao2_container *ast_tone_zones;
 
-static int midi_tohz[128] = {
-                       8,8,9,9,10,10,11,12,12,13,14,
-                       15,16,17,18,19,20,21,23,24,25,
-                       27,29,30,32,34,36,38,41,43,46,
-                       48,51,55,58,61,65,69,73,77,82,
-                       87,92,97,103,110,116,123,130,138,146,
-                       155,164,174,184,195,207,220,233,246,261,
-                       277,293,311,329,349,369,391,415,440,466,
-                       493,523,554,587,622,659,698,739,783,830,
-                       880,932,987,1046,1108,1174,1244,1318,1396,1479,
-                       1567,1661,1760,1864,1975,2093,2217,2349,2489,2637,
-                       2793,2959,3135,3322,3520,3729,3951,4186,4434,4698,
-                       4978,5274,5587,5919,6271,6644,7040,7458,7902,8372,
-                       8869,9397,9956,10548,11175,11839,12543
-                       };
+#define NUM_TONE_ZONE_BUCKETS 53
+
+/*!
+ * \note Access to this is protected by locking the ast_tone_zones container
+ */
+static struct ast_tone_zone *default_tone_zone;
 
 struct playtones_item {
        int fac1;
@@ -100,21 +112,26 @@ static void playtones_release(struct ast_channel *chan, void *params)
 {
        struct playtones_state *ps = params;
 
-       if (chan)
+       if (chan) {
                ast_set_write_format(chan, ps->origwfmt);
-       if (ps->items)
+       }
+
+       if (ps->items) {
                ast_free(ps->items);
+               ps->items = NULL;
+       }
 
        ast_free(ps);
 }
 
-static void * playtones_alloc(struct ast_channel *chan, void *params)
+static void *playtones_alloc(struct ast_channel *chan, void *params)
 {
        struct playtones_def *pd = params;
        struct playtones_state *ps = NULL;
 
-       if (!(ps = ast_calloc(1, sizeof(*ps))))
+       if (!(ps = ast_calloc(1, sizeof(*ps)))) {
                return NULL;
+       }
 
        ps->origwfmt = chan->writeformat;
 
@@ -131,10 +148,11 @@ static void * playtones_alloc(struct ast_channel *chan, void *params)
        }
 
        /* Let interrupts interrupt :) */
-       if (pd->interruptible)
+       if (pd->interruptible) {
                ast_set_flag(chan, AST_FLAG_WRITE_INT);
-       else
+       } else {
                ast_clear_flag(chan, AST_FLAG_WRITE_INT);
+       }
 
        return ps;
 }
@@ -144,17 +162,20 @@ static int playtones_generator(struct ast_channel *chan, void *data, int len, in
        struct playtones_state *ps = data;
        struct playtones_item *pi;
        int x;
-       /* we need to prepare a frame with 16 * timelen samples as we're 
-        * generating SLIN audio
-        */
+
+       /* we need to prepare a frame with 16 * timelen samples as we're
+        * generating SLIN audio */
+
        len = samples * 2;
        if (len > sizeof(ps->data) / 2 - 1) {
                ast_log(LOG_WARNING, "Can't generate that much data!\n");
                return -1;
        }
+
        memset(&ps->f, 0, sizeof(ps->f));
 
        pi = &ps->items[ps->npos];
+
        if (ps->oldnpos != ps->npos) {
                /* Load new parameters */
                ps->v1_1 = 0;
@@ -165,163 +186,199 @@ static int playtones_generator(struct ast_channel *chan, void *data, int len, in
                ps->v3_2 = pi->init_v3_2;
                ps->oldnpos = ps->npos;
        }
-       for (x = 0; x < len/2; x++) {
+
+       for (x = 0; x < samples; x++) {
                ps->v1_1 = ps->v2_1;
                ps->v2_1 = ps->v3_1;
                ps->v3_1 = (pi->fac1 * ps->v2_1 >> 15) - ps->v1_1;
-               
+
                ps->v1_2 = ps->v2_2;
                ps->v2_2 = ps->v3_2;
                ps->v3_2 = (pi->fac2 * ps->v2_2 >> 15) - ps->v1_2;
                if (pi->modulate) {
                        int p;
                        p = ps->v3_2 - 32768;
-                       if (p < 0) p = -p;
+                       if (p < 0) {
+                               p = -p;
+                       }
                        p = ((p * 9) / 10) + 1;
                        ps->data[x] = (ps->v3_1 * p) >> 15;
-               } else
-                       ps->data[x] = ps->v3_1 + ps->v3_2; 
+               } else {
+                       ps->data[x] = ps->v3_1 + ps->v3_2;
+               }
        }
-       
+
        ps->f.frametype = AST_FRAME_VOICE;
        ps->f.subclass = AST_FORMAT_SLINEAR;
        ps->f.datalen = len;
        ps->f.samples = samples;
        ps->f.offset = AST_FRIENDLY_OFFSET;
        ps->f.data.ptr = ps->data;
-       ps->f.delivery.tv_sec = 0;
-       ps->f.delivery.tv_usec = 0;
-       ast_write(chan, &ps->f);
+
+       if (ast_write(chan, &ps->f)) {
+               return -1;
+       }
 
        ps->pos += x;
+
        if (pi->duration && ps->pos >= pi->duration * 8) {      /* item finished? */
                ps->pos = 0;                                    /* start new item */
                ps->npos++;
                if (ps->npos >= ps->nitems) {                   /* last item? */
-                       if (ps->reppos == -1)                   /* repeat set? */
+                       if (ps->reppos == -1) {                 /* repeat set? */
                                return -1;
+                       }
                        ps->npos = ps->reppos;                  /* redo from top */
                }
        }
+
        return 0;
 }
 
 static struct ast_generator playtones = {
-       alloc: playtones_alloc,
-       release: playtones_release,
-       generate: playtones_generator,
+       .alloc     = playtones_alloc,
+       .release   = playtones_release,
+       .generate  = playtones_generator,
 };
 
+int ast_tone_zone_part_parse(const char *s, struct ast_tone_zone_part *tone_data)
+{
+       if (sscanf(s, "%u+%u/%u", &tone_data->freq1, &tone_data->freq2, 
+                       &tone_data->time) == 3) {
+               /* f1+f2/time format */
+       } else if (sscanf(s, "%u+%u", &tone_data->freq1, &tone_data->freq2) == 2) {
+               /* f1+f2 format */
+               tone_data->time = 0;
+       } else if (sscanf(s, "%u*%u/%u", &tone_data->freq1, &tone_data->freq2, 
+                       &tone_data->time) == 3) {
+               /* f1*f2/time format */
+               tone_data->modulate = 1;
+       } else if (sscanf(s, "%u*%u", &tone_data->freq1, &tone_data->freq2) == 2) {
+               /* f1*f2 format */
+               tone_data->time = 0;
+               tone_data->modulate = 1;
+       } else if (sscanf(s, "%u/%u", &tone_data->freq1, &tone_data->time) == 2) {
+               /* f1/time format */
+               tone_data->freq2 = 0;
+       } else if (sscanf(s, "%u", &tone_data->freq1) == 1) {
+               /* f1 format */
+               tone_data->freq2 = 0;
+               tone_data->time = 0;
+       } else if (sscanf(s, "M%u+M%u/%u", &tone_data->freq1, &tone_data->freq2, 
+                       &tone_data->time) == 3) {
+               /* Mf1+Mf2/time format */
+               tone_data->midinote = 1;
+       } else if (sscanf(s, "M%u+M%u", &tone_data->freq1, &tone_data->freq2) == 2) {
+               /* Mf1+Mf2 format */
+               tone_data->time = 0;
+               tone_data->midinote = 1;
+       } else if (sscanf(s, "M%u*M%u/%u", &tone_data->freq1, &tone_data->freq2, 
+                       &tone_data->time) == 3) {
+               /* Mf1*Mf2/time format */
+               tone_data->modulate = 1;
+               tone_data->midinote = 1;
+       } else if (sscanf(s, "M%u*M%u", &tone_data->freq1, &tone_data->freq2) == 2) {
+               /* Mf1*Mf2 format */
+               tone_data->time = 0;
+               tone_data->modulate = 1;
+               tone_data->midinote = 1;
+       } else if (sscanf(s, "M%u/%u", &tone_data->freq1, &tone_data->time) == 2) {
+               /* Mf1/time format */
+               tone_data->freq2 = -1;
+               tone_data->midinote = 1;
+       } else if (sscanf(s, "M%u", &tone_data->freq1) == 1) {
+               /* Mf1 format */
+               tone_data->freq2 = -1;
+               tone_data->time = 0;
+               tone_data->midinote = 1;
+       } else {
+               return -1;
+       }
+
+       return 0;
+}
+
 int ast_playtones_start(struct ast_channel *chan, int vol, const char *playlst, int interruptible)
 {
-       char *s, *data = ast_strdupa(playlst); /* cute */
-       struct playtones_def d = { vol, -1, 0, 1, NULL};
+       char *s, *data = ast_strdupa(playlst);
+       struct playtones_def d = { vol, -1, 0, 1, NULL };
        char *stringp;
        char *separator;
-       
-       if (vol < 1)
+       static const float sample_rate = 8000.0;
+       static const float max_sample_val = 32768.0;
+
+       if (vol < 1) {
                d.vol = 7219; /* Default to -8db */
+       }
 
        d.interruptible = interruptible;
-       
-       stringp=data;
-       /* the stringp/data is not null here */
+
+       stringp = data;
+
        /* check if the data is separated with '|' or with ',' by default */
-       if (strchr(stringp,'|'))
+       if (strchr(stringp,'|')) {
                separator = "|";
-       else
+       } else {
                separator = ",";
-       s = strsep(&stringp,separator);
-       while (s && *s) {
-               int freq1, freq2, duration, modulate = 0, midinote = 0;
+       }
+
+       while ((s = strsep(&stringp, separator)) && !ast_strlen_zero(s)) {
+               struct ast_tone_zone_part tone_data = {
+                       .time = 0,
+               };
 
-               if (s[0]=='!')
+               s = ast_strip(s);
+
+               if (s[0]=='!') {
                        s++;
-               else if (d.reppos == -1)
+               } else if (d.reppos == -1) {
                        d.reppos = d.nitems;
-               if (sscanf(s, "%d+%d/%d", &freq1, &freq2, &duration) == 3) {
-                       /* f1+f2/time format */
-               } else if (sscanf(s, "%d+%d", &freq1, &freq2) == 2) {
-                       /* f1+f2 format */
-                       duration = 0;
-               } else if (sscanf(s, "%d*%d/%d", &freq1, &freq2, &duration) == 3) {
-                       /* f1*f2/time format */
-                       modulate = 1;
-               } else if (sscanf(s, "%d*%d", &freq1, &freq2) == 2) {
-                       /* f1*f2 format */
-                       duration = 0;
-                       modulate = 1;
-               } else if (sscanf(s, "%d/%d", &freq1, &duration) == 2) {
-                       /* f1/time format */
-                       freq2 = 0;
-               } else if (sscanf(s, "%d", &freq1) == 1) {
-                       /* f1 format */
-                       freq2 = 0;
-                       duration = 0;
-               } else if (sscanf(s, "M%d+M%d/%d", &freq1, &freq2, &duration) == 3) {
-                       /* Mf1+Mf2/time format */
-                       midinote = 1;
-               } else if (sscanf(s, "M%d+M%d", &freq1, &freq2) == 2) {
-                       /* Mf1+Mf2 format */
-                       duration = 0;
-                       midinote = 1;
-               } else if (sscanf(s, "M%d*M%d/%d", &freq1, &freq2, &duration) == 3) {
-                       /* Mf1*Mf2/time format */
-                       modulate = 1;
-                       midinote = 1;
-               } else if (sscanf(s, "M%d*M%d", &freq1, &freq2) == 2) {
-                       /* Mf1*Mf2 format */
-                       duration = 0;
-                       modulate = 1;
-                       midinote = 1;
-               } else if (sscanf(s, "M%d/%d", &freq1, &duration) == 2) {
-                       /* Mf1/time format */
-                       freq2 = -1;
-                       midinote = 1;
-               } else if (sscanf(s, "M%d", &freq1) == 1) {
-                       /* Mf1 format */
-                       freq2 = -1;
-                       duration = 0;
-                       midinote = 1;
-               } else {
-                       ast_log(LOG_WARNING,"%s: tone component '%s' of '%s' is no good\n",chan->name,s,playlst);
-                       return -1;
                }
 
-               if (midinote) {
+               if (ast_tone_zone_part_parse(s, &tone_data)) {
+                       ast_log(LOG_ERROR, "Failed to parse tone part '%s'\n", s);
+                       continue;
+               }
+
+               if (tone_data.midinote) {
                        /* midi notes must be between 0 and 127 */
-                       if ((freq1 >= 0) && (freq1 <= 127))
-                               freq1 = midi_tohz[freq1];
-                       else
-                               freq1 = 0;
 
-                       if ((freq2 >= 0) && (freq2 <= 127))
-                               freq2 = midi_tohz[freq2];
-                       else
-                               freq2 = 0;
+                       if (tone_data.freq1 >= 0 && tone_data.freq1 <= 127) {
+                               tone_data.freq1 = midi_tohz[tone_data.freq1];
+                       } else {
+                               tone_data.freq1 = 0;
+                       }
+
+                       if (tone_data.freq2 >= 0 && tone_data.freq2 <= 127) {
+                               tone_data.freq2 = midi_tohz[tone_data.freq2];
+                       } else {
+                               tone_data.freq2 = 0;
+                       }
                }
 
                if (!(d.items = ast_realloc(d.items, (d.nitems + 1) * sizeof(*d.items)))) {
                        return -1;
                }
-               d.items[d.nitems].fac1 = 2.0 * cos(2.0 * M_PI * (freq1 / 8000.0)) * 32768.0;
-               d.items[d.nitems].init_v2_1 = sin(-4.0 * M_PI * (freq1 / 8000.0)) * d.vol;
-               d.items[d.nitems].init_v3_1 = sin(-2.0 * M_PI * (freq1 / 8000.0)) * d.vol;
 
-               d.items[d.nitems].fac2 = 2.0 * cos(2.0 * M_PI * (freq2 / 8000.0)) * 32768.0;
-               d.items[d.nitems].init_v2_2 = sin(-4.0 * M_PI * (freq2 / 8000.0)) * d.vol;
-               d.items[d.nitems].init_v3_2 = sin(-2.0 * M_PI * (freq2 / 8000.0)) * d.vol;
-               d.items[d.nitems].duration = duration;
-               d.items[d.nitems].modulate = modulate;
-               d.nitems++;
+               d.items[d.nitems].fac1 = 2.0 * cos(2.0 * M_PI * (tone_data.freq1 / sample_rate)) * max_sample_val;
+               d.items[d.nitems].init_v2_1 = sin(-4.0 * M_PI * (tone_data.freq1 / sample_rate)) * d.vol;
+               d.items[d.nitems].init_v3_1 = sin(-2.0 * M_PI * (tone_data.freq1 / sample_rate)) * d.vol;
+
+               d.items[d.nitems].fac2 = 2.0 * cos(2.0 * M_PI * (tone_data.freq2 / sample_rate)) * max_sample_val;
+               d.items[d.nitems].init_v2_2 = sin(-4.0 * M_PI * (tone_data.freq2 / sample_rate)) * d.vol;
+               d.items[d.nitems].init_v3_2 = sin(-2.0 * M_PI * (tone_data.freq2 / sample_rate)) * d.vol;
 
-               s = strsep(&stringp,separator);
+               d.items[d.nitems].duration = tone_data.time;
+               d.items[d.nitems].modulate = tone_data.modulate;
+
+               d.nitems++;
        }
 
        if (ast_activate_generator(chan, &playtones, &d)) {
                ast_free(d.items);
                return -1;
        }
+
        return 0;
 }
 
@@ -330,265 +387,735 @@ void ast_playtones_stop(struct ast_channel *chan)
        ast_deactivate_generator(chan);
 }
 
-/*--------------------------------------------*/
-
-static AST_RWLIST_HEAD_STATIC(tone_zones, tone_zone);
-static struct tone_zone *current_tonezone;
-
-struct tone_zone *ast_walk_indications(const struct tone_zone *cur)
+int ast_tone_zone_count(void)
 {
-       struct tone_zone *tz = NULL;
-
-       AST_RWLIST_RDLOCK(&tone_zones);
-       /* If cur is not NULL, then we have to iterate through - otherwise just return the first entry */
-       if (cur) {
-               AST_RWLIST_TRAVERSE(&tone_zones, tz, list) {
-                       if (tz == cur)
-                               break;
-               }
-               tz = AST_RWLIST_NEXT(tz, list);
-       } else {
-               tz = AST_RWLIST_FIRST(&tone_zones);
-       }
-       AST_RWLIST_UNLOCK(&tone_zones);
+       return ao2_container_count(ast_tone_zones);
+}
 
-       return tz;
+struct ao2_iterator ast_tone_zone_iterator_init(void)
+{
+       return ao2_iterator_init(ast_tone_zones, 0);
 }
 
 /* Set global indication country */
-int ast_set_indication_country(const char *country)
+static int ast_set_indication_country(const char *country)
 {
-       struct tone_zone *zone = NULL;
+       struct ast_tone_zone *zone = NULL;
 
        /* If no country is specified or we are unable to find the zone, then return not found */
-       if (!country || !(zone = ast_get_indication_zone(country)))
-               return 1;
-       
+       if (ast_strlen_zero(country) || !(zone = ast_get_indication_zone(country))) {
+               return -1;
+       }
+
        ast_verb(3, "Setting default indication country to '%s'\n", country);
 
-       /* Protect the current tonezone using the tone_zones lock as well */
-       AST_RWLIST_WRLOCK(&tone_zones);
-       current_tonezone = zone;
-       AST_RWLIST_UNLOCK(&tone_zones);
+       ao2_lock(ast_tone_zones);
+       if (default_tone_zone) {
+               default_tone_zone = ast_tone_zone_unref(default_tone_zone);
+       }
+       default_tone_zone = ast_tone_zone_ref(zone);
+       ao2_unlock(ast_tone_zones);
+
+       zone = ast_tone_zone_unref(zone);
 
-       /* Zone was found */
        return 0;
 }
 
-/* locate tone_zone, given the country. if country == NULL, use the default country */
-struct tone_zone *ast_get_indication_zone(const char *country)
+/* locate ast_tone_zone, given the country. if country == NULL, use the default country */
+struct ast_tone_zone *ast_get_indication_zone(const char *country)
 {
-       struct tone_zone *tz = NULL;
-       int alias_loop = 0;
-
-       AST_RWLIST_RDLOCK(&tone_zones);
+       struct ast_tone_zone *tz = NULL;
+       struct ast_tone_zone zone_arg = {
+               .nrringcadence = 0,
+       };
 
        if (ast_strlen_zero(country)) {
-               tz = current_tonezone ? current_tonezone : AST_LIST_FIRST(&tone_zones);
-       } else {
-               do {
-                       AST_RWLIST_TRAVERSE(&tone_zones, tz, list) {
-                               if (!strcasecmp(tz->country, country))
-                                       break;
-                       }
-                       if (!tz)
-                               break;
-                       /* If this is an alias then we have to search yet again otherwise we have found the zonezone */
-                       if (tz->alias && tz->alias[0])
-                               country = tz->alias;
-                       else
-                               break;
-               } while ((++alias_loop < 20) && tz);
-       }
+               ao2_lock(ast_tone_zones);
+               if (default_tone_zone) {
+                       tz = ast_tone_zone_ref(default_tone_zone);
+               }
+               ao2_unlock(ast_tone_zones);
 
-       AST_RWLIST_UNLOCK(&tone_zones);
+               return tz;
+       }
 
-       /* If we reached the maximum loops to find the proper country via alias, print out a notice */
-       if (alias_loop == 20)
-               ast_log(LOG_NOTICE, "Alias loop for '%s' is bonkers\n", country);
+       ast_copy_string(zone_arg.country, country, sizeof(zone_arg.country));
 
-       return tz;
+       return ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER);
 }
 
-/* locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */
-struct tone_zone_sound *ast_get_indication_tone(const struct tone_zone *zone, const char *indication)
+struct ast_tone_zone_sound *ast_get_indication_tone(const struct ast_tone_zone *_zone, const char *indication)
 {
-       struct tone_zone_sound *ts = NULL;
+       struct ast_tone_zone_sound *ts = NULL;
+       /* _zone is const to the users of the API */
+       struct ast_tone_zone *zone = (struct ast_tone_zone *) _zone;
 
-       AST_RWLIST_RDLOCK(&tone_zones);
-
-       /* If no zone is already specified we need to try to pick one */
+       /* If no zone is specified, use the default */
        if (!zone) {
-               if (current_tonezone) {
-                       zone = current_tonezone;
-               } else if (!(zone = AST_LIST_FIRST(&tone_zones))) {
-                       /* No zone has been found ;( */
-                       AST_RWLIST_UNLOCK(&tone_zones);
+               ao2_lock(ast_tone_zones);
+               if (default_tone_zone) {
+                       zone = ast_tone_zone_ref(default_tone_zone);
+               }
+               ao2_unlock(ast_tone_zones);
+
+               if (!zone) {
                        return NULL;
                }
        }
 
+       ast_tone_zone_lock(zone);
+
        /* Look through list of tones in the zone searching for the right one */
-       AST_LIST_TRAVERSE(&zone->tones, ts, list) {
-               if (!strcasecmp(ts->name, indication))
+       AST_LIST_TRAVERSE(&zone->tones, ts, entry) {
+               if (!strcasecmp(ts->name, indication)) {
+                       /* Increase ref count for the reference we will return */
+                       ts = ast_tone_zone_sound_ref(ts);
                        break;
+               }
        }
 
-       AST_RWLIST_UNLOCK(&tone_zones);
+       ast_tone_zone_unlock(zone);
 
        return ts;
 }
 
-static inline void clear_zone_sound(struct tone_zone_sound *ts)
+static void ast_tone_zone_sound_destructor(void *obj)
 {
+       struct ast_tone_zone_sound *ts = obj;
+
        /* Deconstify the 'const char *'s so the compiler doesn't complain. (but it's safe) */
-       ast_free((char *) ts->name);
-       ast_free((char *) ts->data);
+       if (ts->name) {
+               ast_free((char *) ts->name);
+               ts->name = NULL;
+       }
+
+       if (ts->data) {
+               ast_free((char *) ts->data);
+               ts->data = NULL;
+       }
 }
 
 /*! \brief deallocate the passed tone zone */
-void ast_destroy_indication_zone(struct tone_zone *zone)
+static void ast_tone_zone_destructor(void *obj)
 {
-       struct tone_zone_sound *current;
+       struct ast_tone_zone *zone = obj;
+       struct ast_tone_zone_sound *current;
 
-       while ((current = AST_LIST_REMOVE_HEAD(&zone->tones, list))) {
-               clear_zone_sound(current);
-               ast_free(current);
+       while ((current = AST_LIST_REMOVE_HEAD(&zone->tones, entry))) {
+               current = ast_tone_zone_sound_unref(current);
        }
 
-       if (zone->ringcadence)
+       if (zone->ringcadence) {
                ast_free(zone->ringcadence);
+               zone->ringcadence = NULL;
+       }
+}
+
+/* add a new country, if country exists, it will be replaced. */
+static int ast_register_indication_country(struct ast_tone_zone *zone)
+{
+       ao2_lock(ast_tone_zones);
+       if (!default_tone_zone) {
+               default_tone_zone = ast_tone_zone_ref(zone);
+       }
+       ao2_unlock(ast_tone_zones);
+
+       ao2_link(ast_tone_zones, zone);
 
-       ast_free(zone);
+       ast_verb(3, "Registered indication country '%s'\n", zone->country);
+
+       return 0;
 }
 
-/*--------------------------------------------*/
+/* remove an existing country and all its indications, country must exist. */
+static int ast_unregister_indication_country(const char *country)
+{
+       struct ast_tone_zone *tz = NULL;
+       struct ast_tone_zone zone_arg = {
+               .nrringcadence = 0,
+       };
 
-/* add a new country, if country exists, it will be replaced. */
-int ast_register_indication_country(struct tone_zone *zone)
+       ast_copy_string(zone_arg.country, country, sizeof(zone_arg.country));
+
+       if (!(tz = ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER))) {
+               return -1;
+       }
+
+       ao2_lock(ast_tone_zones);
+       if (default_tone_zone == tz) {
+               ast_tone_zone_unref(default_tone_zone);
+               /* Get a new default, punt to the first one we find */
+               default_tone_zone = ao2_callback(ast_tone_zones, 0, NULL, NULL);
+       }
+       ao2_unlock(ast_tone_zones);
+
+       ao2_unlink(ast_tone_zones, tz);
+
+       tz = ast_tone_zone_unref(tz);
+
+       return 0;
+}
+
+/*!
+ * \note called with the tone zone locked
+ */
+static int ast_register_indication(struct ast_tone_zone *zone, const char *indication,
+               const char *tonelist)
 {
-       struct tone_zone *tz = NULL;
+       struct ast_tone_zone_sound *ts;
 
-       AST_RWLIST_WRLOCK(&tone_zones);
-       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&tone_zones, tz, list) {
-               /* If this is not the same zone, then just continue to the next entry */
-               if (strcasecmp(zone->country, tz->country))
-                       continue;
-               /* If this zone we are going to remove is the current default then make the new zone the default */
-               if (tz == current_tonezone)
-                       current_tonezone = zone;
-               /* Remove from the linked list */
-               AST_RWLIST_REMOVE_CURRENT(list);
-               /* Finally free the zone itself */
-               ast_destroy_indication_zone(tz);
-               break;
+       if (ast_strlen_zero(indication) || ast_strlen_zero(tonelist)) {
+               return -1;
        }
-       AST_RWLIST_TRAVERSE_SAFE_END;
 
-       /* Add zone to the list */
-       AST_RWLIST_INSERT_TAIL(&tone_zones, zone, list);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, ts, entry) {
+               if (!strcasecmp(indication, ts->name)) {
+                       AST_LIST_REMOVE_CURRENT(entry);
+                       ts = ast_tone_zone_sound_unref(ts);
+                       break;
+               }
+       }
+       AST_LIST_TRAVERSE_SAFE_END;
 
-       /* It's all over. */
-       AST_RWLIST_UNLOCK(&tone_zones);
+       if (!(ts = ao2_alloc(sizeof(*ts), ast_tone_zone_sound_destructor))) {
+               return -1;
+       }
 
-       ast_verb(3, "Registered indication country '%s'\n", zone->country);
+       if (!(ts->name = ast_strdup(indication)) || !(ts->data = ast_strdup(tonelist))) {
+               ts = ast_tone_zone_sound_unref(ts);
+               return -1;
+       }
+
+       AST_LIST_INSERT_TAIL(&zone->tones, ts, entry); /* Inherit reference */
 
        return 0;
 }
 
-/* remove an existing country and all its indications, country must exist.
- * Also, all countries which are an alias for the specified country are removed. */
-int ast_unregister_indication_country(const char *country)
+/* remove an existing country's indication. Both country and indication must exist */
+static int ast_unregister_indication(struct ast_tone_zone *zone, const char *indication)
 {
-       struct tone_zone *tz = NULL;
+       struct ast_tone_zone_sound *ts;
        int res = -1;
 
-       AST_RWLIST_WRLOCK(&tone_zones);
-       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&tone_zones, tz, list) {
-               if (country && (strcasecmp(country, tz->country) && strcasecmp(country, tz->alias)))
-                       continue;
-               /* If this tonezone is the current default then unset it */
-               if (tz == current_tonezone) {
-                       ast_log(LOG_NOTICE,"Removed default indication country '%s'\n", tz->country);
-                       current_tonezone = NULL;
+       ast_tone_zone_lock(zone);
+
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, ts, entry) {
+               if (!strcasecmp(indication, ts->name)) {
+                       AST_LIST_REMOVE_CURRENT(entry);
+                       ts = ast_tone_zone_sound_unref(ts);
+                       res = 0;
+                       break;
                }
-               /* Remove from the list */
-               AST_RWLIST_REMOVE_CURRENT(list);
-               ast_verb(3, "Unregistered indication country '%s'\n", tz->country);
-               ast_destroy_indication_zone(tz);
-               res = 0;
        }
-       AST_RWLIST_TRAVERSE_SAFE_END;
-       AST_RWLIST_UNLOCK(&tone_zones);
+       AST_LIST_TRAVERSE_SAFE_END;
+
+       ast_tone_zone_unlock(zone);
 
        return res;
 }
 
-/* add a new indication to a tone_zone. tone_zone must exist. if the indication already
- * exists, it will be replaced. */
-int ast_register_indication(struct tone_zone *zone, const char *indication, const char *tonelist)
+static struct ast_tone_zone *ast_tone_zone_alloc(void)
 {
-       struct tone_zone_sound *ts;
-       int found = 0;
+       return ao2_alloc(sizeof(struct ast_tone_zone), ast_tone_zone_destructor);
+}
 
-       /* is it an alias? stop */
-       if (zone->alias[0])
-               return -1;
+static char *complete_country(struct ast_cli_args *a)
+{
+       char *res = NULL;
+       struct ao2_iterator i;
+       int which = 0;
+       size_t wordlen;
+       struct ast_tone_zone *tz;
+
+       wordlen = strlen(a->word);
+
+       i = ao2_iterator_init(ast_tone_zones, 0);
+       while ((tz = ao2_iterator_next(&i))) {
+               if (!strncasecmp(a->word, tz->country, wordlen) && ++which > a->n) {
+                       res = ast_strdup(tz->country);
+               }
+               tz = ast_tone_zone_unref(tz);
+               if (res) {
+                       break;
+               }
+       }
 
-       AST_RWLIST_WRLOCK(&tone_zones);
+       return res;
+}
 
-       AST_LIST_TRAVERSE(&zone->tones, ts, list) {
-               if (!strcasecmp(indication, ts->name)) {
-                       clear_zone_sound(ts);
-                       found = 1;
+static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       struct ast_tone_zone *tz;
+       int created_country = 0;
+       char *res = CLI_SUCCESS;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "indication add";
+               e->usage =
+                       "Usage: indication add <country> <indication> \"<tonelist>\"\n"
+                       "       Add the given indication to the country.\n";
+               return NULL;
+       case CLI_GENERATE:
+               if (a->pos == 2) {
+                       return complete_country(a);
+               } else {
+                       return NULL;
+               }
+       }
+
+       if (a->argc != 5) {
+               return CLI_SHOWUSAGE;
+       }
+
+       if (!(tz = ast_get_indication_zone(a->argv[2]))) {
+               /* country does not exist, create it */
+               ast_log(LOG_NOTICE, "Country '%s' does not exist, creating it.\n", a->argv[2]);
+
+               if (!(tz = ast_tone_zone_alloc())) {
+                       return CLI_FAILURE;
+               }
+
+               ast_copy_string(tz->country, a->argv[2], sizeof(tz->country));
+
+               if (ast_register_indication_country(tz)) {
+                       ast_log(LOG_WARNING, "Unable to register new country\n");
+                       tz = ast_tone_zone_unref(tz);
+                       return CLI_FAILURE;
+               }
+
+               created_country = 1;
+       }
+
+       ast_tone_zone_lock(tz);
+
+       if (ast_register_indication(tz, a->argv[3], a->argv[4])) {
+               ast_log(LOG_WARNING, "Unable to register indication %s/%s\n", a->argv[2], a->argv[3]);
+               if (created_country) {
+                       ast_unregister_indication_country(a->argv[2]);
+               }
+               res = CLI_FAILURE;
+       }
+
+       ast_tone_zone_unlock(tz);
+
+       tz = ast_tone_zone_unref(tz);
+
+       return res;
+}
+
+static char *complete_indications(struct ast_cli_args *a)
+{
+       char *res = NULL;
+       int which = 0;
+       size_t wordlen;
+       struct ast_tone_zone_sound *ts;
+       struct ast_tone_zone *tz, tmp_tz = {
+               .nrringcadence = 0,
+       };
+
+       ast_copy_string(tmp_tz.country, a->argv[a->pos - 1], sizeof(tmp_tz.country));
+
+       if (!(tz = ao2_find(ast_tone_zones, &tmp_tz, OBJ_POINTER))) {
+               return NULL;
+       }
+
+       wordlen = strlen(a->word);
+
+       ast_tone_zone_lock(tz);
+       AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
+               if (!strncasecmp(a->word, ts->name, wordlen) && ++which > a->n) {
+                       res = ast_strdup(ts->name);
                        break;
                }
        }
-       if (!ts) {
-               /* not there, we have to add */
-               if (!(ts = ast_calloc(1, sizeof(*ts)))) {
-                       AST_RWLIST_UNLOCK(&tone_zones);
-                       return -2;
+       ast_tone_zone_unlock(tz);
+
+       tz = ast_tone_zone_unref(tz);
+
+       return res;
+}
+
+static char *handle_cli_indication_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       struct ast_tone_zone *tz;
+       char *res = CLI_SUCCESS;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "indication remove";
+               e->usage =
+                       "Usage: indication remove <country> [indication]\n"
+                       "       Remove the given indication from the country.\n";
+               return NULL;
+       case CLI_GENERATE:
+               if (a->pos == 2) {
+                       return complete_country(a);
+               } else if (a->pos == 3) {
+                       return complete_indications(a);
                }
        }
-       if (!(ts->name = ast_strdup(indication)) || !(ts->data = ast_strdup(tonelist))) {
-               ast_free(ts);
-               AST_RWLIST_UNLOCK(&tone_zones);
-               return -2;
+
+       if (a->argc != 3 && a->argc != 4) {
+               return CLI_SHOWUSAGE;
+       }
+
+       if (a->argc == 3) {
+               /* remove entire country */
+               if (ast_unregister_indication_country(a->argv[2])) {
+                       ast_log(LOG_WARNING, "Unable to unregister indication country %s\n", a->argv[2]);
+                       return CLI_FAILURE;
+               }
+
+               return CLI_SUCCESS;
+       }
+
+       if (!(tz = ast_get_indication_zone(a->argv[2]))) {
+               ast_log(LOG_WARNING, "Unable to unregister indication %s/%s, country does not exists\n", a->argv[2], a->argv[3]);
+               return CLI_FAILURE;
+       }
+
+       if (ast_unregister_indication(tz, a->argv[3])) {
+               ast_log(LOG_WARNING, "Unable to unregister indication %s/%s\n", a->argv[2], a->argv[3]);
+               res = CLI_FAILURE;
+       }
+
+       tz = ast_tone_zone_unref(tz);
+
+       return res;
+}
+
+static char *handle_cli_indication_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       struct ast_tone_zone *tz = NULL;
+       struct ast_str *buf;
+       int found_country = 0;
+       int i;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "indication show";
+               e->usage =
+                       "Usage: indication show [<country> ...]\n"
+                       "       Display either a condensed summary of all countries and indications, or a\n"
+                       "       more verbose list of indications for the specified countries.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return complete_country(a);
+       }
+
+       if (a->argc == 2) {
+               struct ao2_iterator iter;
+               /* no arguments, show a list of countries */
+               ast_cli(a->fd, "Country   Description\n");
+               ast_cli(a->fd, "===========================\n");
+               iter = ast_tone_zone_iterator_init();
+               while ((tz = ao2_iterator_next(&iter))) {
+                       ast_tone_zone_lock(tz);
+                       ast_cli(a->fd, "%-7.7s  %s\n", tz->country, tz->description);
+                       ast_tone_zone_unlock(tz);
+                       tz = ast_tone_zone_unref(tz);
+               }
+               return CLI_SUCCESS;
+       }
+
+       buf = ast_str_alloca(256);
+
+       for (i = 2; i < a->argc; i++) {
+               struct ast_tone_zone zone_arg = {
+                       .nrringcadence = 0,
+               };
+               struct ast_tone_zone_sound *ts;
+               int j;
+
+               ast_copy_string(zone_arg.country, a->argv[i], sizeof(zone_arg.country));
+
+               if (!(tz = ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER))) {
+                       continue;
+               }
+
+               if (!found_country) {
+                       found_country = 1;
+                       ast_cli(a->fd, "Country Indication      PlayList\n");
+                       ast_cli(a->fd, "=====================================\n");
+               }
+
+               ast_tone_zone_lock(tz);
+
+               ast_str_set(&buf, 0, "%-7.7s %-15.15s ", tz->country, "<ringcadence>");
+               for (j = 0; j < tz->nrringcadence; j++) {
+                       ast_str_append(&buf, 0, "%d%s", tz->ringcadence[j],
+                                       (j == tz->nrringcadence - 1) ? "" : ",");
+               }
+               ast_str_append(&buf, 0, "\n");
+               ast_cli(a->fd, "%s", buf->str);
+
+               AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
+                       ast_cli(a->fd, "%-7.7s %-15.15s %s\n", tz->country, ts->name, ts->data);
+               }
+
+               ast_tone_zone_unlock(tz);
+               tz = ast_tone_zone_unref(tz);
        }
 
-       if (!found) {
-               AST_LIST_INSERT_TAIL(&zone->tones, ts, list);
+       if (!found_country) {
+               ast_cli(a->fd, "No countries matched your criteria.\n");
        }
 
-       AST_RWLIST_UNLOCK(&tone_zones);
+       return CLI_SUCCESS;
+}
+
+static int is_valid_tone_zone(struct ast_tone_zone *zone)
+{
+       int res;
+
+       ast_tone_zone_lock(zone);
+       res = (!ast_strlen_zero(zone->description) && !AST_LIST_EMPTY(&zone->tones));
+       ast_tone_zone_unlock(zone);
+
+       return res;
+}
+
+/*!
+ * \note This is called with the tone zone locked.
+ */
+static void store_tone_zone_ring_cadence(struct ast_tone_zone *zone, const char *val)
+{
+       char buf[1024];
+       char *ring, *c = buf;
+
+       ast_copy_string(buf, val, sizeof(buf));
+
+       while ((ring = strsep(&c, ","))) {
+               int *tmp, val;
+
+               ring = ast_strip(ring);
+
+               if (!isdigit(ring[0]) || (val = atoi(ring)) == -1) {
+                       ast_log(LOG_WARNING, "Invalid ringcadence given '%s'.\n", ring);
+                       continue;
+               }
+
+               if (!(tmp = ast_realloc(zone->ringcadence, (zone->nrringcadence + 1) * sizeof(int)))) {
+                       return;
+               }
+
+               zone->ringcadence = tmp;
+               tmp[zone->nrringcadence] = val;
+               zone->nrringcadence++;
+       }
+}
+
+static void store_config_tone_zone(struct ast_tone_zone *zone, const char *var,
+               const char *value)
+{
+       CV_START(var, value);
+
+       CV_STR("description", zone->description);
+       CV_F("ringcadence", store_tone_zone_ring_cadence(zone, value));
+       CV_F("ringcadance", store_tone_zone_ring_cadence(zone, value));
+
+       ast_register_indication(zone, var, value);
+
+       CV_END;
+}
+
+static void reset_tone_zone(struct ast_tone_zone *zone)
+{
+       ast_tone_zone_lock(zone);
+
+       zone->killme = 0;
+
+       if (zone->nrringcadence) {
+               zone->nrringcadence = 0;
+               ast_free(zone->ringcadence);
+               zone->ringcadence = NULL;
+       }
+
+       ast_tone_zone_unlock(zone);
+}
+
+static int parse_tone_zone(struct ast_config *cfg, const char *country)
+{
+       struct ast_variable *v;
+       struct ast_tone_zone *zone;
+       struct ast_tone_zone tmp_zone = {
+               .nrringcadence = 0,
+       };
+       int allocd = 0;
+
+       ast_copy_string(tmp_zone.country, country, sizeof(tmp_zone.country));
+
+       if ((zone = ao2_find(ast_tone_zones, &tmp_zone, OBJ_POINTER))) {
+               reset_tone_zone(zone);
+       } else if ((zone = ast_tone_zone_alloc())) {
+               allocd = 1;
+               ast_copy_string(zone->country, country, sizeof(zone->country));
+       } else {
+               return -1;
+       }
+
+       ast_tone_zone_lock(zone);
+       for (v = ast_variable_browse(cfg, country); v; v = v->next) {
+               store_config_tone_zone(zone, v->name, v->value);
+       }
+       ast_tone_zone_unlock(zone);
+
+       if (allocd) {
+               if (!is_valid_tone_zone(zone)) {
+                       ast_log(LOG_WARNING, "Indication country '%s' is invalid\n", country);
+               } else if (ast_register_indication_country(zone)) {
+                       ast_log(LOG_WARNING, "Unable to register indication country '%s'.\n",
+                                       country);
+               }
+       }
+
+       zone = ast_tone_zone_unref(zone);
+
        return 0;
 }
 
-/* remove an existing country's indication. Both country and indication must exist */
-int ast_unregister_indication(struct tone_zone *zone, const char *indication)
+/*!
+ * Mark the zone and its tones before parsing configuration.  We will use this
+ * to know what to remove after configuration is parsed.
+ */
+static int tone_zone_mark(void *obj, void *arg, int flags)
+{
+       struct ast_tone_zone *zone = obj;
+       struct ast_tone_zone_sound *s;
+
+       ast_tone_zone_lock(zone);
+
+       zone->killme = 1;
+
+       AST_LIST_TRAVERSE(&zone->tones, s, entry) {
+               s->killme = 1;
+       }
+
+       ast_tone_zone_unlock(zone);
+
+       return 0;
+}
+
+/*!
+ * Prune tones no longer in the configuration, and have the tone zone unlinked
+ * if it is no longer in the configuration at all.
+ */
+static int prune_tone_zone(void *obj, void *arg, int flags)
+{
+       struct ast_tone_zone *zone = obj;
+       struct ast_tone_zone_sound *s;
+
+       ast_tone_zone_lock(zone);
+
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, s, entry) {
+               if (s->killme) {
+                       AST_LIST_REMOVE_CURRENT(entry);
+                       s = ast_tone_zone_sound_unref(s);
+               }
+       }
+       AST_LIST_TRAVERSE_SAFE_END;
+
+       ast_tone_zone_unlock(zone);
+
+       return zone->killme ? CMP_MATCH : 0;
+}
+
+/*! \brief load indications module */
+static int load_indications(int reload)
 {
-       struct tone_zone_sound *ts;
+       struct ast_config *cfg;
+       const char *cxt = NULL;
+       const char *country = NULL;
+       struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
        int res = -1;
 
-       /* is it an alias? stop */
-       if (zone->alias[0])
+       cfg = ast_config_load2(config, "indications", config_flags);
+
+       if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
                return -1;
+       } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
+               return 0;
+       }
 
-       AST_RWLIST_WRLOCK(&tone_zones);
+       /* Lock the container to prevent multiple simultaneous reloads */
+       ao2_lock(ast_tone_zones);
 
-       AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, ts, list) {
-               if (!strcasecmp(indication, ts->name)) {
-                       AST_LIST_REMOVE_CURRENT(list);
-                       clear_zone_sound(ts);
-                       ast_free(ts);
-                       res = 0;
-                       break;
+       ao2_callback(ast_tone_zones, OBJ_NODATA, tone_zone_mark, NULL);
+
+       /* Use existing config to populate the Indication table */
+       while ((cxt = ast_category_browse(cfg, cxt))) {
+               /* All categories but "general" are considered countries */
+               if (!strcasecmp(cxt, "general")) {
+                       continue;
+               }
+
+               if (parse_tone_zone(cfg, cxt)) {
+                       goto return_cleanup;
                }
        }
-       AST_LIST_TRAVERSE_SAFE_END;
 
-       /* indication not found, goodbye */
-       AST_RWLIST_UNLOCK(&tone_zones);
+       ao2_callback(ast_tone_zones, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
+                       prune_tone_zone, NULL);
+
+       /* determine which country is the default */
+       country = ast_variable_retrieve(cfg, "general", "country");
+       if (ast_strlen_zero(country) || ast_set_indication_country(country)) {
+               ast_log(LOG_WARNING, "Unable to set the default country (for indication tones)\n");
+       }
+
+       res = 0;
+
+return_cleanup:
+       ao2_unlock(ast_tone_zones);
+       ast_config_destroy(cfg);
+
        return res;
 }
+
+/*! \brief CLI entries for commands provided by this module */
+static struct ast_cli_entry cli_indications[] = {
+       AST_CLI_DEFINE(handle_cli_indication_add,    "Add the given indication to the country"),
+       AST_CLI_DEFINE(handle_cli_indication_remove, "Remove the given indication from the country"),
+       AST_CLI_DEFINE(handle_cli_indication_show,   "Display a list of all countries/indications")
+};
+
+static int ast_tone_zone_hash(const void *obj, const int flags)
+{
+       const struct ast_tone_zone *zone = obj;
+
+       return ast_str_case_hash(zone->country);
+}
+
+static int ast_tone_zone_cmp(void *obj, void *arg, int flags)
+{
+       struct ast_tone_zone *zone = obj;
+       struct ast_tone_zone *zone_arg = arg;
+
+       return (!strcasecmp(zone->country, zone_arg->country)) ?
+                       CMP_MATCH | CMP_STOP : 0;
+}
+
+/*! \brief Load indications module */
+int ast_indications_init(void)
+{
+       if (!(ast_tone_zones = ao2_container_alloc(NUM_TONE_ZONE_BUCKETS,
+                       ast_tone_zone_hash, ast_tone_zone_cmp))) {
+               return -1;
+       }
+
+       if (load_indications(0)) {
+               return -1;
+       }
+
+       ast_cli_register_multiple(cli_indications, ARRAY_LEN(cli_indications));
+
+       return 0;
+}
+
+/*! \brief Reload indications module */
+int ast_indications_reload(void)
+{
+       return load_indications(1);
+}
+
index e8a5662..05524fe 100644 (file)
@@ -249,6 +249,7 @@ static struct reload_classes {
        { "features",   ast_features_reload },
        { "dsp",        ast_dsp_reload},
        { "udptl",      ast_udptl_reload },
+       { "indications", ast_indications_reload },
        { NULL,         NULL }
 };
 
index 4c2b273..0807edf 100644 (file)
@@ -8562,11 +8562,13 @@ static int pbx_builtin_waitexten(struct ast_channel *chan, void *data)
        } else if (ast_test_flag(&flags, WAITEXTEN_MOH)) {
                ast_indicate_data(chan, AST_CONTROL_HOLD, opts[0], strlen(opts[0]));
        } else if (ast_test_flag(&flags, WAITEXTEN_DIALTONE)) {
-               const struct tone_zone_sound *ts = ast_get_indication_tone(chan->zone, "dial");
-               if (ts)
+               struct ast_tone_zone_sound *ts = ast_get_indication_tone(chan->zone, "dial");
+               if (ts) {
                        ast_playtones_start(chan, 0, ts->data, 0);
-               else
+                       ts = ast_tone_zone_sound_unref(ts);
+               } else {
                        ast_tonepair_start(chan, 350, 440, 0, 0);
+               }
        }
        /* Wait for "n" seconds */
        if (args.timeout && (s = atof(args.timeout)) > 0)
diff --git a/res/res_indications.c b/res/res_indications.c
deleted file mode 100644 (file)
index b2ba5fc..0000000
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2002, Pauline Middelink
- *
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*! \file res_indications.c 
- *
- * \brief Load the indications
- * 
- * \author Pauline Middelink <middelink@polyware.nl>
- *
- * Load the country specific dialtones into the asterisk PBX.
- */
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include <ctype.h>
-#include <sys/stat.h>
-
-#include "asterisk/lock.h"
-#include "asterisk/file.h"
-#include "asterisk/cli.h"
-#include "asterisk/config.h"
-#include "asterisk/channel.h"
-#include "asterisk/pbx.h"
-#include "asterisk/module.h"
-#include "asterisk/translate.h"
-#include "asterisk/indications.h"
-#include "asterisk/utils.h"
-
-/*** DOCUMENTATION
-       <application name="PlayTones" language="en_US">
-               <synopsis>
-                       Play a tone list.
-               </synopsis>
-               <syntax>
-                       <parameter name="arg" required="true">
-                               <para>Arg is either the tone name defined in the <filename>indications.conf</filename>
-                               configuration file, or a directly specified list of frequencies and durations.</para>
-                       </parameter>
-               </syntax>
-               <description>
-                       <para>Plays a tone list. Execution will continue with the next step immediately,
-                       while the tones continue to play.</para>
-                       <para>See the sample <filename>indications.conf</filename> for a description of the
-                       specification of a tonelist.</para>
-               </description>
-               <see-also>
-                       <ref type="application">StopPlayTones</ref>
-               </see-also>
-       </application>
-       <application name="StopPlayTones" language="en_US">
-               <synopsis>
-                       Stop playing a tone list.
-               </synopsis>
-               <syntax />
-               <description>
-                       <para>Stop playing a tone list, initiated by PlayTones().</para>
-               </description>
-               <see-also>
-                       <ref type="application">PlayTones</ref>
-               </see-also>
-       </application>
- ***/
-
-/* Globals */
-static const char config[] = "indications.conf";
-
-/*
- * Implementation of functions provided by this module
- */
-
-/*!
- * \brief Add a country to indication
- * \param e the ast_cli_entry for this CLI command
- * \param cmd the reason we are being called
- * \param a the arguments being passed to us
- */
-static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       struct tone_zone *tz;
-       int created_country = 0;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "indication add";
-               e->usage =
-                       "Usage: indication add <country> <indication> \"<tonelist>\"\n"
-                       "       Add the given indication to the country.\n";
-               return NULL;
-       case CLI_GENERATE:
-               return NULL;
-       }
-
-       if (a->argc != 5)
-               return CLI_SHOWUSAGE;
-
-       tz = ast_get_indication_zone(a->argv[2]);
-       if (!tz) {
-               /* country does not exist, create it */
-               ast_log(LOG_NOTICE, "Country '%s' does not exist, creating it.\n", a->argv[2]);
-               
-               if (!(tz = ast_calloc(1, sizeof(*tz)))) {
-                       return CLI_FAILURE;
-               }
-               ast_copy_string(tz->country, a->argv[2], sizeof(tz->country));
-               if (ast_register_indication_country(tz)) {
-                       ast_log(LOG_WARNING, "Unable to register new country\n");
-                       ast_free(tz);
-                       return CLI_FAILURE;
-               }
-               created_country = 1;
-       }
-       if (ast_register_indication(tz, a->argv[3], a->argv[4])) {
-               ast_log(LOG_WARNING, "Unable to register indication %s/%s\n", a->argv[2], a->argv[3]);
-               if (created_country)
-                       ast_unregister_indication_country(a->argv[2]);
-               return CLI_FAILURE;
-       }
-       return CLI_SUCCESS;
-}
-
-/*!
- * \brief Remove a country from indication
- * \param e the ast_cli_entry for this CLI command
- * \param cmd the reason we are being called
- * \param a the arguments being passed to us
- */
-static char *handle_cli_indication_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       struct tone_zone *tz;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "indication remove";
-               e->usage =
-                       "Usage: indication remove <country> <indication>\n"
-                       "       Remove the given indication from the country.\n";
-               return NULL;
-       case CLI_GENERATE:
-               return NULL;
-       }
-
-       if (a->argc != 3 && a->argc != 4)
-               return CLI_SHOWUSAGE;
-
-       if (a->argc == 3) {
-               /* remove entiry country */
-               if (ast_unregister_indication_country(a->argv[2])) {
-                       ast_log(LOG_WARNING, "Unable to unregister indication country %s\n", a->argv[2]);
-                       return CLI_FAILURE;
-               }
-               return CLI_SUCCESS;
-       }
-
-       tz = ast_get_indication_zone(a->argv[2]);
-       if (!tz) {
-               ast_log(LOG_WARNING, "Unable to unregister indication %s/%s, country does not exists\n", a->argv[2], a->argv[3]);
-               return CLI_FAILURE;
-       }
-       if (ast_unregister_indication(tz, a->argv[3])) {
-               ast_log(LOG_WARNING, "Unable to unregister indication %s/%s\n", a->argv[2], a->argv[3]);
-               return CLI_FAILURE;
-       }
-       return CLI_SUCCESS;
-}
-
-/*!
- * \brief Show the current indications
- * \param e the ast_cli_entry for this CLI command
- * \param cmd the reason we are being called
- * \param a the arguments being passed to us
- */
-static char *handle_cli_indication_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-       struct tone_zone *tz = NULL;
-       char buf[256];
-       int found_country = 0;
-
-       switch (cmd) {
-       case CLI_INIT:
-               e->command = "indication show";
-               e->usage =
-                       "Usage: indication show [<country> ...]\n"
-                       "       Display either a condensed for of all country/indications, or the\n"
-                       "       indications for the specified countries.\n";
-               return NULL;
-       case CLI_GENERATE:
-               return NULL;
-       }
-
-       if (a->argc == 2) {
-               /* no arguments, show a list of countries */
-               ast_cli(a->fd, "Country Alias   Description\n");
-               ast_cli(a->fd, "===========================\n");
-               while ((tz = ast_walk_indications(tz)))
-                       ast_cli(a->fd, "%-7.7s %-7.7s %s\n", tz->country, tz->alias, tz->description);
-               return CLI_SUCCESS;
-       }
-       /* there was a request for specific country(ies), lets humor them */
-       while ((tz = ast_walk_indications(tz))) {
-               int i, j;
-               for (i = 2; i < a->argc; i++) {
-                       if (strcasecmp(tz->country, a->argv[i]) == 0 && !tz->alias[0]) {
-                               struct tone_zone_sound *ts;
-                               if (!found_country) {
-                                       found_country = 1;
-                                       ast_cli(a->fd, "Country Indication      PlayList\n");
-                                       ast_cli(a->fd, "=====================================\n");
-                               }
-                               j = snprintf(buf, sizeof(buf), "%-7.7s %-15.15s ", tz->country, "<ringcadence>");
-                               for (i = 0; i < tz->nrringcadence; i++) {
-                                       j += snprintf(buf + j, sizeof(buf) - j, "%d,", tz->ringcadence[i]);
-                               }
-                               if (tz->nrringcadence)
-                                       j--;
-                               ast_copy_string(buf + j, "\n", sizeof(buf) - j);
-                               ast_cli(a->fd, "%s", buf);
-                               AST_LIST_TRAVERSE(&tz->tones, ts, list) {
-                                       ast_cli(a->fd, "%-7.7s %-15.15s %s\n", tz->country, ts->name, ts->data);
-                               }
-                               break;
-                       }
-               }
-       }
-       if (!found_country)
-               ast_cli(a->fd, "No countries matched your criteria.\n");
-       return CLI_SUCCESS;
-}
-
-/*!
- * \brief play tone for indication country
- * \param chan ast_channel to play the sounds back to
- * \param data contains tone to play
- */
-static int handle_playtones(struct ast_channel *chan, void *data)
-{
-       struct tone_zone_sound *ts;
-       int res;
-
-       if (!data || !((char*)data)[0]) {
-               ast_log(LOG_NOTICE,"Nothing to play\n");
-               return -1;
-       }
-       ts = ast_get_indication_tone(chan->zone, (const char*)data);
-       if (ts && ts->data[0])
-               res = ast_playtones_start(chan, 0, ts->data, 0);
-       else
-               res = ast_playtones_start(chan, 0, (const char*)data, 0);
-       if (res)
-               ast_log(LOG_NOTICE,"Unable to start playtones\n");
-       return res;
-}
-
-/*!
- * \brief Stop tones playing
- * \param chan 
- * \param data 
- */
-static int handle_stopplaytones(struct ast_channel *chan, void *data)
-{
-       ast_playtones_stop(chan);
-       return 0;
-}
-
-/*! \brief load indications module */
-static int ind_load_module(int reload)
-{
-       struct ast_config *cfg;
-       struct ast_variable *v;
-       char *cxt;
-       char *c;
-       struct tone_zone *tones;
-       const char *country = NULL;
-       struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
-
-       /* that the following cast is needed, is yuk! */
-       /* yup, checked it out. It is NOT written to. */
-       cfg = ast_config_load((char *)config, config_flags);
-       if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
-               return -1;
-       } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
-               return 0;
-       }
-
-       if (reload)
-               ast_unregister_indication_country(NULL);
-
-       /* Use existing config to populate the Indication table */
-       cxt = ast_category_browse(cfg, NULL);
-       while(cxt) {
-               /* All categories but "general" are considered countries */
-               if (!strcasecmp(cxt, "general")) {
-                       cxt = ast_category_browse(cfg, cxt);
-                       continue;
-               }               
-               if (!(tones = ast_calloc(1, sizeof(*tones)))) {
-                       ast_config_destroy(cfg);
-                       return -1;
-               }
-               ast_copy_string(tones->country,cxt,sizeof(tones->country));
-
-               v = ast_variable_browse(cfg, cxt);
-               while(v) {
-                       if (!strcasecmp(v->name, "description")) {
-                               ast_copy_string(tones->description, v->value, sizeof(tones->description));
-                       } else if ((!strcasecmp(v->name,"ringcadence"))||(!strcasecmp(v->name,"ringcadance"))) {
-                               char *ring,*rings = ast_strdupa(v->value);
-                               c = rings;
-                               ring = strsep(&c,",");
-                               while (ring) {
-                                       int *tmp, val;
-                                       if (!isdigit(ring[0]) || (val=atoi(ring))==-1) {
-                                               ast_log(LOG_WARNING,"Invalid ringcadence given '%s' at line %d.\n",ring,v->lineno);
-                                               ring = strsep(&c,",");
-                                               continue;
-                                       }                                       
-                                       if (!(tmp = ast_realloc(tones->ringcadence, (tones->nrringcadence + 1) * sizeof(int)))) {
-                                               ast_config_destroy(cfg);
-                                               ast_destroy_indication_zone(tones);
-                                               return -1;
-                                       }
-                                       tones->ringcadence = tmp;
-                                       tmp[tones->nrringcadence] = val;
-                                       tones->nrringcadence++;
-                                       /* next item */
-                                       ring = strsep(&c,",");
-                               }
-                       } else if (!strcasecmp(v->name,"alias")) {
-                               char *countries = ast_strdupa(v->value);
-                               c = countries;
-                               country = strsep(&c,",");
-                               while (country) {
-                                       struct tone_zone* azone;
-                                       if (!(azone = ast_calloc(1, sizeof(*azone)))) {
-                                               ast_config_destroy(cfg);
-                                               ast_destroy_indication_zone(tones);
-                                               return -1;
-                                       }
-                                       ast_copy_string(azone->country, country, sizeof(azone->country));
-                                       ast_copy_string(azone->alias, cxt, sizeof(azone->alias));
-                                       if (ast_register_indication_country(azone)) {
-                                               ast_log(LOG_WARNING, "Unable to register indication alias at line %d.\n",v->lineno);
-                                               ast_destroy_indication_zone(tones);
-                                       }
-                                       /* next item */
-                                       country = strsep(&c,",");
-                               }
-                       } else {
-                               struct tone_zone_sound *ts;
-
-                               /* add tone to country */
-                               AST_LIST_TRAVERSE(&tones->tones, ts, list) {
-                                       if (!strcasecmp(v->name, ts->name)) {
-                                               /* already there */
-                                               ast_log(LOG_NOTICE, "Duplicate entry '%s' skipped.\n", v->name);
-                                               goto out;
-                                       }
-                               }
-
-                               /* not there, add it to the back */
-                               if (!(ts = ast_calloc(1, sizeof(*ts)))) {
-                                       ast_config_destroy(cfg);
-                                       return -1;
-                               }
-                               ts->name = ast_strdup(v->name);
-                               ts->data = ast_strdup(v->value);
-
-                               AST_LIST_INSERT_TAIL(&tones->tones, ts, list);
-                       }
-out:                   v = v->next;
-               }
-               if (tones->description[0] || tones->alias[0] || !AST_LIST_EMPTY(&tones->tones)) {
-                       if (ast_register_indication_country(tones)) {
-                               ast_log(LOG_WARNING, "Unable to register indication at line %d.\n",v->lineno);
-                               ast_destroy_indication_zone(tones);
-                       }
-               } else {
-                       ast_destroy_indication_zone(tones);
-               }
-
-               cxt = ast_category_browse(cfg, cxt);
-       }
-
-       /* determine which country is the default */
-       country = ast_variable_retrieve(cfg,"general","country");
-       if (ast_strlen_zero(country) || ast_set_indication_country(country)) {
-               ast_log(LOG_WARNING,"Unable to set the default country (for indication tones)\n");
-       }
-
-       ast_config_destroy(cfg);
-       return 0;
-}
-
-/*! \brief CLI entries for commands provided by this module */
-static struct ast_cli_entry cli_indications[] = {
-       AST_CLI_DEFINE(handle_cli_indication_add,    "Add the given indication to the country"),
-       AST_CLI_DEFINE(handle_cli_indication_remove, "Remove the given indication from the country"),
-       AST_CLI_DEFINE(handle_cli_indication_show,   "Display a list of all countries/indications")
-};
-
-/*! \brief Unload indicators module */
-static int unload_module(void)
-{
-       /* remove the registed indications... */
-       ast_unregister_indication_country(NULL);
-
-       /* and the functions */
-       ast_cli_unregister_multiple(cli_indications, ARRAY_LEN(cli_indications));
-       ast_unregister_application("PlayTones");
-       ast_unregister_application("StopPlayTones");
-       return 0;
-}
-
-
-/*! \brief Load indications module */
-static int load_module(void)
-{
-       if (ind_load_module(0))
-               return AST_MODULE_LOAD_DECLINE; 
-       ast_cli_register_multiple(cli_indications, ARRAY_LEN(cli_indications));
-       ast_register_application_xml("PlayTones", handle_playtones);
-       ast_register_application_xml("StopPlayTones", handle_stopplaytones);
-
-       return AST_MODULE_LOAD_SUCCESS;
-}
-
-/*! \brief Reload indications module */
-static int reload(void)
-{
-       return ind_load_module(1);
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Region-specific tones",
-               .load = load_module,
-               .unload = unload_module,
-               .reload = reload,
-              );
index 0a47879..f7e0822 100644 (file)
@@ -644,23 +644,34 @@ static u_char *ast_var_indications(struct variable *vp, oid *name, size_t *lengt
                                                                  int exact, size_t *var_len, WriteMethod **write_method)
 {
        static unsigned long long_ret;
-       struct tone_zone *tz = NULL;
+       static char ret_buf[128];
+       struct ast_tone_zone *tz = NULL;
 
        if (header_generic(vp, name, length, exact, var_len, write_method))
                return NULL;
 
        switch (vp->magic) {
        case ASTINDCOUNT:
+       {
+               struct ao2_iterator i;
+
                long_ret = 0;
-               while ( (tz = ast_walk_indications(tz)) )
+
+               i = ast_tone_zone_iterator_init();
+               while ((tz = ao2_iterator_next(&i))) {
+                       tz = ast_tone_zone_unref(tz);
                        long_ret++;
+               }
 
-               return (u_char *)&long_ret;
+               return (u_char *) &long_ret;
+       }
        case ASTINDCURRENT:
                tz = ast_get_indication_zone(NULL);
                if (tz) {
-                       *var_len = strlen(tz->country);
-                       return (u_char *)tz->country;
+                       ast_copy_string(ret_buf, tz->country, sizeof(ret_buf));
+                       *var_len = strlen(ret_buf);
+                       tz = ast_tone_zone_unref(tz);
+                       return (u_char *) ret_buf;
                }
                *var_len = 0;
                return NULL;
@@ -674,34 +685,47 @@ static u_char *ast_var_indications_table(struct variable *vp, oid *name, size_t
                                                                           int exact, size_t *var_len, WriteMethod **write_method)
 {
        static unsigned long long_ret;
-       struct tone_zone *tz = NULL;
+       static char ret_buf[256];
+       struct ast_tone_zone *tz = NULL;
        int i;
+       struct ao2_iterator iter;
 
-       if (header_simple_table(vp, name, length, exact, var_len, write_method, -1))
+       if (header_simple_table(vp, name, length, exact, var_len, write_method, -1)) {
                return NULL;
+       }
 
        i = name[*length - 1] - 1;
-       while ( (tz = ast_walk_indications(tz)) && i )
-       i--;
-       if (tz == NULL)
+
+       iter = ast_tone_zone_iterator_init();
+
+       while ((tz = ao2_iterator_next(&iter)) && i) {
+               tz = ast_tone_zone_unref(tz);
+               i--;
+       }
+
+       if (tz == NULL) {
                return NULL;
+       }
 
        switch (vp->magic) {
        case ASTINDINDEX:
                long_ret = name[*length - 1];
                return (u_char *)&long_ret;
        case ASTINDCOUNTRY:
-               *var_len = strlen(tz->country);
-               return (u_char *)tz->country;
+               ast_copy_string(ret_buf, tz->country, sizeof(ret_buf));
+               tz = ast_tone_zone_unref(tz);
+               *var_len = strlen(ret_buf);
+               return (u_char *) ret_buf;
        case ASTINDALIAS:
-               if (tz->alias) {
-                       *var_len = strlen(tz->alias);
-                       return (u_char *)tz->alias;
-               }
+               /* No longer exists */
                return NULL;
        case ASTINDDESCRIPTION:
-               *var_len = strlen(tz->description);
-               return (u_char *)tz->description;
+               ast_tone_zone_lock(tz);
+               ast_copy_string(ret_buf, tz->description, sizeof(ret_buf));
+               ast_tone_zone_unlock(tz);
+               tz = ast_tone_zone_unref(tz);
+               *var_len = strlen(ret_buf);
+               return (u_char *) ret_buf;
        default:
                break;
        }