Sat Mar 10 01:54:19 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: 352955 $")
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    int 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    ps->origwfmt = chan->writeformat;
00151 
00152    if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
00153       ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
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(chan, AST_FLAG_WRITE_INT);
00167    } else {
00168       ast_clear_flag(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    ps->f.subclass.codec = AST_FORMAT_SLINEAR;
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 /* Set global indication country */
00420 static int ast_set_indication_country(const char *country)
00421 {
00422    struct ast_tone_zone *zone = NULL;
00423 
00424    /* If no country is specified or we are unable to find the zone, then return not found */
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 /* 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 /* 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 /* 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 /* 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 /*!
00901  * \note This is called with the tone zone locked.
00902  */
00903 static void store_tone_zone_ring_cadence(struct ast_tone_zone *zone, const char *val)
00904 {
00905    char buf[1024];
00906    char *ring, *c = buf;
00907 
00908    ast_copy_string(buf, val, sizeof(buf));
00909 
00910    while ((ring = strsep(&c, ","))) {
00911       int *tmp, val;
00912 
00913       ring = ast_strip(ring);
00914 
00915       if (!isdigit(ring[0]) || (val = atoi(ring)) == -1) {
00916          ast_log(LOG_WARNING, "Invalid ringcadence given '%s'.\n", ring);
00917          continue;
00918       }
00919 
00920       if (!(tmp = ast_realloc(zone->ringcadence, (zone->nrringcadence + 1) * sizeof(int)))) {
00921          return;
00922       }
00923 
00924       zone->ringcadence = tmp;
00925       tmp[zone->nrringcadence] = val;
00926       zone->nrringcadence++;
00927    }
00928 }
00929 
00930 static void store_config_tone_zone(struct ast_tone_zone *zone, const char *var,
00931       const char *value)
00932 {
00933    CV_START(var, value);
00934 
00935    CV_STR("description", zone->description);
00936    CV_F("ringcadence", store_tone_zone_ring_cadence(zone, value));
00937    CV_F("ringcadance", store_tone_zone_ring_cadence(zone, value));
00938 
00939    ast_register_indication(zone, var, value);
00940 
00941    CV_END;
00942 }
00943 
00944 static void reset_tone_zone(struct ast_tone_zone *zone)
00945 {
00946    ast_tone_zone_lock(zone);
00947 
00948    zone->killme = 0;
00949 
00950    if (zone->nrringcadence) {
00951       zone->nrringcadence = 0;
00952       ast_free(zone->ringcadence);
00953       zone->ringcadence = NULL;
00954    }
00955 
00956    ast_tone_zone_unlock(zone);
00957 }
00958 
00959 static int parse_tone_zone(struct ast_config *cfg, const char *country)
00960 {
00961    struct ast_variable *v;
00962    struct ast_tone_zone *zone;
00963    struct ast_tone_zone tmp_zone = {
00964       .nrringcadence = 0,
00965    };
00966    int allocd = 0;
00967 
00968    ast_copy_string(tmp_zone.country, country, sizeof(tmp_zone.country));
00969 
00970    if ((zone = ao2_find(ast_tone_zones, &tmp_zone, OBJ_POINTER))) {
00971       reset_tone_zone(zone);
00972    } else if ((zone = ast_tone_zone_alloc())) {
00973       allocd = 1;
00974       ast_copy_string(zone->country, country, sizeof(zone->country));
00975    } else {
00976       return -1;
00977    }
00978 
00979    ast_tone_zone_lock(zone);
00980    for (v = ast_variable_browse(cfg, country); v; v = v->next) {
00981       store_config_tone_zone(zone, v->name, v->value);
00982    }
00983    ast_tone_zone_unlock(zone);
00984 
00985    if (allocd) {
00986       if (!is_valid_tone_zone(zone)) {
00987          ast_log(LOG_WARNING, "Indication country '%s' is invalid\n", country);
00988       } else if (ast_register_indication_country(zone)) {
00989          ast_log(LOG_WARNING, "Unable to register indication country '%s'.\n",
00990                country);
00991       }
00992    }
00993 
00994    zone = ast_tone_zone_unref(zone);
00995 
00996    return 0;
00997 }
00998 
00999 /*!
01000  * Mark the zone and its tones before parsing configuration.  We will use this
01001  * to know what to remove after configuration is parsed.
01002  */
01003 static int tone_zone_mark(void *obj, void *arg, int flags)
01004 {
01005    struct ast_tone_zone *zone = obj;
01006    struct ast_tone_zone_sound *s;
01007 
01008    ast_tone_zone_lock(zone);
01009 
01010    zone->killme = 1;
01011 
01012    AST_LIST_TRAVERSE(&zone->tones, s, entry) {
01013       s->killme = 1;
01014    }
01015 
01016    ast_tone_zone_unlock(zone);
01017 
01018    return 0;
01019 }
01020 
01021 /*!
01022  * Prune tones no longer in the configuration, and have the tone zone unlinked
01023  * if it is no longer in the configuration at all.
01024  */
01025 static int prune_tone_zone(void *obj, void *arg, int flags)
01026 {
01027    struct ast_tone_zone *zone = obj;
01028    struct ast_tone_zone_sound *s;
01029 
01030    ast_tone_zone_lock(zone);
01031 
01032    AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, s, entry) {
01033       if (s->killme) {
01034          AST_LIST_REMOVE_CURRENT(entry);
01035          s = ast_tone_zone_sound_unref(s);
01036       }
01037    }
01038    AST_LIST_TRAVERSE_SAFE_END;
01039 
01040    ast_tone_zone_unlock(zone);
01041 
01042    return zone->killme ? CMP_MATCH : 0;
01043 }
01044 
01045 /*! \brief load indications module */
01046 static int load_indications(int reload)
01047 {
01048    struct ast_config *cfg;
01049    const char *cxt = NULL;
01050    const char *country = NULL;
01051    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01052    int res = -1;
01053 
01054    cfg = ast_config_load2(config, "indications", config_flags);
01055 
01056    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01057       ast_log(LOG_WARNING, "Can't find indications config file %s.\n", config);
01058       return 0;
01059    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01060       return 0;
01061    }
01062 
01063    /* Lock the container to prevent multiple simultaneous reloads */
01064    ao2_lock(ast_tone_zones);
01065 
01066    ao2_callback(ast_tone_zones, OBJ_NODATA, tone_zone_mark, NULL);
01067 
01068    /* Use existing config to populate the Indication table */
01069    while ((cxt = ast_category_browse(cfg, cxt))) {
01070       /* All categories but "general" are considered countries */
01071       if (!strcasecmp(cxt, "general")) {
01072          continue;
01073       }
01074 
01075       if (parse_tone_zone(cfg, cxt)) {
01076          goto return_cleanup;
01077       }
01078    }
01079 
01080    ao2_callback(ast_tone_zones, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
01081          prune_tone_zone, NULL);
01082 
01083    /* determine which country is the default */
01084    country = ast_variable_retrieve(cfg, "general", "country");
01085    if (ast_strlen_zero(country) || ast_set_indication_country(country)) {
01086       ast_log(LOG_WARNING, "Unable to set the default country (for indication tones)\n");
01087    }
01088 
01089    res = 0;
01090 
01091 return_cleanup:
01092    ao2_unlock(ast_tone_zones);
01093    ast_config_destroy(cfg);
01094 
01095    return res;
01096 }
01097 
01098 /*! \brief CLI entries for commands provided by this module */
01099 static struct ast_cli_entry cli_indications[] = {
01100    AST_CLI_DEFINE(handle_cli_indication_add,    "Add the given indication to the country"),
01101    AST_CLI_DEFINE(handle_cli_indication_remove, "Remove the given indication from the country"),
01102    AST_CLI_DEFINE(handle_cli_indication_show,   "Display a list of all countries/indications")
01103 };
01104 
01105 static int ast_tone_zone_hash(const void *obj, const int flags)
01106 {
01107    const struct ast_tone_zone *zone = obj;
01108 
01109    return ast_str_case_hash(zone->country);
01110 }
01111 
01112 static int ast_tone_zone_cmp(void *obj, void *arg, int flags)
01113 {
01114    struct ast_tone_zone *zone = obj;
01115    struct ast_tone_zone *zone_arg = arg;
01116 
01117    return (!strcasecmp(zone->country, zone_arg->country)) ?
01118          CMP_MATCH | CMP_STOP : 0;
01119 }
01120 
01121 int ast_tone_zone_data_add_structure(struct ast_data *tree, struct ast_tone_zone *zone)
01122 {
01123    struct ast_data *data_zone_sound;
01124    struct ast_tone_zone_sound *s;
01125 
01126    ast_data_add_structure(ast_tone_zone, tree, zone);
01127 
01128    if (AST_LIST_EMPTY(&zone->tones)) {
01129       return 0;
01130    }
01131 
01132    data_zone_sound = ast_data_add_node(tree, "tones");
01133    if (!data_zone_sound) {
01134       return -1;
01135    }
01136 
01137    ast_tone_zone_lock(zone);
01138 
01139    AST_LIST_TRAVERSE(&zone->tones, s, entry) {
01140       ast_data_add_structure(ast_tone_zone_sound, data_zone_sound, s);
01141    }
01142 
01143    ast_tone_zone_unlock(zone);
01144 
01145    return 0;
01146 }
01147 
01148 /*! \brief Load indications module */
01149 int ast_indications_init(void)
01150 {
01151    if (!(ast_tone_zones = ao2_container_alloc(NUM_TONE_ZONE_BUCKETS,
01152          ast_tone_zone_hash, ast_tone_zone_cmp))) {
01153       return -1;
01154    }
01155 
01156    if (load_indications(0)) {
01157       return -1;
01158    }
01159 
01160    ast_cli_register_multiple(cli_indications, ARRAY_LEN(cli_indications));
01161 
01162    return 0;
01163 }
01164 
01165 /*! \brief Reload indications module */
01166 int ast_indications_reload(void)
01167 {
01168    return load_indications(1);
01169 }
01170 

Generated on Sat Mar 10 01:54:19 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7