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: 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"
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
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
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
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
00181
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
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) {
00239 ps->pos = 0;
00240 ps->npos++;
00241 if (ps->npos >= ps->nitems) {
00242 if (ps->reppos == -1) {
00243 return -1;
00244 }
00245 ps->npos = ps->reppos;
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
00263 } else if (sscanf(s, "%30u+%30u", &tone_data->freq1, &tone_data->freq2) == 2) {
00264
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
00269 tone_data->modulate = 1;
00270 } else if (sscanf(s, "%30u*%30u", &tone_data->freq1, &tone_data->freq2) == 2) {
00271
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
00276 tone_data->freq2 = 0;
00277 } else if (sscanf(s, "%30u", &tone_data->freq1) == 1) {
00278
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
00284 tone_data->midinote = 1;
00285 } else if (sscanf(s, "M%30u+M%30u", &tone_data->freq1, &tone_data->freq2) == 2) {
00286
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
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
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
00301 tone_data->freq2 = -1;
00302 tone_data->midinote = 1;
00303 } else if (sscanf(s, "M%30u", &tone_data->freq1) == 1) {
00304
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;
00326 }
00327
00328 d.interruptible = interruptible;
00329
00330 stringp = data;
00331
00332
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
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
00420 static int ast_set_indication_country(const char *country)
00421 {
00422 struct ast_tone_zone *zone = NULL;
00423
00424
00425 if (ast_strlen_zero(country) || !(zone = ast_get_indication_zone(country))) {
00426 return -1;
00427 }
00428
00429 ast_verb(3, "Setting default indication country to '%s'\n", country);
00430
00431 ao2_lock(ast_tone_zones);
00432 if (default_tone_zone) {
00433 default_tone_zone = ast_tone_zone_unref(default_tone_zone);
00434 }
00435 default_tone_zone = ast_tone_zone_ref(zone);
00436 ao2_unlock(ast_tone_zones);
00437
00438 zone = ast_tone_zone_unref(zone);
00439
00440 return 0;
00441 }
00442
00443
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
00470 struct ast_tone_zone *zone = (struct ast_tone_zone *) _zone;
00471
00472
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
00488 AST_LIST_TRAVERSE(&zone->tones, ts, entry) {
00489 if (!strcasecmp(ts->name, indication)) {
00490
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
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
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
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
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
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
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);
00609
00610 return 0;
00611 }
00612
00613
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
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
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
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
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
00999
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
01021
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
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
01062 ao2_lock(ast_tone_zones);
01063
01064 ao2_callback(ast_tone_zones, OBJ_NODATA, tone_zone_mark, NULL);
01065
01066
01067 while ((cxt = ast_category_browse(cfg, cxt))) {
01068
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
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
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
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
01164 int ast_indications_reload(void)
01165 {
01166 return load_indications(1);
01167 }
01168