Sun May 20 06:33:54 2012

Asterisk developer's documentation


indications.c

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

Generated on Sun May 20 06:33:54 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6