Thu Apr 28 2011 16:56:47

Asterisk developer's documentation


indications.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2002, Pauline Middelink
00005  * Copyright (C) 2009, Digium, Inc.
00006  *
00007  * See http://www.asterisk.org for more information about
00008  * the Asterisk project. Please do not directly contact
00009  * any of the maintainers of this project for assistance;
00010  * the project provides a web site, mailing lists and IRC
00011  * channels for your use.
00012  *
00013  * This program is free software, distributed under the terms of
00014  * the GNU General Public License Version 2. See the LICENSE file
00015  * at the top of the source tree.
00016  */
00017 
00018 /*!
00019  * \file
00020  * \brief Indication Tone Handling
00021  *
00022  * \author Pauline Middelink <middelink@polyware.nl>
00023  * \author Russell Bryant <russell@digium.com>
00024  */
00025 
00026 #include "asterisk.h"
00027 
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 257950 $")
00029 
00030 #include <math.h>
00031 
00032 #include "asterisk/lock.h"
00033 #include "asterisk/linkedlists.h"
00034 #include "asterisk/indications.h"
00035 #include "asterisk/frame.h"
00036 #include "asterisk/channel.h"
00037 #include "asterisk/utils.h"
00038 #include "asterisk/cli.h"
00039 #include "asterisk/module.h"
00040 #include "asterisk/astobj2.h"
00041 
00042 #include "asterisk/_private.h" /* _init(), _reload() */
00043 
00044 /* Globals */
00045 static const char config[] = "indications.conf";
00046 
00047 static const int midi_tohz[128] = {
00048    8,     8,     9,     9,     10,    10,    11,    12,    12,    13,
00049    14,    15,    16,    17,    18,    19,    20,    21,    23,    24,
00050    25,    27,    29,    30,    32,    34,    36,    38,    41,    43,
00051    46,    48,    51,    55,    58,    61,    65,    69,    73,    77,
00052    82,    87,    92,    97,    103,   110,   116,   123,   130,   138,
00053    146,   155,   164,   174,   184,   195,   207,   220,   233,   246,
00054    261,   277,   293,   311,   329,   349,   369,   391,   415,   440,
00055    466,   493,   523,   554,   587,   622,   659,   698,   739,   783,
00056    830,   880,   932,   987,   1046,  1108,  1174,  1244,  1318,  1396,
00057    1479,  1567,  1661,  1760,  1864,  1975,  2093,  2217,  2349,  2489,
00058    2637,  2793,  2959,  3135,  3322,  3520,  3729,  3951,  4186,  4434,
00059    4698,  4978,  5274,  5587,  5919,  6271,  6644,  7040,  7458,  7902,
00060    8372,  8869,  9397,  9956,  10548, 11175, 11839, 12543
00061 };
00062 
00063 static struct ao2_container *ast_tone_zones;
00064 
00065 #define NUM_TONE_ZONE_BUCKETS 53
00066 
00067 /*!
00068  * \note Access to this is protected by locking the ast_tone_zones container
00069  */
00070 static struct ast_tone_zone *default_tone_zone;
00071 
00072 struct playtones_item {
00073    int fac1;
00074    int init_v2_1;
00075    int init_v3_1;
00076    int fac2;
00077    int init_v2_2;
00078    int init_v3_2;
00079    int modulate;
00080    int duration;
00081 };
00082 
00083 struct playtones_def {
00084    int vol;
00085    int reppos;
00086    int nitems;
00087    int interruptible;
00088    struct playtones_item *items;
00089 };
00090 
00091 struct playtones_state {
00092    int vol;
00093    int v1_1;
00094    int v2_1;
00095    int v3_1;
00096    int v1_2;
00097    int v2_2;
00098    int v3_2;
00099    int reppos;
00100    int nitems;
00101    struct playtones_item *items;
00102    int npos;
00103    int oldnpos;
00104    int pos;
00105    int origwfmt;
00106    struct ast_frame f;
00107    unsigned char offset[AST_FRIENDLY_OFFSET];
00108    short data[4000];
00109 };
00110 
00111 static void playtones_release(struct ast_channel *chan, void *params)
00112 {
00113    struct playtones_state *ps = params;
00114 
00115    if (chan) {
00116       ast_set_write_format(chan, ps->origwfmt);
00117    }
00118 
00119    if (ps->items) {
00120       ast_free(ps->items);
00121       ps->items = NULL;
00122    }
00123 
00124    ast_free(ps);
00125 }
00126 
00127 static void *playtones_alloc(struct ast_channel *chan, void *params)
00128 {
00129    struct playtones_def *pd = params;
00130    struct playtones_state *ps = NULL;
00131 
00132    if (!(ps = ast_calloc(1, sizeof(*ps)))) {
00133       return NULL;
00134    }
00135 
00136    ps->origwfmt = chan->writeformat;
00137 
00138    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
00139       ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
00140       playtones_release(NULL, ps);
00141       ps = NULL;
00142    } else {
00143       ps->vol = pd->vol;
00144       ps->reppos = pd->reppos;
00145       ps->nitems = pd->nitems;
00146       ps->items = pd->items;
00147       ps->oldnpos = -1;
00148    }
00149 
00150    /* Let interrupts interrupt :) */
00151    if (pd->interruptible) {
00152       ast_set_flag(chan, AST_FLAG_WRITE_INT);
00153    } else {
00154       ast_clear_flag(chan, AST_FLAG_WRITE_INT);
00155    }
00156 
00157    return ps;
00158 }
00159 
00160 static int playtones_generator(struct ast_channel *chan, void *data, int len, int samples)
00161 {
00162    struct playtones_state *ps = data;
00163    struct playtones_item *pi;
00164    int x;
00165 
00166    /* we need to prepare a frame with 16 * timelen samples as we're
00167     * generating SLIN audio */
00168 
00169    len = samples * 2;
00170    if (len > sizeof(ps->data) / 2 - 1) {
00171       ast_log(LOG_WARNING, "Can't generate that much data!\n");
00172       return -1;
00173    }
00174 
00175    memset(&ps->f, 0, sizeof(ps->f));
00176 
00177    pi = &ps->items[ps->npos];
00178 
00179    if (ps->oldnpos != ps->npos) {
00180       /* Load new parameters */
00181       ps->v1_1 = 0;
00182       ps->v2_1 = pi->init_v2_1;
00183       ps->v3_1 = pi->init_v3_1;
00184       ps->v1_2 = 0;
00185       ps->v2_2 = pi->init_v2_2;
00186       ps->v3_2 = pi->init_v3_2;
00187       ps->oldnpos = ps->npos;
00188    }
00189 
00190    for (x = 0; x < samples; x++) {
00191       ps->v1_1 = ps->v2_1;
00192       ps->v2_1 = ps->v3_1;
00193       ps->v3_1 = (pi->fac1 * ps->v2_1 >> 15) - ps->v1_1;
00194 
00195       ps->v1_2 = ps->v2_2;
00196       ps->v2_2 = ps->v3_2;
00197       ps->v3_2 = (pi->fac2 * ps->v2_2 >> 15) - ps->v1_2;
00198       if (pi->modulate) {
00199          int p;
00200          p = ps->v3_2 - 32768;
00201          if (p < 0) {
00202             p = -p;
00203          }
00204          p = ((p * 9) / 10) + 1;
00205          ps->data[x] = (ps->v3_1 * p) >> 15;
00206       } else {
00207          ps->data[x] = ps->v3_1 + ps->v3_2;
00208       }
00209    }
00210 
00211    ps->f.frametype = AST_FRAME_VOICE;
00212    ps->f.subclass = AST_FORMAT_SLINEAR;
00213    ps->f.datalen = len;
00214    ps->f.samples = samples;
00215    ps->f.offset = AST_FRIENDLY_OFFSET;
00216    ps->f.data.ptr = ps->data;
00217 
00218    if (ast_write(chan, &ps->f)) {
00219       return -1;
00220    }
00221 
00222    ps->pos += x;
00223 
00224    if (pi->duration && ps->pos >= pi->duration * 8) { /* item finished? */
00225       ps->pos = 0;               /* start new item */
00226       ps->npos++;
00227       if (ps->npos >= ps->nitems) {       /* last item? */
00228          if (ps->reppos == -1) {       /* repeat set? */
00229             return -1;
00230          }
00231          ps->npos = ps->reppos;        /* redo from top */
00232       }
00233    }
00234 
00235    return 0;
00236 }
00237 
00238 static struct ast_generator playtones = {
00239    .alloc     = playtones_alloc,
00240    .release   = playtones_release,
00241    .generate  = playtones_generator,
00242 };
00243 
00244 int ast_tone_zone_part_parse(const char *s, struct ast_tone_zone_part *tone_data)
00245 {
00246    if (sscanf(s, "%30u+%30u/%30u", &tone_data->freq1, &tone_data->freq2, 
00247          &tone_data->time) == 3) {
00248       /* f1+f2/time format */
00249    } else if (sscanf(s, "%30u+%30u", &tone_data->freq1, &tone_data->freq2) == 2) {
00250       /* f1+f2 format */
00251       tone_data->time = 0;
00252    } else if (sscanf(s, "%30u*%30u/%30u", &tone_data->freq1, &tone_data->freq2, 
00253          &tone_data->time) == 3) {
00254       /* f1*f2/time format */
00255       tone_data->modulate = 1;
00256    } else if (sscanf(s, "%30u*%30u", &tone_data->freq1, &tone_data->freq2) == 2) {
00257       /* f1*f2 format */
00258       tone_data->time = 0;
00259       tone_data->modulate = 1;
00260    } else if (sscanf(s, "%30u/%30u", &tone_data->freq1, &tone_data->time) == 2) {
00261       /* f1/time format */
00262       tone_data->freq2 = 0;
00263    } else if (sscanf(s, "%30u", &tone_data->freq1) == 1) {
00264       /* f1 format */
00265       tone_data->freq2 = 0;
00266       tone_data->time = 0;
00267    } else if (sscanf(s, "M%30u+M%30u/%30u", &tone_data->freq1, &tone_data->freq2, 
00268          &tone_data->time) == 3) {
00269       /* Mf1+Mf2/time format */
00270       tone_data->midinote = 1;
00271    } else if (sscanf(s, "M%30u+M%30u", &tone_data->freq1, &tone_data->freq2) == 2) {
00272       /* Mf1+Mf2 format */
00273       tone_data->time = 0;
00274       tone_data->midinote = 1;
00275    } else if (sscanf(s, "M%30u*M%30u/%30u", &tone_data->freq1, &tone_data->freq2, 
00276          &tone_data->time) == 3) {
00277       /* Mf1*Mf2/time format */
00278       tone_data->modulate = 1;
00279       tone_data->midinote = 1;
00280    } else if (sscanf(s, "M%30u*M%30u", &tone_data->freq1, &tone_data->freq2) == 2) {
00281       /* Mf1*Mf2 format */
00282       tone_data->time = 0;
00283       tone_data->modulate = 1;
00284       tone_data->midinote = 1;
00285    } else if (sscanf(s, "M%30u/%30u", &tone_data->freq1, &tone_data->time) == 2) {
00286       /* Mf1/time format */
00287       tone_data->freq2 = -1;
00288       tone_data->midinote = 1;
00289    } else if (sscanf(s, "M%30u", &tone_data->freq1) == 1) {
00290       /* Mf1 format */
00291       tone_data->freq2 = -1;
00292       tone_data->time = 0;
00293       tone_data->midinote = 1;
00294    } else {
00295       return -1;
00296    }
00297 
00298    return 0;
00299 }
00300 
00301 int ast_playtones_start(struct ast_channel *chan, int vol, const char *playlst, int interruptible)
00302 {
00303    char *s, *data = ast_strdupa(playlst);
00304    struct playtones_def d = { vol, -1, 0, 1, NULL };
00305    char *stringp;
00306    char *separator;
00307    static const float sample_rate = 8000.0;
00308    static const float max_sample_val = 32768.0;
00309 
00310    if (vol < 1) {
00311       d.vol = 7219; /* Default to -8db */
00312    }
00313 
00314    d.interruptible = interruptible;
00315 
00316    stringp = data;
00317 
00318    /* check if the data is separated with '|' or with ',' by default */
00319    if (strchr(stringp,'|')) {
00320       separator = "|";
00321    } else {
00322       separator = ",";
00323    }
00324 
00325    while ((s = strsep(&stringp, separator)) && !ast_strlen_zero(s)) {
00326       struct ast_tone_zone_part tone_data = {
00327          .time = 0,
00328       };
00329 
00330       s = ast_strip(s);
00331 
00332       if (s[0]=='!') {
00333          s++;
00334       } else if (d.reppos == -1) {
00335          d.reppos = d.nitems;
00336       }
00337 
00338       if (ast_tone_zone_part_parse(s, &tone_data)) {
00339          ast_log(LOG_ERROR, "Failed to parse tone part '%s'\n", s);
00340          continue;
00341       }
00342 
00343       if (tone_data.midinote) {
00344          /* midi notes must be between 0 and 127 */
00345 
00346          if (tone_data.freq1 >= 0 && tone_data.freq1 <= 127) {
00347             tone_data.freq1 = midi_tohz[tone_data.freq1];
00348          } else {
00349             tone_data.freq1 = 0;
00350          }
00351 
00352          if (tone_data.freq2 >= 0 && tone_data.freq2 <= 127) {
00353             tone_data.freq2 = midi_tohz[tone_data.freq2];
00354          } else {
00355             tone_data.freq2 = 0;
00356          }
00357       }
00358 
00359       if (!(d.items = ast_realloc(d.items, (d.nitems + 1) * sizeof(*d.items)))) {
00360          return -1;
00361       }
00362 
00363       d.items[d.nitems].fac1 = 2.0 * cos(2.0 * M_PI * (tone_data.freq1 / sample_rate)) * max_sample_val;
00364       d.items[d.nitems].init_v2_1 = sin(-4.0 * M_PI * (tone_data.freq1 / sample_rate)) * d.vol;
00365       d.items[d.nitems].init_v3_1 = sin(-2.0 * M_PI * (tone_data.freq1 / sample_rate)) * d.vol;
00366 
00367       d.items[d.nitems].fac2 = 2.0 * cos(2.0 * M_PI * (tone_data.freq2 / sample_rate)) * max_sample_val;
00368       d.items[d.nitems].init_v2_2 = sin(-4.0 * M_PI * (tone_data.freq2 / sample_rate)) * d.vol;
00369       d.items[d.nitems].init_v3_2 = sin(-2.0 * M_PI * (tone_data.freq2 / sample_rate)) * d.vol;
00370 
00371       d.items[d.nitems].duration = tone_data.time;
00372       d.items[d.nitems].modulate = tone_data.modulate;
00373 
00374       d.nitems++;
00375    }
00376 
00377    if (!d.nitems) {
00378       ast_log(LOG_ERROR, "No valid tone parts\n");
00379       return -1;
00380    }
00381 
00382    if (ast_activate_generator(chan, &playtones, &d)) {
00383       ast_free(d.items);
00384       return -1;
00385    }
00386 
00387    return 0;
00388 }
00389 
00390 void ast_playtones_stop(struct ast_channel *chan)
00391 {
00392    ast_deactivate_generator(chan);
00393 }
00394 
00395 int ast_tone_zone_count(void)
00396 {
00397    return ao2_container_count(ast_tone_zones);
00398 }
00399 
00400 struct ao2_iterator ast_tone_zone_iterator_init(void)
00401 {
00402    return ao2_iterator_init(ast_tone_zones, 0);
00403 }
00404 
00405 /* Set global indication country */
00406 static int ast_set_indication_country(const char *country)
00407 {
00408    struct ast_tone_zone *zone = NULL;
00409 
00410    /* If no country is specified or we are unable to find the zone, then return not found */
00411    if (ast_strlen_zero(country) || !(zone = ast_get_indication_zone(country))) {
00412       return -1;
00413    }
00414 
00415    ast_verb(3, "Setting default indication country to '%s'\n", country);
00416 
00417    ao2_lock(ast_tone_zones);
00418    if (default_tone_zone) {
00419       default_tone_zone = ast_tone_zone_unref(default_tone_zone);
00420    }
00421    default_tone_zone = ast_tone_zone_ref(zone);
00422    ao2_unlock(ast_tone_zones);
00423 
00424    zone = ast_tone_zone_unref(zone);
00425 
00426    return 0;
00427 }
00428 
00429 /* locate ast_tone_zone, given the country. if country == NULL, use the default country */
00430 struct ast_tone_zone *ast_get_indication_zone(const char *country)
00431 {
00432    struct ast_tone_zone *tz = NULL;
00433    struct ast_tone_zone zone_arg = {
00434       .nrringcadence = 0,
00435    };
00436 
00437    if (ast_strlen_zero(country)) {
00438       ao2_lock(ast_tone_zones);
00439       if (default_tone_zone) {
00440          tz = ast_tone_zone_ref(default_tone_zone);
00441       }
00442       ao2_unlock(ast_tone_zones);
00443 
00444       return tz;
00445    }
00446 
00447    ast_copy_string(zone_arg.country, country, sizeof(zone_arg.country));
00448 
00449    return ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER);
00450 }
00451 
00452 struct ast_tone_zone_sound *ast_get_indication_tone(const struct ast_tone_zone *_zone, const char *indication)
00453 {
00454    struct ast_tone_zone_sound *ts = NULL;
00455    /* _zone is const to the users of the API */
00456    struct ast_tone_zone *zone = (struct ast_tone_zone *) _zone;
00457 
00458    /* If no zone is specified, use the default */
00459    if (!zone) {
00460       ao2_lock(ast_tone_zones);
00461       if (default_tone_zone) {
00462          zone = ast_tone_zone_ref(default_tone_zone);
00463       }
00464       ao2_unlock(ast_tone_zones);
00465 
00466       if (!zone) {
00467          return NULL;
00468       }
00469    }
00470 
00471    ast_tone_zone_lock(zone);
00472 
00473    /* Look through list of tones in the zone searching for the right one */
00474    AST_LIST_TRAVERSE(&zone->tones, ts, entry) {
00475       if (!strcasecmp(ts->name, indication)) {
00476          /* Increase ref count for the reference we will return */
00477          ts = ast_tone_zone_sound_ref(ts);
00478          break;
00479       }
00480    }
00481 
00482    ast_tone_zone_unlock(zone);
00483 
00484    return ts;
00485 }
00486 
00487 static void ast_tone_zone_sound_destructor(void *obj)
00488 {
00489    struct ast_tone_zone_sound *ts = obj;
00490 
00491    /* Deconstify the 'const char *'s so the compiler doesn't complain. (but it's safe) */
00492    if (ts->name) {
00493       ast_free((char *) ts->name);
00494       ts->name = NULL;
00495    }
00496 
00497    if (ts->data) {
00498       ast_free((char *) ts->data);
00499       ts->data = NULL;
00500    }
00501 }
00502 
00503 /*! \brief deallocate the passed tone zone */
00504 static void ast_tone_zone_destructor(void *obj)
00505 {
00506    struct ast_tone_zone *zone = obj;
00507    struct ast_tone_zone_sound *current;
00508 
00509    while ((current = AST_LIST_REMOVE_HEAD(&zone->tones, entry))) {
00510       current = ast_tone_zone_sound_unref(current);
00511    }
00512 
00513    if (zone->ringcadence) {
00514       ast_free(zone->ringcadence);
00515       zone->ringcadence = NULL;
00516    }
00517 }
00518 
00519 /* add a new country, if country exists, it will be replaced. */
00520 static int ast_register_indication_country(struct ast_tone_zone *zone)
00521 {
00522    ao2_lock(ast_tone_zones);
00523    if (!default_tone_zone) {
00524       default_tone_zone = ast_tone_zone_ref(zone);
00525    }
00526    ao2_unlock(ast_tone_zones);
00527 
00528    ao2_link(ast_tone_zones, zone);
00529 
00530    ast_verb(3, "Registered indication country '%s'\n", zone->country);
00531 
00532    return 0;
00533 }
00534 
00535 /* remove an existing country and all its indications, country must exist. */
00536 static int ast_unregister_indication_country(const char *country)
00537 {
00538    struct ast_tone_zone *tz = NULL;
00539    struct ast_tone_zone zone_arg = {
00540       .nrringcadence = 0,
00541    };
00542 
00543    ast_copy_string(zone_arg.country, country, sizeof(zone_arg.country));
00544 
00545    if (!(tz = ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER))) {
00546       return -1;
00547    }
00548 
00549    ao2_lock(ast_tone_zones);
00550    if (default_tone_zone == tz) {
00551       ast_tone_zone_unref(default_tone_zone);
00552       /* Get a new default, punt to the first one we find */
00553       default_tone_zone = ao2_callback(ast_tone_zones, 0, NULL, NULL);
00554    }
00555    ao2_unlock(ast_tone_zones);
00556 
00557    ao2_unlink(ast_tone_zones, tz);
00558 
00559    tz = ast_tone_zone_unref(tz);
00560 
00561    return 0;
00562 }
00563 
00564 /*!
00565  * \note called with the tone zone locked
00566  */
00567 static int ast_register_indication(struct ast_tone_zone *zone, const char *indication,
00568       const char *tonelist)
00569 {
00570    struct ast_tone_zone_sound *ts;
00571 
00572    if (ast_strlen_zero(indication) || ast_strlen_zero(tonelist)) {
00573       return -1;
00574    }
00575 
00576    AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, ts, entry) {
00577       if (!strcasecmp(indication, ts->name)) {
00578          AST_LIST_REMOVE_CURRENT(entry);
00579          ts = ast_tone_zone_sound_unref(ts);
00580          break;
00581       }
00582    }
00583    AST_LIST_TRAVERSE_SAFE_END;
00584 
00585    if (!(ts = ao2_alloc(sizeof(*ts), ast_tone_zone_sound_destructor))) {
00586       return -1;
00587    }
00588 
00589    if (!(ts->name = ast_strdup(indication)) || !(ts->data = ast_strdup(tonelist))) {
00590       ts = ast_tone_zone_sound_unref(ts);
00591       return -1;
00592    }
00593 
00594    AST_LIST_INSERT_TAIL(&zone->tones, ts, entry); /* Inherit reference */
00595 
00596    return 0;
00597 }
00598 
00599 /* remove an existing country's indication. Both country and indication must exist */
00600 static int ast_unregister_indication(struct ast_tone_zone *zone, const char *indication)
00601 {
00602    struct ast_tone_zone_sound *ts;
00603    int res = -1;
00604 
00605    ast_tone_zone_lock(zone);
00606 
00607    AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, ts, entry) {
00608       if (!strcasecmp(indication, ts->name)) {
00609          AST_LIST_REMOVE_CURRENT(entry);
00610          ts = ast_tone_zone_sound_unref(ts);
00611          res = 0;
00612          break;
00613       }
00614    }
00615    AST_LIST_TRAVERSE_SAFE_END;
00616 
00617    ast_tone_zone_unlock(zone);
00618 
00619    return res;
00620 }
00621 
00622 static struct ast_tone_zone *ast_tone_zone_alloc(void)
00623 {
00624    return ao2_alloc(sizeof(struct ast_tone_zone), ast_tone_zone_destructor);
00625 }
00626 
00627 static char *complete_country(struct ast_cli_args *a)
00628 {
00629    char *res = NULL;
00630    struct ao2_iterator i;
00631    int which = 0;
00632    size_t wordlen;
00633    struct ast_tone_zone *tz;
00634 
00635    wordlen = strlen(a->word);
00636 
00637    i = ao2_iterator_init(ast_tone_zones, 0);
00638    while ((tz = ao2_iterator_next(&i))) {
00639       if (!strncasecmp(a->word, tz->country, wordlen) && ++which > a->n) {
00640          res = ast_strdup(tz->country);
00641       }
00642       tz = ast_tone_zone_unref(tz);
00643       if (res) {
00644          break;
00645       }
00646    }
00647 
00648    return res;
00649 }
00650 
00651 static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00652 {
00653    struct ast_tone_zone *tz;
00654    int created_country = 0;
00655    char *res = CLI_SUCCESS;
00656 
00657    switch (cmd) {
00658    case CLI_INIT:
00659       e->command = "indication add";
00660       e->usage =
00661          "Usage: indication add <country> <indication> \"<tonelist>\"\n"
00662          "       Add the given indication to the country.\n";
00663       return NULL;
00664    case CLI_GENERATE:
00665       if (a->pos == 2) {
00666          return complete_country(a);
00667       } else {
00668          return NULL;
00669       }
00670    }
00671 
00672    if (a->argc != 5) {
00673       return CLI_SHOWUSAGE;
00674    }
00675 
00676    if (!(tz = ast_get_indication_zone(a->argv[2]))) {
00677       /* country does not exist, create it */
00678       ast_log(LOG_NOTICE, "Country '%s' does not exist, creating it.\n", a->argv[2]);
00679 
00680       if (!(tz = ast_tone_zone_alloc())) {
00681          return CLI_FAILURE;
00682       }
00683 
00684       ast_copy_string(tz->country, a->argv[2], sizeof(tz->country));
00685 
00686       if (ast_register_indication_country(tz)) {
00687          ast_log(LOG_WARNING, "Unable to register new country\n");
00688          tz = ast_tone_zone_unref(tz);
00689          return CLI_FAILURE;
00690       }
00691 
00692       created_country = 1;
00693    }
00694 
00695    ast_tone_zone_lock(tz);
00696 
00697    if (ast_register_indication(tz, a->argv[3], a->argv[4])) {
00698       ast_log(LOG_WARNING, "Unable to register indication %s/%s\n", a->argv[2], a->argv[3]);
00699       if (created_country) {
00700          ast_unregister_indication_country(a->argv[2]);
00701       }
00702       res = CLI_FAILURE;
00703    }
00704 
00705    ast_tone_zone_unlock(tz);
00706 
00707    tz = ast_tone_zone_unref(tz);
00708 
00709    return res;
00710 }
00711 
00712 static char *complete_indications(struct ast_cli_args *a)
00713 {
00714    char *res = NULL;
00715    int which = 0;
00716    size_t wordlen;
00717    struct ast_tone_zone_sound *ts;
00718    struct ast_tone_zone *tz, tmp_tz = {
00719       .nrringcadence = 0,
00720    };
00721 
00722    ast_copy_string(tmp_tz.country, a->argv[a->pos - 1], sizeof(tmp_tz.country));
00723 
00724    if (!(tz = ao2_find(ast_tone_zones, &tmp_tz, OBJ_POINTER))) {
00725       return NULL;
00726    }
00727 
00728    wordlen = strlen(a->word);
00729 
00730    ast_tone_zone_lock(tz);
00731    AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
00732       if (!strncasecmp(a->word, ts->name, wordlen) && ++which > a->n) {
00733          res = ast_strdup(ts->name);
00734          break;
00735       }
00736    }
00737    ast_tone_zone_unlock(tz);
00738 
00739    tz = ast_tone_zone_unref(tz);
00740 
00741    return res;
00742 }
00743 
00744 static char *handle_cli_indication_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00745 {
00746    struct ast_tone_zone *tz;
00747    char *res = CLI_SUCCESS;
00748 
00749    switch (cmd) {
00750    case CLI_INIT:
00751       e->command = "indication remove";
00752       e->usage =
00753          "Usage: indication remove <country> [indication]\n"
00754          "       Remove the given indication from the country.\n";
00755       return NULL;
00756    case CLI_GENERATE:
00757       if (a->pos == 2) {
00758          return complete_country(a);
00759       } else if (a->pos == 3) {
00760          return complete_indications(a);
00761       }
00762    }
00763 
00764    if (a->argc != 3 && a->argc != 4) {
00765       return CLI_SHOWUSAGE;
00766    }
00767 
00768    if (a->argc == 3) {
00769       /* remove entire country */
00770       if (ast_unregister_indication_country(a->argv[2])) {
00771          ast_log(LOG_WARNING, "Unable to unregister indication country %s\n", a->argv[2]);
00772          return CLI_FAILURE;
00773       }
00774 
00775       return CLI_SUCCESS;
00776    }
00777 
00778    if (!(tz = ast_get_indication_zone(a->argv[2]))) {
00779       ast_log(LOG_WARNING, "Unable to unregister indication %s/%s, country does not exists\n", a->argv[2], a->argv[3]);
00780       return CLI_FAILURE;
00781    }
00782 
00783    if (ast_unregister_indication(tz, a->argv[3])) {
00784       ast_log(LOG_WARNING, "Unable to unregister indication %s/%s\n", a->argv[2], a->argv[3]);
00785       res = CLI_FAILURE;
00786    }
00787 
00788    tz = ast_tone_zone_unref(tz);
00789 
00790    return res;
00791 }
00792 
00793 static char *handle_cli_indication_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00794 {
00795    struct ast_tone_zone *tz = NULL;
00796    struct ast_str *buf;
00797    int found_country = 0;
00798    int i;
00799 
00800    switch (cmd) {
00801    case CLI_INIT:
00802       e->command = "indication show";
00803       e->usage =
00804          "Usage: indication show [<country> ...]\n"
00805          "       Display either a condensed summary of all countries and indications, or a\n"
00806          "       more verbose list of indications for the specified countries.\n";
00807       return NULL;
00808    case CLI_GENERATE:
00809       return complete_country(a);
00810    }
00811 
00812    if (a->argc == 2) {
00813       struct ao2_iterator iter;
00814       /* no arguments, show a list of countries */
00815       ast_cli(a->fd, "Country   Description\n");
00816       ast_cli(a->fd, "===========================\n");
00817       iter = ast_tone_zone_iterator_init();
00818       while ((tz = ao2_iterator_next(&iter))) {
00819          ast_tone_zone_lock(tz);
00820          ast_cli(a->fd, "%-7.7s  %s\n", tz->country, tz->description);
00821          ast_tone_zone_unlock(tz);
00822          tz = ast_tone_zone_unref(tz);
00823       }
00824       return CLI_SUCCESS;
00825    }
00826 
00827    buf = ast_str_alloca(256);
00828 
00829    for (i = 2; i < a->argc; i++) {
00830       struct ast_tone_zone zone_arg = {
00831          .nrringcadence = 0,
00832       };
00833       struct ast_tone_zone_sound *ts;
00834       int j;
00835 
00836       ast_copy_string(zone_arg.country, a->argv[i], sizeof(zone_arg.country));
00837 
00838       if (!(tz = ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER))) {
00839          continue;
00840       }
00841 
00842       if (!found_country) {
00843          found_country = 1;
00844          ast_cli(a->fd, "Country Indication      PlayList\n");
00845          ast_cli(a->fd, "=====================================\n");
00846       }
00847 
00848       ast_tone_zone_lock(tz);
00849 
00850       ast_str_set(&buf, 0, "%-7.7s %-15.15s ", tz->country, "<ringcadence>");
00851       for (j = 0; j < tz->nrringcadence; j++) {
00852          ast_str_append(&buf, 0, "%d%s", tz->ringcadence[j],
00853                (j == tz->nrringcadence - 1) ? "" : ",");
00854       }
00855       ast_str_append(&buf, 0, "\n");
00856       ast_cli(a->fd, "%s", buf->str);
00857 
00858       AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
00859          ast_cli(a->fd, "%-7.7s %-15.15s %s\n", tz->country, ts->name, ts->data);
00860       }
00861 
00862       ast_tone_zone_unlock(tz);
00863       tz = ast_tone_zone_unref(tz);
00864    }
00865 
00866    if (!found_country) {
00867       ast_cli(a->fd, "No countries matched your criteria.\n");
00868    }
00869 
00870    return CLI_SUCCESS;
00871 }
00872 
00873 static int is_valid_tone_zone(struct ast_tone_zone *zone)
00874 {
00875    int res;
00876 
00877    ast_tone_zone_lock(zone);
00878    res = (!ast_strlen_zero(zone->description) && !AST_LIST_EMPTY(&zone->tones));
00879    ast_tone_zone_unlock(zone);
00880 
00881    return res;
00882 }
00883 
00884 /*!
00885  * \note This is called with the tone zone locked.
00886  */
00887 static void store_tone_zone_ring_cadence(struct ast_tone_zone *zone, const char *val)
00888 {
00889    char buf[1024];
00890    char *ring, *c = buf;
00891 
00892    ast_copy_string(buf, val, sizeof(buf));
00893 
00894    while ((ring = strsep(&c, ","))) {
00895       int *tmp, val;
00896 
00897       ring = ast_strip(ring);
00898 
00899       if (!isdigit(ring[0]) || (val = atoi(ring)) == -1) {
00900          ast_log(LOG_WARNING, "Invalid ringcadence given '%s'.\n", ring);
00901          continue;
00902       }
00903 
00904       if (!(tmp = ast_realloc(zone->ringcadence, (zone->nrringcadence + 1) * sizeof(int)))) {
00905          return;
00906       }
00907 
00908       zone->ringcadence = tmp;
00909       tmp[zone->nrringcadence] = val;
00910       zone->nrringcadence++;
00911    }
00912 }
00913 
00914 static void store_config_tone_zone(struct ast_tone_zone *zone, const char *var,
00915       const char *value)
00916 {
00917    CV_START(var, value);
00918 
00919    CV_STR("description", zone->description);
00920    CV_F("ringcadence", store_tone_zone_ring_cadence(zone, value));
00921    CV_F("ringcadance", store_tone_zone_ring_cadence(zone, value));
00922 
00923    ast_register_indication(zone, var, value);
00924 
00925    CV_END;
00926 }
00927 
00928 static void reset_tone_zone(struct ast_tone_zone *zone)
00929 {
00930    ast_tone_zone_lock(zone);
00931 
00932    zone->killme = 0;
00933 
00934    if (zone->nrringcadence) {
00935       zone->nrringcadence = 0;
00936       ast_free(zone->ringcadence);
00937       zone->ringcadence = NULL;
00938    }
00939 
00940    ast_tone_zone_unlock(zone);
00941 }
00942 
00943 static int parse_tone_zone(struct ast_config *cfg, const char *country)
00944 {
00945    struct ast_variable *v;
00946    struct ast_tone_zone *zone;
00947    struct ast_tone_zone tmp_zone = {
00948       .nrringcadence = 0,
00949    };
00950    int allocd = 0;
00951 
00952    ast_copy_string(tmp_zone.country, country, sizeof(tmp_zone.country));
00953 
00954    if ((zone = ao2_find(ast_tone_zones, &tmp_zone, OBJ_POINTER))) {
00955       reset_tone_zone(zone);
00956    } else if ((zone = ast_tone_zone_alloc())) {
00957       allocd = 1;
00958       ast_copy_string(zone->country, country, sizeof(zone->country));
00959    } else {
00960       return -1;
00961    }
00962 
00963    ast_tone_zone_lock(zone);
00964    for (v = ast_variable_browse(cfg, country); v; v = v->next) {
00965       store_config_tone_zone(zone, v->name, v->value);
00966    }
00967    ast_tone_zone_unlock(zone);
00968 
00969    if (allocd) {
00970       if (!is_valid_tone_zone(zone)) {
00971          ast_log(LOG_WARNING, "Indication country '%s' is invalid\n", country);
00972       } else if (ast_register_indication_country(zone)) {
00973          ast_log(LOG_WARNING, "Unable to register indication country '%s'.\n",
00974                country);
00975       }
00976    }
00977 
00978    zone = ast_tone_zone_unref(zone);
00979 
00980    return 0;
00981 }
00982 
00983 /*!
00984  * Mark the zone and its tones before parsing configuration.  We will use this
00985  * to know what to remove after configuration is parsed.
00986  */
00987 static int tone_zone_mark(void *obj, void *arg, int flags)
00988 {
00989    struct ast_tone_zone *zone = obj;
00990    struct ast_tone_zone_sound *s;
00991 
00992    ast_tone_zone_lock(zone);
00993 
00994    zone->killme = 1;
00995 
00996    AST_LIST_TRAVERSE(&zone->tones, s, entry) {
00997       s->killme = 1;
00998    }
00999 
01000    ast_tone_zone_unlock(zone);
01001 
01002    return 0;
01003 }
01004 
01005 /*!
01006  * Prune tones no longer in the configuration, and have the tone zone unlinked
01007  * if it is no longer in the configuration at all.
01008  */
01009 static int prune_tone_zone(void *obj, void *arg, int flags)
01010 {
01011    struct ast_tone_zone *zone = obj;
01012    struct ast_tone_zone_sound *s;
01013 
01014    ast_tone_zone_lock(zone);
01015 
01016    AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, s, entry) {
01017       if (s->killme) {
01018          AST_LIST_REMOVE_CURRENT(entry);
01019          s = ast_tone_zone_sound_unref(s);
01020       }
01021    }
01022    AST_LIST_TRAVERSE_SAFE_END;
01023 
01024    ast_tone_zone_unlock(zone);
01025 
01026    return zone->killme ? CMP_MATCH : 0;
01027 }
01028 
01029 /*! \brief load indications module */
01030 static int load_indications(int reload)
01031 {
01032    struct ast_config *cfg;
01033    const char *cxt = NULL;
01034    const char *country = NULL;
01035    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01036    int res = -1;
01037 
01038    cfg = ast_config_load2(config, "indications", config_flags);
01039 
01040    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01041       ast_log(LOG_WARNING, "Can't find indications config file %s.\n", config);
01042       return 0;
01043    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01044       return 0;
01045    }
01046 
01047    /* Lock the container to prevent multiple simultaneous reloads */
01048    ao2_lock(ast_tone_zones);
01049 
01050    ao2_callback(ast_tone_zones, OBJ_NODATA, tone_zone_mark, NULL);
01051 
01052    /* Use existing config to populate the Indication table */
01053    while ((cxt = ast_category_browse(cfg, cxt))) {
01054       /* All categories but "general" are considered countries */
01055       if (!strcasecmp(cxt, "general")) {
01056          continue;
01057       }
01058 
01059       if (parse_tone_zone(cfg, cxt)) {
01060          goto return_cleanup;
01061       }
01062    }
01063 
01064    ao2_callback(ast_tone_zones, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
01065          prune_tone_zone, NULL);
01066 
01067    /* determine which country is the default */
01068    country = ast_variable_retrieve(cfg, "general", "country");
01069    if (ast_strlen_zero(country) || ast_set_indication_country(country)) {
01070       ast_log(LOG_WARNING, "Unable to set the default country (for indication tones)\n");
01071    }
01072 
01073    res = 0;
01074 
01075 return_cleanup:
01076    ao2_unlock(ast_tone_zones);
01077    ast_config_destroy(cfg);
01078 
01079    return res;
01080 }
01081 
01082 /*! \brief CLI entries for commands provided by this module */
01083 static struct ast_cli_entry cli_indications[] = {
01084    AST_CLI_DEFINE(handle_cli_indication_add,    "Add the given indication to the country"),
01085    AST_CLI_DEFINE(handle_cli_indication_remove, "Remove the given indication from the country"),
01086    AST_CLI_DEFINE(handle_cli_indication_show,   "Display a list of all countries/indications")
01087 };
01088 
01089 static int ast_tone_zone_hash(const void *obj, const int flags)
01090 {
01091    const struct ast_tone_zone *zone = obj;
01092 
01093    return ast_str_case_hash(zone->country);
01094 }
01095 
01096 static int ast_tone_zone_cmp(void *obj, void *arg, int flags)
01097 {
01098    struct ast_tone_zone *zone = obj;
01099    struct ast_tone_zone *zone_arg = arg;
01100 
01101    return (!strcasecmp(zone->country, zone_arg->country)) ?
01102          CMP_MATCH | CMP_STOP : 0;
01103 }
01104 
01105 /*! \brief Load indications module */
01106 int ast_indications_init(void)
01107 {
01108    if (!(ast_tone_zones = ao2_container_alloc(NUM_TONE_ZONE_BUCKETS,
01109          ast_tone_zone_hash, ast_tone_zone_cmp))) {
01110       return -1;
01111    }
01112 
01113    if (load_indications(0)) {
01114       return -1;
01115    }
01116 
01117    ast_cli_register_multiple(cli_indications, ARRAY_LEN(cli_indications));
01118 
01119    return 0;
01120 }
01121 
01122 /*! \brief Reload indications module */
01123 int ast_indications_reload(void)
01124 {
01125    return load_indications(1);
01126 }
01127