Wed Apr 6 11:29:45 2011

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: 274727 $")
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 
00662    return res;
00663 }
00664 
00665 static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00666 {
00667    struct ast_tone_zone *tz;
00668    int created_country = 0;
00669    char *res = CLI_SUCCESS;
00670 
00671    switch (cmd) {
00672    case CLI_INIT:
00673       e->command = "indication add";
00674       e->usage =
00675          "Usage: indication add <country> <indication> \"<tonelist>\"\n"
00676          "       Add the given indication to the country.\n";
00677       return NULL;
00678    case CLI_GENERATE:
00679       if (a->pos == 2) {
00680          return complete_country(a);
00681       } else {
00682          return NULL;
00683       }
00684    }
00685 
00686    if (a->argc != 5) {
00687       return CLI_SHOWUSAGE;
00688    }
00689 
00690    if (!(tz = ast_get_indication_zone(a->argv[2]))) {
00691       /* country does not exist, create it */
00692       ast_log(LOG_NOTICE, "Country '%s' does not exist, creating it.\n", a->argv[2]);
00693 
00694       if (!(tz = ast_tone_zone_alloc())) {
00695          return CLI_FAILURE;
00696       }
00697 
00698       ast_copy_string(tz->country, a->argv[2], sizeof(tz->country));
00699 
00700       if (ast_register_indication_country(tz)) {
00701          ast_log(LOG_WARNING, "Unable to register new country\n");
00702          tz = ast_tone_zone_unref(tz);
00703          return CLI_FAILURE;
00704       }
00705 
00706       created_country = 1;
00707    }
00708 
00709    ast_tone_zone_lock(tz);
00710 
00711    if (ast_register_indication(tz, a->argv[3], a->argv[4])) {
00712       ast_log(LOG_WARNING, "Unable to register indication %s/%s\n", a->argv[2], a->argv[3]);
00713       if (created_country) {
00714          ast_unregister_indication_country(a->argv[2]);
00715       }
00716       res = CLI_FAILURE;
00717    }
00718 
00719    ast_tone_zone_unlock(tz);
00720 
00721    tz = ast_tone_zone_unref(tz);
00722 
00723    return res;
00724 }
00725 
00726 static char *complete_indications(struct ast_cli_args *a)
00727 {
00728    char *res = NULL;
00729    int which = 0;
00730    size_t wordlen;
00731    struct ast_tone_zone_sound *ts;
00732    struct ast_tone_zone *tz, tmp_tz = {
00733       .nrringcadence = 0,
00734    };
00735 
00736    ast_copy_string(tmp_tz.country, a->argv[a->pos - 1], sizeof(tmp_tz.country));
00737 
00738    if (!(tz = ao2_find(ast_tone_zones, &tmp_tz, OBJ_POINTER))) {
00739       return NULL;
00740    }
00741 
00742    wordlen = strlen(a->word);
00743 
00744    ast_tone_zone_lock(tz);
00745    AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
00746       if (!strncasecmp(a->word, ts->name, wordlen) && ++which > a->n) {
00747          res = ast_strdup(ts->name);
00748          break;
00749       }
00750    }
00751    ast_tone_zone_unlock(tz);
00752 
00753    tz = ast_tone_zone_unref(tz);
00754 
00755    return res;
00756 }
00757 
00758 static char *handle_cli_indication_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00759 {
00760    struct ast_tone_zone *tz;
00761    char *res = CLI_SUCCESS;
00762 
00763    switch (cmd) {
00764    case CLI_INIT:
00765       e->command = "indication remove";
00766       e->usage =
00767          "Usage: indication remove <country> [indication]\n"
00768          "       Remove the given indication from the country.\n";
00769       return NULL;
00770    case CLI_GENERATE:
00771       if (a->pos == 2) {
00772          return complete_country(a);
00773       } else if (a->pos == 3) {
00774          return complete_indications(a);
00775       }
00776    }
00777 
00778    if (a->argc != 3 && a->argc != 4) {
00779       return CLI_SHOWUSAGE;
00780    }
00781 
00782    if (a->argc == 3) {
00783       /* remove entire country */
00784       if (ast_unregister_indication_country(a->argv[2])) {
00785          ast_log(LOG_WARNING, "Unable to unregister indication country %s\n", a->argv[2]);
00786          return CLI_FAILURE;
00787       }
00788 
00789       return CLI_SUCCESS;
00790    }
00791 
00792    if (!(tz = ast_get_indication_zone(a->argv[2]))) {
00793       ast_log(LOG_WARNING, "Unable to unregister indication %s/%s, country does not exists\n", a->argv[2], a->argv[3]);
00794       return CLI_FAILURE;
00795    }
00796 
00797    if (ast_unregister_indication(tz, a->argv[3])) {
00798       ast_log(LOG_WARNING, "Unable to unregister indication %s/%s\n", a->argv[2], a->argv[3]);
00799       res = CLI_FAILURE;
00800    }
00801 
00802    tz = ast_tone_zone_unref(tz);
00803 
00804    return res;
00805 }
00806 
00807 static char *handle_cli_indication_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00808 {
00809    struct ast_tone_zone *tz = NULL;
00810    struct ast_str *buf;
00811    int found_country = 0;
00812    int i;
00813 
00814    switch (cmd) {
00815    case CLI_INIT:
00816       e->command = "indication show";
00817       e->usage =
00818          "Usage: indication show [<country> ...]\n"
00819          "       Display either a condensed summary of all countries and indications, or a\n"
00820          "       more verbose list of indications for the specified countries.\n";
00821       return NULL;
00822    case CLI_GENERATE:
00823       return complete_country(a);
00824    }
00825 
00826    if (a->argc == 2) {
00827       struct ao2_iterator iter;
00828       /* no arguments, show a list of countries */
00829       ast_cli(a->fd, "Country   Description\n");
00830       ast_cli(a->fd, "===========================\n");
00831       iter = ast_tone_zone_iterator_init();
00832       while ((tz = ao2_iterator_next(&iter))) {
00833          ast_tone_zone_lock(tz);
00834          ast_cli(a->fd, "%-7.7s  %s\n", tz->country, tz->description);
00835          ast_tone_zone_unlock(tz);
00836          tz = ast_tone_zone_unref(tz);
00837       }
00838       return CLI_SUCCESS;
00839    }
00840 
00841    buf = ast_str_alloca(256);
00842 
00843    for (i = 2; i < a->argc; i++) {
00844       struct ast_tone_zone zone_arg = {
00845          .nrringcadence = 0,
00846       };
00847       struct ast_tone_zone_sound *ts;
00848       int j;
00849 
00850       ast_copy_string(zone_arg.country, a->argv[i], sizeof(zone_arg.country));
00851 
00852       if (!(tz = ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER))) {
00853          continue;
00854       }
00855 
00856       if (!found_country) {
00857          found_country = 1;
00858          ast_cli(a->fd, "Country Indication      PlayList\n");
00859          ast_cli(a->fd, "=====================================\n");
00860       }
00861 
00862       ast_tone_zone_lock(tz);
00863 
00864       ast_str_set(&buf, 0, "%-7.7s %-15.15s ", tz->country, "<ringcadence>");
00865       for (j = 0; j < tz->nrringcadence; j++) {
00866          ast_str_append(&buf, 0, "%d%s", tz->ringcadence[j],
00867                (j == tz->nrringcadence - 1) ? "" : ",");
00868       }
00869       ast_str_append(&buf, 0, "\n");
00870       ast_cli(a->fd, "%s", buf->str);
00871 
00872       AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
00873          ast_cli(a->fd, "%-7.7s %-15.15s %s\n", tz->country, ts->name, ts->data);
00874       }
00875 
00876       ast_tone_zone_unlock(tz);
00877       tz = ast_tone_zone_unref(tz);
00878    }
00879 
00880    if (!found_country) {
00881       ast_cli(a->fd, "No countries matched your criteria.\n");
00882    }
00883 
00884    return CLI_SUCCESS;
00885 }
00886 
00887 static int is_valid_tone_zone(struct ast_tone_zone *zone)
00888 {
00889    int res;
00890 
00891    ast_tone_zone_lock(zone);
00892    res = (!ast_strlen_zero(zone->description) && !AST_LIST_EMPTY(&zone->tones));
00893    ast_tone_zone_unlock(zone);
00894 
00895    return res;
00896 }
00897 
00898 /*!
00899  * \note This is called with the tone zone locked.
00900  */
00901 static void store_tone_zone_ring_cadence(struct ast_tone_zone *zone, const char *val)
00902 {
00903    char buf[1024];
00904    char *ring, *c = buf;
00905 
00906    ast_copy_string(buf, val, sizeof(buf));
00907 
00908    while ((ring = strsep(&c, ","))) {
00909       int *tmp, val;
00910 
00911       ring = ast_strip(ring);
00912 
00913       if (!isdigit(ring[0]) || (val = atoi(ring)) == -1) {
00914          ast_log(LOG_WARNING, "Invalid ringcadence given '%s'.\n", ring);
00915          continue;
00916       }
00917 
00918       if (!(tmp = ast_realloc(zone->ringcadence, (zone->nrringcadence + 1) * sizeof(int)))) {
00919          return;
00920       }
00921 
00922       zone->ringcadence = tmp;
00923       tmp[zone->nrringcadence] = val;
00924       zone->nrringcadence++;
00925    }
00926 }
00927 
00928 static void store_config_tone_zone(struct ast_tone_zone *zone, const char *var,
00929       const char *value)
00930 {
00931    CV_START(var, value);
00932 
00933    CV_STR("description", zone->description);
00934    CV_F("ringcadence", store_tone_zone_ring_cadence(zone, value));
00935    CV_F("ringcadance", store_tone_zone_ring_cadence(zone, value));
00936 
00937    ast_register_indication(zone, var, value);
00938 
00939    CV_END;
00940 }
00941 
00942 static void reset_tone_zone(struct ast_tone_zone *zone)
00943 {
00944    ast_tone_zone_lock(zone);
00945 
00946    zone->killme = 0;
00947 
00948    if (zone->nrringcadence) {
00949       zone->nrringcadence = 0;
00950       ast_free(zone->ringcadence);
00951       zone->ringcadence = NULL;
00952    }
00953 
00954    ast_tone_zone_unlock(zone);
00955 }
00956 
00957 static int parse_tone_zone(struct ast_config *cfg, const char *country)
00958 {
00959    struct ast_variable *v;
00960    struct ast_tone_zone *zone;
00961    struct ast_tone_zone tmp_zone = {
00962       .nrringcadence = 0,
00963    };
00964    int allocd = 0;
00965 
00966    ast_copy_string(tmp_zone.country, country, sizeof(tmp_zone.country));
00967 
00968    if ((zone = ao2_find(ast_tone_zones, &tmp_zone, OBJ_POINTER))) {
00969       reset_tone_zone(zone);
00970    } else if ((zone = ast_tone_zone_alloc())) {
00971       allocd = 1;
00972       ast_copy_string(zone->country, country, sizeof(zone->country));
00973    } else {
00974       return -1;
00975    }
00976 
00977    ast_tone_zone_lock(zone);
00978    for (v = ast_variable_browse(cfg, country); v; v = v->next) {
00979       store_config_tone_zone(zone, v->name, v->value);
00980    }
00981    ast_tone_zone_unlock(zone);
00982 
00983    if (allocd) {
00984       if (!is_valid_tone_zone(zone)) {
00985          ast_log(LOG_WARNING, "Indication country '%s' is invalid\n", country);
00986       } else if (ast_register_indication_country(zone)) {
00987          ast_log(LOG_WARNING, "Unable to register indication country '%s'.\n",
00988                country);
00989       }
00990    }
00991 
00992    zone = ast_tone_zone_unref(zone);
00993 
00994    return 0;
00995 }
00996 
00997 /*!
00998  * Mark the zone and its tones before parsing configuration.  We will use this
00999  * to know what to remove after configuration is parsed.
01000  */
01001 static int tone_zone_mark(void *obj, void *arg, int flags)
01002 {
01003    struct ast_tone_zone *zone = obj;
01004    struct ast_tone_zone_sound *s;
01005 
01006    ast_tone_zone_lock(zone);
01007 
01008    zone->killme = 1;
01009 
01010    AST_LIST_TRAVERSE(&zone->tones, s, entry) {
01011       s->killme = 1;
01012    }
01013 
01014    ast_tone_zone_unlock(zone);
01015 
01016    return 0;
01017 }
01018 
01019 /*!
01020  * Prune tones no longer in the configuration, and have the tone zone unlinked
01021  * if it is no longer in the configuration at all.
01022  */
01023 static int prune_tone_zone(void *obj, void *arg, int flags)
01024 {
01025    struct ast_tone_zone *zone = obj;
01026    struct ast_tone_zone_sound *s;
01027 
01028    ast_tone_zone_lock(zone);
01029 
01030    AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, s, entry) {
01031       if (s->killme) {
01032          AST_LIST_REMOVE_CURRENT(entry);
01033          s = ast_tone_zone_sound_unref(s);
01034       }
01035    }
01036    AST_LIST_TRAVERSE_SAFE_END;
01037 
01038    ast_tone_zone_unlock(zone);
01039 
01040    return zone->killme ? CMP_MATCH : 0;
01041 }
01042 
01043 /*! \brief load indications module */
01044 static int load_indications(int reload)
01045 {
01046    struct ast_config *cfg;
01047    const char *cxt = NULL;
01048    const char *country = NULL;
01049    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01050    int res = -1;
01051 
01052    cfg = ast_config_load2(config, "indications", config_flags);
01053 
01054    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01055       ast_log(LOG_WARNING, "Can't find indications config file %s.\n", config);
01056       return 0;
01057    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01058       return 0;
01059    }
01060 
01061    /* Lock the container to prevent multiple simultaneous reloads */
01062    ao2_lock(ast_tone_zones);
01063 
01064    ao2_callback(ast_tone_zones, OBJ_NODATA, tone_zone_mark, NULL);
01065 
01066    /* Use existing config to populate the Indication table */
01067    while ((cxt = ast_category_browse(cfg, cxt))) {
01068       /* All categories but "general" are considered countries */
01069       if (!strcasecmp(cxt, "general")) {
01070          continue;
01071       }
01072 
01073       if (parse_tone_zone(cfg, cxt)) {
01074          goto return_cleanup;
01075       }
01076    }
01077 
01078    ao2_callback(ast_tone_zones, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
01079          prune_tone_zone, NULL);
01080 
01081    /* determine which country is the default */
01082    country = ast_variable_retrieve(cfg, "general", "country");
01083    if (ast_strlen_zero(country) || ast_set_indication_country(country)) {
01084       ast_log(LOG_WARNING, "Unable to set the default country (for indication tones)\n");
01085    }
01086 
01087    res = 0;
01088 
01089 return_cleanup:
01090    ao2_unlock(ast_tone_zones);
01091    ast_config_destroy(cfg);
01092 
01093    return res;
01094 }
01095 
01096 /*! \brief CLI entries for commands provided by this module */
01097 static struct ast_cli_entry cli_indications[] = {
01098    AST_CLI_DEFINE(handle_cli_indication_add,    "Add the given indication to the country"),
01099    AST_CLI_DEFINE(handle_cli_indication_remove, "Remove the given indication from the country"),
01100    AST_CLI_DEFINE(handle_cli_indication_show,   "Display a list of all countries/indications")
01101 };
01102 
01103 static int ast_tone_zone_hash(const void *obj, const int flags)
01104 {
01105    const struct ast_tone_zone *zone = obj;
01106 
01107    return ast_str_case_hash(zone->country);
01108 }
01109 
01110 static int ast_tone_zone_cmp(void *obj, void *arg, int flags)
01111 {
01112    struct ast_tone_zone *zone = obj;
01113    struct ast_tone_zone *zone_arg = arg;
01114 
01115    return (!strcasecmp(zone->country, zone_arg->country)) ?
01116          CMP_MATCH | CMP_STOP : 0;
01117 }
01118 
01119 int ast_tone_zone_data_add_structure(struct ast_data *tree, struct ast_tone_zone *zone)
01120 {
01121    struct ast_data *data_zone_sound;
01122    struct ast_tone_zone_sound *s;
01123 
01124    ast_data_add_structure(ast_tone_zone, tree, zone);
01125 
01126    if (AST_LIST_EMPTY(&zone->tones)) {
01127       return 0;
01128    }
01129 
01130    data_zone_sound = ast_data_add_node(tree, "tones");
01131    if (!data_zone_sound) {
01132       return -1;
01133    }
01134 
01135    ast_tone_zone_lock(zone);
01136 
01137    AST_LIST_TRAVERSE(&zone->tones, s, entry) {
01138       ast_data_add_structure(ast_tone_zone_sound, data_zone_sound, s);
01139    }
01140 
01141    ast_tone_zone_unlock(zone);
01142 
01143    return 0;
01144 }
01145 
01146 /*! \brief Load indications module */
01147 int ast_indications_init(void)
01148 {
01149    if (!(ast_tone_zones = ao2_container_alloc(NUM_TONE_ZONE_BUCKETS,
01150          ast_tone_zone_hash, ast_tone_zone_cmp))) {
01151       return -1;
01152    }
01153 
01154    if (load_indications(0)) {
01155       return -1;
01156    }
01157 
01158    ast_cli_register_multiple(cli_indications, ARRAY_LEN(cli_indications));
01159 
01160    return 0;
01161 }
01162 
01163 /*! \brief Reload indications module */
01164 int ast_indications_reload(void)
01165 {
01166    return load_indications(1);
01167 }
01168 

Generated on Wed Apr 6 11:29:45 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7