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