Sat Apr 26 2014 22:01:37

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