Wed Jan 27 20:02:11 2016

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: 398757 $")
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    int 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    ps->origwfmt = chan->writeformat;
00155 
00156    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
00157       ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
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(chan, AST_FLAG_WRITE_INT);
00171    } else {
00172       ast_clear_flag(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    ps->f.subclass.codec = AST_FORMAT_SLINEAR;
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 /* Set global indication country */
00427 static int ast_set_indication_country(const char *country)
00428 {
00429    struct ast_tone_zone *zone = NULL;
00430 
00431    /* If no country is specified or we are unable to find the zone, then return not found */
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 /* 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    return ts;
00506 }
00507 
00508 static void ast_tone_zone_sound_destructor(void *obj)
00509 {
00510    struct ast_tone_zone_sound *ts = obj;
00511 
00512    /* Deconstify the 'const char *'s so the compiler doesn't complain. (but it's safe) */
00513    if (ts->name) {
00514       ast_free((char *) ts->name);
00515       ts->name = NULL;
00516    }
00517 
00518    if (ts->data) {
00519       ast_free((char *) ts->data);
00520       ts->data = NULL;
00521    }
00522 }
00523 
00524 /*! \brief deallocate the passed tone zone */
00525 static void ast_tone_zone_destructor(void *obj)
00526 {
00527    struct ast_tone_zone *zone = obj;
00528    struct ast_tone_zone_sound *current;
00529 
00530    while ((current = AST_LIST_REMOVE_HEAD(&zone->tones, entry))) {
00531       current = ast_tone_zone_sound_unref(current);
00532    }
00533 
00534    if (zone->ringcadence) {
00535       ast_free(zone->ringcadence);
00536       zone->ringcadence = NULL;
00537    }
00538 }
00539 
00540 /* add a new country, if country exists, it will be replaced. */
00541 static int ast_register_indication_country(struct ast_tone_zone *zone)
00542 {
00543    ao2_lock(ast_tone_zones);
00544    if (!default_tone_zone) {
00545       default_tone_zone = ast_tone_zone_ref(zone);
00546    }
00547    ao2_unlock(ast_tone_zones);
00548 
00549    ao2_link(ast_tone_zones, zone);
00550 
00551    ast_verb(3, "Registered indication country '%s'\n", zone->country);
00552 
00553    return 0;
00554 }
00555 
00556 /* remove an existing country and all its indications, country must exist. */
00557 static int ast_unregister_indication_country(const char *country)
00558 {
00559    struct ast_tone_zone *tz = NULL;
00560    struct ast_tone_zone zone_arg = {
00561       .nrringcadence = 0,
00562    };
00563 
00564    ast_copy_string(zone_arg.country, country, sizeof(zone_arg.country));
00565 
00566    ao2_lock(ast_tone_zones);
00567    tz = ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER | OBJ_UNLINK);
00568    if (!tz) {
00569       ao2_unlock(ast_tone_zones);
00570       return -1;
00571    }
00572 
00573    if (default_tone_zone == tz) {
00574       ast_tone_zone_unref(default_tone_zone);
00575       /* Get a new default, punt to the first one we find */
00576       default_tone_zone = ao2_callback(ast_tone_zones, 0, NULL, NULL);
00577    }
00578    ao2_unlock(ast_tone_zones);
00579 
00580    tz = ast_tone_zone_unref(tz);
00581 
00582    return 0;
00583 }
00584 
00585 /*!
00586  * \note called with the tone zone locked
00587  */
00588 static int ast_register_indication(struct ast_tone_zone *zone, const char *indication,
00589       const char *tonelist)
00590 {
00591    struct ast_tone_zone_sound *ts;
00592 
00593    if (ast_strlen_zero(indication) || ast_strlen_zero(tonelist)) {
00594       return -1;
00595    }
00596 
00597    AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, ts, entry) {
00598       if (!strcasecmp(indication, ts->name)) {
00599          AST_LIST_REMOVE_CURRENT(entry);
00600          ts = ast_tone_zone_sound_unref(ts);
00601          break;
00602       }
00603    }
00604    AST_LIST_TRAVERSE_SAFE_END;
00605 
00606    if (!(ts = ao2_alloc(sizeof(*ts), ast_tone_zone_sound_destructor))) {
00607       return -1;
00608    }
00609 
00610    if (!(ts->name = ast_strdup(indication)) || !(ts->data = ast_strdup(tonelist))) {
00611       ts = ast_tone_zone_sound_unref(ts);
00612       return -1;
00613    }
00614 
00615    AST_LIST_INSERT_TAIL(&zone->tones, ts, entry); /* Inherit reference */
00616 
00617    return 0;
00618 }
00619 
00620 /* remove an existing country's indication. Both country and indication must exist */
00621 static int ast_unregister_indication(struct ast_tone_zone *zone, const char *indication)
00622 {
00623    struct ast_tone_zone_sound *ts;
00624    int res = -1;
00625 
00626    ast_tone_zone_lock(zone);
00627 
00628    AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, ts, entry) {
00629       if (!strcasecmp(indication, ts->name)) {
00630          AST_LIST_REMOVE_CURRENT(entry);
00631          ts = ast_tone_zone_sound_unref(ts);
00632          res = 0;
00633          break;
00634       }
00635    }
00636    AST_LIST_TRAVERSE_SAFE_END;
00637 
00638    ast_tone_zone_unlock(zone);
00639 
00640    return res;
00641 }
00642 
00643 static struct ast_tone_zone *ast_tone_zone_alloc(void)
00644 {
00645    return ao2_alloc(sizeof(struct ast_tone_zone), ast_tone_zone_destructor);
00646 }
00647 
00648 static char *complete_country(struct ast_cli_args *a)
00649 {
00650    char *res = NULL;
00651    struct ao2_iterator i;
00652    int which = 0;
00653    size_t wordlen;
00654    struct ast_tone_zone *tz;
00655 
00656    wordlen = strlen(a->word);
00657 
00658    i = ao2_iterator_init(ast_tone_zones, 0);
00659    while ((tz = ao2_iterator_next(&i))) {
00660       if (!strncasecmp(a->word, tz->country, wordlen) && ++which > a->n) {
00661          res = ast_strdup(tz->country);
00662       }
00663       tz = ast_tone_zone_unref(tz);
00664       if (res) {
00665          break;
00666       }
00667    }
00668    ao2_iterator_destroy(&i);
00669 
00670    return res;
00671 }
00672 
00673 static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00674 {
00675    struct ast_tone_zone *tz;
00676    int created_country = 0;
00677    char *res = CLI_SUCCESS;
00678 
00679    switch (cmd) {
00680    case CLI_INIT:
00681       e->command = "indication add";
00682       e->usage =
00683          "Usage: indication add <country> <indication> \"<tonelist>\"\n"
00684          "       Add the given indication to the country.\n";
00685       return NULL;
00686    case CLI_GENERATE:
00687       if (a->pos == 2) {
00688          return complete_country(a);
00689       } else {
00690          return NULL;
00691       }
00692    }
00693 
00694    if (a->argc != 5) {
00695       return CLI_SHOWUSAGE;
00696    }
00697 
00698    if (!(tz = ast_get_indication_zone(a->argv[2]))) {
00699       /* country does not exist, create it */
00700       ast_log(LOG_NOTICE, "Country '%s' does not exist, creating it.\n", a->argv[2]);
00701 
00702       if (!(tz = ast_tone_zone_alloc())) {
00703          return CLI_FAILURE;
00704       }
00705 
00706       ast_copy_string(tz->country, a->argv[2], sizeof(tz->country));
00707 
00708       if (ast_register_indication_country(tz)) {
00709          ast_log(LOG_WARNING, "Unable to register new country\n");
00710          tz = ast_tone_zone_unref(tz);
00711          return CLI_FAILURE;
00712       }
00713 
00714       created_country = 1;
00715    }
00716 
00717    ast_tone_zone_lock(tz);
00718 
00719    if (ast_register_indication(tz, a->argv[3], a->argv[4])) {
00720       ast_log(LOG_WARNING, "Unable to register indication %s/%s\n", a->argv[2], a->argv[3]);
00721       if (created_country) {
00722          ast_unregister_indication_country(a->argv[2]);
00723       }
00724       res = CLI_FAILURE;
00725    }
00726 
00727    ast_tone_zone_unlock(tz);
00728 
00729    tz = ast_tone_zone_unref(tz);
00730 
00731    return res;
00732 }
00733 
00734 static char *complete_indications(struct ast_cli_args *a)
00735 {
00736    char *res = NULL;
00737    int which = 0;
00738    size_t wordlen;
00739    struct ast_tone_zone_sound *ts;
00740    struct ast_tone_zone *tz, tmp_tz = {
00741       .nrringcadence = 0,
00742    };
00743 
00744    ast_copy_string(tmp_tz.country, a->argv[a->pos - 1], sizeof(tmp_tz.country));
00745 
00746    if (!(tz = ao2_find(ast_tone_zones, &tmp_tz, OBJ_POINTER))) {
00747       return NULL;
00748    }
00749 
00750    wordlen = strlen(a->word);
00751 
00752    ast_tone_zone_lock(tz);
00753    AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
00754       if (!strncasecmp(a->word, ts->name, wordlen) && ++which > a->n) {
00755          res = ast_strdup(ts->name);
00756          break;
00757       }
00758    }
00759    ast_tone_zone_unlock(tz);
00760 
00761    tz = ast_tone_zone_unref(tz);
00762 
00763    return res;
00764 }
00765 
00766 static char *handle_cli_indication_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00767 {
00768    struct ast_tone_zone *tz;
00769    char *res = CLI_SUCCESS;
00770 
00771    switch (cmd) {
00772    case CLI_INIT:
00773       e->command = "indication remove";
00774       e->usage =
00775          "Usage: indication remove <country> [indication]\n"
00776          "       Remove the given indication from the country.\n";
00777       return NULL;
00778    case CLI_GENERATE:
00779       if (a->pos == 2) {
00780          return complete_country(a);
00781       } else if (a->pos == 3) {
00782          return complete_indications(a);
00783       }
00784    }
00785 
00786    if (a->argc != 3 && a->argc != 4) {
00787       return CLI_SHOWUSAGE;
00788    }
00789 
00790    if (a->argc == 3) {
00791       /* remove entire country */
00792       if (ast_unregister_indication_country(a->argv[2])) {
00793          ast_log(LOG_WARNING, "Unable to unregister indication country %s\n", a->argv[2]);
00794          return CLI_FAILURE;
00795       }
00796 
00797       return CLI_SUCCESS;
00798    }
00799 
00800    if (!(tz = ast_get_indication_zone(a->argv[2]))) {
00801       ast_log(LOG_WARNING, "Unable to unregister indication %s/%s, country does not exists\n", a->argv[2], a->argv[3]);
00802       return CLI_FAILURE;
00803    }
00804 
00805    if (ast_unregister_indication(tz, a->argv[3])) {
00806       ast_log(LOG_WARNING, "Unable to unregister indication %s/%s\n", a->argv[2], a->argv[3]);
00807       res = CLI_FAILURE;
00808    }
00809 
00810    tz = ast_tone_zone_unref(tz);
00811 
00812    return res;
00813 }
00814 
00815 static char *handle_cli_indication_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00816 {
00817    struct ast_tone_zone *tz = NULL;
00818    struct ast_str *buf;
00819    int found_country = 0;
00820    int i;
00821 
00822    switch (cmd) {
00823    case CLI_INIT:
00824       e->command = "indication show";
00825       e->usage =
00826          "Usage: indication show [<country> ...]\n"
00827          "       Display either a condensed summary of all countries and indications, or a\n"
00828          "       more verbose list of indications for the specified countries.\n";
00829       return NULL;
00830    case CLI_GENERATE:
00831       return complete_country(a);
00832    }
00833 
00834    if (a->argc == 2) {
00835       struct ao2_iterator iter;
00836       /* no arguments, show a list of countries */
00837       ast_cli(a->fd, "Country   Description\n");
00838       ast_cli(a->fd, "===========================\n");
00839       iter = ast_tone_zone_iterator_init();
00840       while ((tz = ao2_iterator_next(&iter))) {
00841          ast_tone_zone_lock(tz);
00842          ast_cli(a->fd, "%-7.7s  %s\n", tz->country, tz->description);
00843          ast_tone_zone_unlock(tz);
00844          tz = ast_tone_zone_unref(tz);
00845       }
00846       ao2_iterator_destroy(&iter);
00847       return CLI_SUCCESS;
00848    }
00849 
00850    buf = ast_str_alloca(256);
00851 
00852    for (i = 2; i < a->argc; i++) {
00853       struct ast_tone_zone zone_arg = {
00854          .nrringcadence = 0,
00855       };
00856       struct ast_tone_zone_sound *ts;
00857       int j;
00858 
00859       ast_copy_string(zone_arg.country, a->argv[i], sizeof(zone_arg.country));
00860 
00861       if (!(tz = ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER))) {
00862          continue;
00863       }
00864 
00865       if (!found_country) {
00866          found_country = 1;
00867          ast_cli(a->fd, "Country Indication      PlayList\n");
00868          ast_cli(a->fd, "=====================================\n");
00869       }
00870 
00871       ast_tone_zone_lock(tz);
00872 
00873       ast_str_set(&buf, 0, "%-7.7s %-15.15s ", tz->country, "<ringcadence>");
00874       for (j = 0; j < tz->nrringcadence; j++) {
00875          ast_str_append(&buf, 0, "%d%s", tz->ringcadence[j],
00876                (j == tz->nrringcadence - 1) ? "" : ",");
00877       }
00878       ast_str_append(&buf, 0, "\n");
00879       ast_cli(a->fd, "%s", ast_str_buffer(buf));
00880 
00881       AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
00882          ast_cli(a->fd, "%-7.7s %-15.15s %s\n", tz->country, ts->name, ts->data);
00883       }
00884 
00885       ast_tone_zone_unlock(tz);
00886       tz = ast_tone_zone_unref(tz);
00887    }
00888 
00889    if (!found_country) {
00890       ast_cli(a->fd, "No countries matched your criteria.\n");
00891    }
00892 
00893    return CLI_SUCCESS;
00894 }
00895 
00896 static int is_valid_tone_zone(struct ast_tone_zone *zone)
00897 {
00898    int res;
00899 
00900    ast_tone_zone_lock(zone);
00901    res = (!ast_strlen_zero(zone->description) && !AST_LIST_EMPTY(&zone->tones));
00902    ast_tone_zone_unlock(zone);
00903 
00904    return res;
00905 }
00906 
00907 /*!
00908  * \note This is called with the tone zone locked.
00909  */
00910 static void store_tone_zone_ring_cadence(struct ast_tone_zone *zone, const char *val)
00911 {
00912    char buf[1024];
00913    char *ring, *c = buf;
00914 
00915    ast_copy_string(buf, val, sizeof(buf));
00916 
00917    while ((ring = strsep(&c, ","))) {
00918       int *tmp, val;
00919 
00920       ring = ast_strip(ring);
00921 
00922       if (!isdigit(ring[0]) || (val = atoi(ring)) == -1) {
00923          ast_log(LOG_WARNING, "Invalid ringcadence given '%s'.\n", ring);
00924          continue;
00925       }
00926 
00927       if (!(tmp = ast_realloc(zone->ringcadence, (zone->nrringcadence + 1) * sizeof(int)))) {
00928          return;
00929       }
00930 
00931       zone->ringcadence = tmp;
00932       tmp[zone->nrringcadence] = val;
00933       zone->nrringcadence++;
00934    }
00935 }
00936 
00937 static void store_config_tone_zone(struct ast_tone_zone *zone, const char *var,
00938       const char *value)
00939 {
00940    CV_START(var, value);
00941 
00942    CV_STR("description", zone->description);
00943    CV_F("ringcadence", store_tone_zone_ring_cadence(zone, value));
00944 
00945    ast_register_indication(zone, var, value);
00946 
00947    CV_END;
00948 }
00949 
00950 static void reset_tone_zone(struct ast_tone_zone *zone)
00951 {
00952    ast_tone_zone_lock(zone);
00953 
00954    zone->killme = 0;
00955 
00956    if (zone->nrringcadence) {
00957       zone->nrringcadence = 0;
00958       ast_free(zone->ringcadence);
00959       zone->ringcadence = NULL;
00960    }
00961 
00962    ast_tone_zone_unlock(zone);
00963 }
00964 
00965 static int parse_tone_zone(struct ast_config *cfg, const char *country)
00966 {
00967    struct ast_variable *v;
00968    struct ast_tone_zone *zone;
00969    struct ast_tone_zone tmp_zone = {
00970       .nrringcadence = 0,
00971    };
00972    int allocd = 0;
00973 
00974    ast_copy_string(tmp_zone.country, country, sizeof(tmp_zone.country));
00975 
00976    if ((zone = ao2_find(ast_tone_zones, &tmp_zone, OBJ_POINTER))) {
00977       reset_tone_zone(zone);
00978    } else if ((zone = ast_tone_zone_alloc())) {
00979       allocd = 1;
00980       ast_copy_string(zone->country, country, sizeof(zone->country));
00981    } else {
00982       return -1;
00983    }
00984 
00985    ast_tone_zone_lock(zone);
00986    for (v = ast_variable_browse(cfg, country); v; v = v->next) {
00987       store_config_tone_zone(zone, v->name, v->value);
00988    }
00989    ast_tone_zone_unlock(zone);
00990 
00991    if (allocd) {
00992       if (!is_valid_tone_zone(zone)) {
00993          ast_log(LOG_WARNING, "Indication country '%s' is invalid\n", country);
00994       } else if (ast_register_indication_country(zone)) {
00995          ast_log(LOG_WARNING, "Unable to register indication country '%s'.\n",
00996                country);
00997       }
00998    }
00999 
01000    zone = ast_tone_zone_unref(zone);
01001 
01002    return 0;
01003 }
01004 
01005 /*!
01006  * Mark the zone and its tones before parsing configuration.  We will use this
01007  * to know what to remove after configuration is parsed.
01008  */
01009 static int tone_zone_mark(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    zone->killme = 1;
01017 
01018    AST_LIST_TRAVERSE(&zone->tones, s, entry) {
01019       s->killme = 1;
01020    }
01021 
01022    ast_tone_zone_unlock(zone);
01023 
01024    return 0;
01025 }
01026 
01027 /*!
01028  * Prune tones no longer in the configuration, and have the tone zone unlinked
01029  * if it is no longer in the configuration at all.
01030  */
01031 static int prune_tone_zone(void *obj, void *arg, int flags)
01032 {
01033    struct ast_tone_zone *zone = obj;
01034    struct ast_tone_zone_sound *s;
01035 
01036    ast_tone_zone_lock(zone);
01037 
01038    AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, s, entry) {
01039       if (s->killme) {
01040          AST_LIST_REMOVE_CURRENT(entry);
01041          s = ast_tone_zone_sound_unref(s);
01042       }
01043    }
01044    AST_LIST_TRAVERSE_SAFE_END;
01045 
01046    ast_tone_zone_unlock(zone);
01047 
01048    return zone->killme ? CMP_MATCH : 0;
01049 }
01050 
01051 /*! \brief load indications module */
01052 static int load_indications(int reload)
01053 {
01054    struct ast_config *cfg;
01055    const char *cxt = NULL;
01056    const char *country = NULL;
01057    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01058    int res = -1;
01059 
01060    cfg = ast_config_load2(config, "indications", config_flags);
01061 
01062    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01063       ast_log(LOG_WARNING, "Can't find indications config file %s.\n", config);
01064       return 0;
01065    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01066       return 0;
01067    }
01068 
01069    /* Lock the container to prevent multiple simultaneous reloads */
01070    ao2_lock(ast_tone_zones);
01071 
01072    ao2_callback(ast_tone_zones, OBJ_NODATA, tone_zone_mark, NULL);
01073 
01074    /* Use existing config to populate the Indication table */
01075    while ((cxt = ast_category_browse(cfg, cxt))) {
01076       /* All categories but "general" are considered countries */
01077       if (!strcasecmp(cxt, "general")) {
01078          continue;
01079       }
01080 
01081       if (parse_tone_zone(cfg, cxt)) {
01082          goto return_cleanup;
01083       }
01084    }
01085 
01086    ao2_callback(ast_tone_zones, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
01087          prune_tone_zone, NULL);
01088 
01089    /* determine which country is the default */
01090    country = ast_variable_retrieve(cfg, "general", "country");
01091    if (ast_strlen_zero(country) || ast_set_indication_country(country)) {
01092       ast_log(LOG_WARNING, "Unable to set the default country (for indication tones)\n");
01093    }
01094 
01095    res = 0;
01096 
01097 return_cleanup:
01098    ao2_unlock(ast_tone_zones);
01099    ast_config_destroy(cfg);
01100 
01101    return res;
01102 }
01103 
01104 /*! \brief CLI entries for commands provided by this module */
01105 static struct ast_cli_entry cli_indications[] = {
01106    AST_CLI_DEFINE(handle_cli_indication_add,    "Add the given indication to the country"),
01107    AST_CLI_DEFINE(handle_cli_indication_remove, "Remove the given indication from the country"),
01108    AST_CLI_DEFINE(handle_cli_indication_show,   "Display a list of all countries/indications")
01109 };
01110 
01111 static int ast_tone_zone_hash(const void *obj, const int flags)
01112 {
01113    const struct ast_tone_zone *zone = obj;
01114 
01115    return ast_str_case_hash(zone->country);
01116 }
01117 
01118 static int ast_tone_zone_cmp(void *obj, void *arg, int flags)
01119 {
01120    struct ast_tone_zone *zone = obj;
01121    struct ast_tone_zone *zone_arg = arg;
01122 
01123    return (!strcasecmp(zone->country, zone_arg->country)) ?
01124          CMP_MATCH | CMP_STOP : 0;
01125 }
01126 
01127 int ast_tone_zone_data_add_structure(struct ast_data *tree, struct ast_tone_zone *zone)
01128 {
01129    struct ast_data *data_zone_sound;
01130    struct ast_tone_zone_sound *s;
01131 
01132    ast_data_add_structure(ast_tone_zone, tree, zone);
01133 
01134    if (AST_LIST_EMPTY(&zone->tones)) {
01135       return 0;
01136    }
01137 
01138    data_zone_sound = ast_data_add_node(tree, "tones");
01139    if (!data_zone_sound) {
01140       return -1;
01141    }
01142 
01143    ast_tone_zone_lock(zone);
01144 
01145    AST_LIST_TRAVERSE(&zone->tones, s, entry) {
01146       ast_data_add_structure(ast_tone_zone_sound, data_zone_sound, s);
01147    }
01148 
01149    ast_tone_zone_unlock(zone);
01150 
01151    return 0;
01152 }
01153 
01154 /*! \internal \brief Clean up resources on Asterisk shutdown */
01155 static void indications_shutdown(void)
01156 {
01157    ast_cli_unregister_multiple(cli_indications, ARRAY_LEN(cli_indications));
01158    if (default_tone_zone) {
01159       ast_tone_zone_unref(default_tone_zone);
01160       default_tone_zone = NULL;
01161    }
01162    if (ast_tone_zones) {
01163       ao2_ref(ast_tone_zones, -1);
01164       ast_tone_zones = NULL;
01165    }
01166 }
01167 
01168 /*! \brief Load indications module */
01169 int ast_indications_init(void)
01170 {
01171    if (!(ast_tone_zones = ao2_container_alloc(NUM_TONE_ZONE_BUCKETS,
01172          ast_tone_zone_hash, ast_tone_zone_cmp))) {
01173       return -1;
01174    }
01175 
01176    if (load_indications(0)) {
01177       indications_shutdown();
01178       return -1;
01179    }
01180 
01181    ast_cli_register_multiple(cli_indications, ARRAY_LEN(cli_indications));
01182 
01183    ast_register_atexit(indications_shutdown);
01184    return 0;
01185 }
01186 
01187 /*! \brief Reload indications module */
01188 int ast_indications_reload(void)
01189 {
01190    return load_indications(1);
01191 }
01192 

Generated on 27 Jan 2016 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1