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