Tue Aug 20 16:34:34 2013

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

Generated on 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1