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: 398757 $")
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 playtones_item *new_items;
00345 struct ast_tone_zone_part tone_data = {
00346 .time = 0,
00347 };
00348
00349 s = ast_strip(s);
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 new_items = ast_realloc(d.items, (d.nitems + 1) * sizeof(*d.items));
00378 if (!new_items) {
00379 ast_free(d.items);
00380 return -1;
00381 }
00382 d.items = new_items;
00383
00384 d.items[d.nitems].fac1 = 2.0 * cos(2.0 * M_PI * (tone_data.freq1 / sample_rate)) * max_sample_val;
00385 d.items[d.nitems].init_v2_1 = sin(-4.0 * M_PI * (tone_data.freq1 / sample_rate)) * d.vol;
00386 d.items[d.nitems].init_v3_1 = sin(-2.0 * M_PI * (tone_data.freq1 / sample_rate)) * d.vol;
00387
00388 d.items[d.nitems].fac2 = 2.0 * cos(2.0 * M_PI * (tone_data.freq2 / sample_rate)) * max_sample_val;
00389 d.items[d.nitems].init_v2_2 = sin(-4.0 * M_PI * (tone_data.freq2 / sample_rate)) * d.vol;
00390 d.items[d.nitems].init_v3_2 = sin(-2.0 * M_PI * (tone_data.freq2 / sample_rate)) * d.vol;
00391
00392 d.items[d.nitems].duration = tone_data.time;
00393 d.items[d.nitems].modulate = tone_data.modulate;
00394
00395 d.nitems++;
00396 }
00397
00398 if (!d.nitems) {
00399 ast_log(LOG_ERROR, "No valid tone parts\n");
00400 return -1;
00401 }
00402
00403 if (ast_activate_generator(chan, &playtones, &d)) {
00404 ast_free(d.items);
00405 return -1;
00406 }
00407
00408 return 0;
00409 }
00410
00411 void ast_playtones_stop(struct ast_channel *chan)
00412 {
00413 ast_deactivate_generator(chan);
00414 }
00415
00416 int ast_tone_zone_count(void)
00417 {
00418 return ao2_container_count(ast_tone_zones);
00419 }
00420
00421 struct ao2_iterator ast_tone_zone_iterator_init(void)
00422 {
00423 return ao2_iterator_init(ast_tone_zones, 0);
00424 }
00425
00426
00427 static int ast_set_indication_country(const char *country)
00428 {
00429 struct ast_tone_zone *zone = NULL;
00430
00431
00432 if (ast_strlen_zero(country) || !(zone = ast_get_indication_zone(country))) {
00433 return -1;
00434 }
00435
00436 ast_verb(3, "Setting default indication country to '%s'\n", country);
00437
00438 ao2_lock(ast_tone_zones);
00439 if (default_tone_zone) {
00440 default_tone_zone = ast_tone_zone_unref(default_tone_zone);
00441 }
00442 default_tone_zone = ast_tone_zone_ref(zone);
00443 ao2_unlock(ast_tone_zones);
00444
00445 zone = ast_tone_zone_unref(zone);
00446
00447 return 0;
00448 }
00449
00450
00451 struct ast_tone_zone *ast_get_indication_zone(const char *country)
00452 {
00453 struct ast_tone_zone *tz = NULL;
00454 struct ast_tone_zone zone_arg = {
00455 .nrringcadence = 0,
00456 };
00457
00458 if (ast_strlen_zero(country)) {
00459 ao2_lock(ast_tone_zones);
00460 if (default_tone_zone) {
00461 tz = ast_tone_zone_ref(default_tone_zone);
00462 }
00463 ao2_unlock(ast_tone_zones);
00464
00465 return tz;
00466 }
00467
00468 ast_copy_string(zone_arg.country, country, sizeof(zone_arg.country));
00469
00470 return ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER);
00471 }
00472
00473 struct ast_tone_zone_sound *ast_get_indication_tone(const struct ast_tone_zone *_zone, const char *indication)
00474 {
00475 struct ast_tone_zone_sound *ts = NULL;
00476
00477 struct ast_tone_zone *zone = (struct ast_tone_zone *) _zone;
00478
00479
00480 if (!zone) {
00481 ao2_lock(ast_tone_zones);
00482 if (default_tone_zone) {
00483 zone = ast_tone_zone_ref(default_tone_zone);
00484 }
00485 ao2_unlock(ast_tone_zones);
00486
00487 if (!zone) {
00488 return NULL;
00489 }
00490 }
00491
00492 ast_tone_zone_lock(zone);
00493
00494
00495 AST_LIST_TRAVERSE(&zone->tones, ts, entry) {
00496 if (!strcasecmp(ts->name, indication)) {
00497
00498 ts = ast_tone_zone_sound_ref(ts);
00499 break;
00500 }
00501 }
00502
00503 ast_tone_zone_unlock(zone);
00504
00505 return ts;
00506 }
00507
00508 static void ast_tone_zone_sound_destructor(void *obj)
00509 {
00510 struct ast_tone_zone_sound *ts = obj;
00511
00512
00513 if (ts->name) {
00514 ast_free((char *) ts->name);
00515 ts->name = NULL;
00516 }
00517
00518 if (ts->data) {
00519 ast_free((char *) ts->data);
00520 ts->data = NULL;
00521 }
00522 }
00523
00524
00525 static void ast_tone_zone_destructor(void *obj)
00526 {
00527 struct ast_tone_zone *zone = obj;
00528 struct ast_tone_zone_sound *current;
00529
00530 while ((current = AST_LIST_REMOVE_HEAD(&zone->tones, entry))) {
00531 current = ast_tone_zone_sound_unref(current);
00532 }
00533
00534 if (zone->ringcadence) {
00535 ast_free(zone->ringcadence);
00536 zone->ringcadence = NULL;
00537 }
00538 }
00539
00540
00541 static int ast_register_indication_country(struct ast_tone_zone *zone)
00542 {
00543 ao2_lock(ast_tone_zones);
00544 if (!default_tone_zone) {
00545 default_tone_zone = ast_tone_zone_ref(zone);
00546 }
00547 ao2_unlock(ast_tone_zones);
00548
00549 ao2_link(ast_tone_zones, zone);
00550
00551 ast_verb(3, "Registered indication country '%s'\n", zone->country);
00552
00553 return 0;
00554 }
00555
00556
00557 static int ast_unregister_indication_country(const char *country)
00558 {
00559 struct ast_tone_zone *tz = NULL;
00560 struct ast_tone_zone zone_arg = {
00561 .nrringcadence = 0,
00562 };
00563
00564 ast_copy_string(zone_arg.country, country, sizeof(zone_arg.country));
00565
00566 ao2_lock(ast_tone_zones);
00567 tz = ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER | OBJ_UNLINK);
00568 if (!tz) {
00569 ao2_unlock(ast_tone_zones);
00570 return -1;
00571 }
00572
00573 if (default_tone_zone == tz) {
00574 ast_tone_zone_unref(default_tone_zone);
00575
00576 default_tone_zone = ao2_callback(ast_tone_zones, 0, NULL, NULL);
00577 }
00578 ao2_unlock(ast_tone_zones);
00579
00580 tz = ast_tone_zone_unref(tz);
00581
00582 return 0;
00583 }
00584
00585
00586
00587
00588 static int ast_register_indication(struct ast_tone_zone *zone, const char *indication,
00589 const char *tonelist)
00590 {
00591 struct ast_tone_zone_sound *ts;
00592
00593 if (ast_strlen_zero(indication) || ast_strlen_zero(tonelist)) {
00594 return -1;
00595 }
00596
00597 AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, ts, entry) {
00598 if (!strcasecmp(indication, ts->name)) {
00599 AST_LIST_REMOVE_CURRENT(entry);
00600 ts = ast_tone_zone_sound_unref(ts);
00601 break;
00602 }
00603 }
00604 AST_LIST_TRAVERSE_SAFE_END;
00605
00606 if (!(ts = ao2_alloc(sizeof(*ts), ast_tone_zone_sound_destructor))) {
00607 return -1;
00608 }
00609
00610 if (!(ts->name = ast_strdup(indication)) || !(ts->data = ast_strdup(tonelist))) {
00611 ts = ast_tone_zone_sound_unref(ts);
00612 return -1;
00613 }
00614
00615 AST_LIST_INSERT_TAIL(&zone->tones, ts, entry);
00616
00617 return 0;
00618 }
00619
00620
00621 static int ast_unregister_indication(struct ast_tone_zone *zone, const char *indication)
00622 {
00623 struct ast_tone_zone_sound *ts;
00624 int res = -1;
00625
00626 ast_tone_zone_lock(zone);
00627
00628 AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, ts, entry) {
00629 if (!strcasecmp(indication, ts->name)) {
00630 AST_LIST_REMOVE_CURRENT(entry);
00631 ts = ast_tone_zone_sound_unref(ts);
00632 res = 0;
00633 break;
00634 }
00635 }
00636 AST_LIST_TRAVERSE_SAFE_END;
00637
00638 ast_tone_zone_unlock(zone);
00639
00640 return res;
00641 }
00642
00643 static struct ast_tone_zone *ast_tone_zone_alloc(void)
00644 {
00645 return ao2_alloc(sizeof(struct ast_tone_zone), ast_tone_zone_destructor);
00646 }
00647
00648 static char *complete_country(struct ast_cli_args *a)
00649 {
00650 char *res = NULL;
00651 struct ao2_iterator i;
00652 int which = 0;
00653 size_t wordlen;
00654 struct ast_tone_zone *tz;
00655
00656 wordlen = strlen(a->word);
00657
00658 i = ao2_iterator_init(ast_tone_zones, 0);
00659 while ((tz = ao2_iterator_next(&i))) {
00660 if (!strncasecmp(a->word, tz->country, wordlen) && ++which > a->n) {
00661 res = ast_strdup(tz->country);
00662 }
00663 tz = ast_tone_zone_unref(tz);
00664 if (res) {
00665 break;
00666 }
00667 }
00668 ao2_iterator_destroy(&i);
00669
00670 return res;
00671 }
00672
00673 static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00674 {
00675 struct ast_tone_zone *tz;
00676 int created_country = 0;
00677 char *res = CLI_SUCCESS;
00678
00679 switch (cmd) {
00680 case CLI_INIT:
00681 e->command = "indication add";
00682 e->usage =
00683 "Usage: indication add <country> <indication> \"<tonelist>\"\n"
00684 " Add the given indication to the country.\n";
00685 return NULL;
00686 case CLI_GENERATE:
00687 if (a->pos == 2) {
00688 return complete_country(a);
00689 } else {
00690 return NULL;
00691 }
00692 }
00693
00694 if (a->argc != 5) {
00695 return CLI_SHOWUSAGE;
00696 }
00697
00698 if (!(tz = ast_get_indication_zone(a->argv[2]))) {
00699
00700 ast_log(LOG_NOTICE, "Country '%s' does not exist, creating it.\n", a->argv[2]);
00701
00702 if (!(tz = ast_tone_zone_alloc())) {
00703 return CLI_FAILURE;
00704 }
00705
00706 ast_copy_string(tz->country, a->argv[2], sizeof(tz->country));
00707
00708 if (ast_register_indication_country(tz)) {
00709 ast_log(LOG_WARNING, "Unable to register new country\n");
00710 tz = ast_tone_zone_unref(tz);
00711 return CLI_FAILURE;
00712 }
00713
00714 created_country = 1;
00715 }
00716
00717 ast_tone_zone_lock(tz);
00718
00719 if (ast_register_indication(tz, a->argv[3], a->argv[4])) {
00720 ast_log(LOG_WARNING, "Unable to register indication %s/%s\n", a->argv[2], a->argv[3]);
00721 if (created_country) {
00722 ast_unregister_indication_country(a->argv[2]);
00723 }
00724 res = CLI_FAILURE;
00725 }
00726
00727 ast_tone_zone_unlock(tz);
00728
00729 tz = ast_tone_zone_unref(tz);
00730
00731 return res;
00732 }
00733
00734 static char *complete_indications(struct ast_cli_args *a)
00735 {
00736 char *res = NULL;
00737 int which = 0;
00738 size_t wordlen;
00739 struct ast_tone_zone_sound *ts;
00740 struct ast_tone_zone *tz, tmp_tz = {
00741 .nrringcadence = 0,
00742 };
00743
00744 ast_copy_string(tmp_tz.country, a->argv[a->pos - 1], sizeof(tmp_tz.country));
00745
00746 if (!(tz = ao2_find(ast_tone_zones, &tmp_tz, OBJ_POINTER))) {
00747 return NULL;
00748 }
00749
00750 wordlen = strlen(a->word);
00751
00752 ast_tone_zone_lock(tz);
00753 AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
00754 if (!strncasecmp(a->word, ts->name, wordlen) && ++which > a->n) {
00755 res = ast_strdup(ts->name);
00756 break;
00757 }
00758 }
00759 ast_tone_zone_unlock(tz);
00760
00761 tz = ast_tone_zone_unref(tz);
00762
00763 return res;
00764 }
00765
00766 static char *handle_cli_indication_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00767 {
00768 struct ast_tone_zone *tz;
00769 char *res = CLI_SUCCESS;
00770
00771 switch (cmd) {
00772 case CLI_INIT:
00773 e->command = "indication remove";
00774 e->usage =
00775 "Usage: indication remove <country> [indication]\n"
00776 " Remove the given indication from the country.\n";
00777 return NULL;
00778 case CLI_GENERATE:
00779 if (a->pos == 2) {
00780 return complete_country(a);
00781 } else if (a->pos == 3) {
00782 return complete_indications(a);
00783 }
00784 }
00785
00786 if (a->argc != 3 && a->argc != 4) {
00787 return CLI_SHOWUSAGE;
00788 }
00789
00790 if (a->argc == 3) {
00791
00792 if (ast_unregister_indication_country(a->argv[2])) {
00793 ast_log(LOG_WARNING, "Unable to unregister indication country %s\n", a->argv[2]);
00794 return CLI_FAILURE;
00795 }
00796
00797 return CLI_SUCCESS;
00798 }
00799
00800 if (!(tz = ast_get_indication_zone(a->argv[2]))) {
00801 ast_log(LOG_WARNING, "Unable to unregister indication %s/%s, country does not exists\n", a->argv[2], a->argv[3]);
00802 return CLI_FAILURE;
00803 }
00804
00805 if (ast_unregister_indication(tz, a->argv[3])) {
00806 ast_log(LOG_WARNING, "Unable to unregister indication %s/%s\n", a->argv[2], a->argv[3]);
00807 res = CLI_FAILURE;
00808 }
00809
00810 tz = ast_tone_zone_unref(tz);
00811
00812 return res;
00813 }
00814
00815 static char *handle_cli_indication_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00816 {
00817 struct ast_tone_zone *tz = NULL;
00818 struct ast_str *buf;
00819 int found_country = 0;
00820 int i;
00821
00822 switch (cmd) {
00823 case CLI_INIT:
00824 e->command = "indication show";
00825 e->usage =
00826 "Usage: indication show [<country> ...]\n"
00827 " Display either a condensed summary of all countries and indications, or a\n"
00828 " more verbose list of indications for the specified countries.\n";
00829 return NULL;
00830 case CLI_GENERATE:
00831 return complete_country(a);
00832 }
00833
00834 if (a->argc == 2) {
00835 struct ao2_iterator iter;
00836
00837 ast_cli(a->fd, "Country Description\n");
00838 ast_cli(a->fd, "===========================\n");
00839 iter = ast_tone_zone_iterator_init();
00840 while ((tz = ao2_iterator_next(&iter))) {
00841 ast_tone_zone_lock(tz);
00842 ast_cli(a->fd, "%-7.7s %s\n", tz->country, tz->description);
00843 ast_tone_zone_unlock(tz);
00844 tz = ast_tone_zone_unref(tz);
00845 }
00846 ao2_iterator_destroy(&iter);
00847 return CLI_SUCCESS;
00848 }
00849
00850 buf = ast_str_alloca(256);
00851
00852 for (i = 2; i < a->argc; i++) {
00853 struct ast_tone_zone zone_arg = {
00854 .nrringcadence = 0,
00855 };
00856 struct ast_tone_zone_sound *ts;
00857 int j;
00858
00859 ast_copy_string(zone_arg.country, a->argv[i], sizeof(zone_arg.country));
00860
00861 if (!(tz = ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER))) {
00862 continue;
00863 }
00864
00865 if (!found_country) {
00866 found_country = 1;
00867 ast_cli(a->fd, "Country Indication PlayList\n");
00868 ast_cli(a->fd, "=====================================\n");
00869 }
00870
00871 ast_tone_zone_lock(tz);
00872
00873 ast_str_set(&buf, 0, "%-7.7s %-15.15s ", tz->country, "<ringcadence>");
00874 for (j = 0; j < tz->nrringcadence; j++) {
00875 ast_str_append(&buf, 0, "%d%s", tz->ringcadence[j],
00876 (j == tz->nrringcadence - 1) ? "" : ",");
00877 }
00878 ast_str_append(&buf, 0, "\n");
00879 ast_cli(a->fd, "%s", ast_str_buffer(buf));
00880
00881 AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
00882 ast_cli(a->fd, "%-7.7s %-15.15s %s\n", tz->country, ts->name, ts->data);
00883 }
00884
00885 ast_tone_zone_unlock(tz);
00886 tz = ast_tone_zone_unref(tz);
00887 }
00888
00889 if (!found_country) {
00890 ast_cli(a->fd, "No countries matched your criteria.\n");
00891 }
00892
00893 return CLI_SUCCESS;
00894 }
00895
00896 static int is_valid_tone_zone(struct ast_tone_zone *zone)
00897 {
00898 int res;
00899
00900 ast_tone_zone_lock(zone);
00901 res = (!ast_strlen_zero(zone->description) && !AST_LIST_EMPTY(&zone->tones));
00902 ast_tone_zone_unlock(zone);
00903
00904 return res;
00905 }
00906
00907
00908
00909
00910 static void store_tone_zone_ring_cadence(struct ast_tone_zone *zone, const char *val)
00911 {
00912 char buf[1024];
00913 char *ring, *c = buf;
00914
00915 ast_copy_string(buf, val, sizeof(buf));
00916
00917 while ((ring = strsep(&c, ","))) {
00918 int *tmp, val;
00919
00920 ring = ast_strip(ring);
00921
00922 if (!isdigit(ring[0]) || (val = atoi(ring)) == -1) {
00923 ast_log(LOG_WARNING, "Invalid ringcadence given '%s'.\n", ring);
00924 continue;
00925 }
00926
00927 if (!(tmp = ast_realloc(zone->ringcadence, (zone->nrringcadence + 1) * sizeof(int)))) {
00928 return;
00929 }
00930
00931 zone->ringcadence = tmp;
00932 tmp[zone->nrringcadence] = val;
00933 zone->nrringcadence++;
00934 }
00935 }
00936
00937 static void store_config_tone_zone(struct ast_tone_zone *zone, const char *var,
00938 const char *value)
00939 {
00940 CV_START(var, value);
00941
00942 CV_STR("description", zone->description);
00943 CV_F("ringcadence", store_tone_zone_ring_cadence(zone, value));
00944
00945 ast_register_indication(zone, var, value);
00946
00947 CV_END;
00948 }
00949
00950 static void reset_tone_zone(struct ast_tone_zone *zone)
00951 {
00952 ast_tone_zone_lock(zone);
00953
00954 zone->killme = 0;
00955
00956 if (zone->nrringcadence) {
00957 zone->nrringcadence = 0;
00958 ast_free(zone->ringcadence);
00959 zone->ringcadence = NULL;
00960 }
00961
00962 ast_tone_zone_unlock(zone);
00963 }
00964
00965 static int parse_tone_zone(struct ast_config *cfg, const char *country)
00966 {
00967 struct ast_variable *v;
00968 struct ast_tone_zone *zone;
00969 struct ast_tone_zone tmp_zone = {
00970 .nrringcadence = 0,
00971 };
00972 int allocd = 0;
00973
00974 ast_copy_string(tmp_zone.country, country, sizeof(tmp_zone.country));
00975
00976 if ((zone = ao2_find(ast_tone_zones, &tmp_zone, OBJ_POINTER))) {
00977 reset_tone_zone(zone);
00978 } else if ((zone = ast_tone_zone_alloc())) {
00979 allocd = 1;
00980 ast_copy_string(zone->country, country, sizeof(zone->country));
00981 } else {
00982 return -1;
00983 }
00984
00985 ast_tone_zone_lock(zone);
00986 for (v = ast_variable_browse(cfg, country); v; v = v->next) {
00987 store_config_tone_zone(zone, v->name, v->value);
00988 }
00989 ast_tone_zone_unlock(zone);
00990
00991 if (allocd) {
00992 if (!is_valid_tone_zone(zone)) {
00993 ast_log(LOG_WARNING, "Indication country '%s' is invalid\n", country);
00994 } else if (ast_register_indication_country(zone)) {
00995 ast_log(LOG_WARNING, "Unable to register indication country '%s'.\n",
00996 country);
00997 }
00998 }
00999
01000 zone = ast_tone_zone_unref(zone);
01001
01002 return 0;
01003 }
01004
01005
01006
01007
01008
01009 static int tone_zone_mark(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 zone->killme = 1;
01017
01018 AST_LIST_TRAVERSE(&zone->tones, s, entry) {
01019 s->killme = 1;
01020 }
01021
01022 ast_tone_zone_unlock(zone);
01023
01024 return 0;
01025 }
01026
01027
01028
01029
01030
01031 static int prune_tone_zone(void *obj, void *arg, int flags)
01032 {
01033 struct ast_tone_zone *zone = obj;
01034 struct ast_tone_zone_sound *s;
01035
01036 ast_tone_zone_lock(zone);
01037
01038 AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, s, entry) {
01039 if (s->killme) {
01040 AST_LIST_REMOVE_CURRENT(entry);
01041 s = ast_tone_zone_sound_unref(s);
01042 }
01043 }
01044 AST_LIST_TRAVERSE_SAFE_END;
01045
01046 ast_tone_zone_unlock(zone);
01047
01048 return zone->killme ? CMP_MATCH : 0;
01049 }
01050
01051
01052 static int load_indications(int reload)
01053 {
01054 struct ast_config *cfg;
01055 const char *cxt = NULL;
01056 const char *country = NULL;
01057 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01058 int res = -1;
01059
01060 cfg = ast_config_load2(config, "indications", config_flags);
01061
01062 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01063 ast_log(LOG_WARNING, "Can't find indications config file %s.\n", config);
01064 return 0;
01065 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01066 return 0;
01067 }
01068
01069
01070 ao2_lock(ast_tone_zones);
01071
01072 ao2_callback(ast_tone_zones, OBJ_NODATA, tone_zone_mark, NULL);
01073
01074
01075 while ((cxt = ast_category_browse(cfg, cxt))) {
01076
01077 if (!strcasecmp(cxt, "general")) {
01078 continue;
01079 }
01080
01081 if (parse_tone_zone(cfg, cxt)) {
01082 goto return_cleanup;
01083 }
01084 }
01085
01086 ao2_callback(ast_tone_zones, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
01087 prune_tone_zone, NULL);
01088
01089
01090 country = ast_variable_retrieve(cfg, "general", "country");
01091 if (ast_strlen_zero(country) || ast_set_indication_country(country)) {
01092 ast_log(LOG_WARNING, "Unable to set the default country (for indication tones)\n");
01093 }
01094
01095 res = 0;
01096
01097 return_cleanup:
01098 ao2_unlock(ast_tone_zones);
01099 ast_config_destroy(cfg);
01100
01101 return res;
01102 }
01103
01104
01105 static struct ast_cli_entry cli_indications[] = {
01106 AST_CLI_DEFINE(handle_cli_indication_add, "Add the given indication to the country"),
01107 AST_CLI_DEFINE(handle_cli_indication_remove, "Remove the given indication from the country"),
01108 AST_CLI_DEFINE(handle_cli_indication_show, "Display a list of all countries/indications")
01109 };
01110
01111 static int ast_tone_zone_hash(const void *obj, const int flags)
01112 {
01113 const struct ast_tone_zone *zone = obj;
01114
01115 return ast_str_case_hash(zone->country);
01116 }
01117
01118 static int ast_tone_zone_cmp(void *obj, void *arg, int flags)
01119 {
01120 struct ast_tone_zone *zone = obj;
01121 struct ast_tone_zone *zone_arg = arg;
01122
01123 return (!strcasecmp(zone->country, zone_arg->country)) ?
01124 CMP_MATCH | CMP_STOP : 0;
01125 }
01126
01127 int ast_tone_zone_data_add_structure(struct ast_data *tree, struct ast_tone_zone *zone)
01128 {
01129 struct ast_data *data_zone_sound;
01130 struct ast_tone_zone_sound *s;
01131
01132 ast_data_add_structure(ast_tone_zone, tree, zone);
01133
01134 if (AST_LIST_EMPTY(&zone->tones)) {
01135 return 0;
01136 }
01137
01138 data_zone_sound = ast_data_add_node(tree, "tones");
01139 if (!data_zone_sound) {
01140 return -1;
01141 }
01142
01143 ast_tone_zone_lock(zone);
01144
01145 AST_LIST_TRAVERSE(&zone->tones, s, entry) {
01146 ast_data_add_structure(ast_tone_zone_sound, data_zone_sound, s);
01147 }
01148
01149 ast_tone_zone_unlock(zone);
01150
01151 return 0;
01152 }
01153
01154
01155 static void indications_shutdown(void)
01156 {
01157 ast_cli_unregister_multiple(cli_indications, ARRAY_LEN(cli_indications));
01158 if (default_tone_zone) {
01159 ast_tone_zone_unref(default_tone_zone);
01160 default_tone_zone = NULL;
01161 }
01162 if (ast_tone_zones) {
01163 ao2_ref(ast_tone_zones, -1);
01164 ast_tone_zones = NULL;
01165 }
01166 }
01167
01168
01169 int ast_indications_init(void)
01170 {
01171 if (!(ast_tone_zones = ao2_container_alloc(NUM_TONE_ZONE_BUCKETS,
01172 ast_tone_zone_hash, ast_tone_zone_cmp))) {
01173 return -1;
01174 }
01175
01176 if (load_indications(0)) {
01177 indications_shutdown();
01178 return -1;
01179 }
01180
01181 ast_cli_register_multiple(cli_indications, ARRAY_LEN(cli_indications));
01182
01183 ast_register_atexit(indications_shutdown);
01184 return 0;
01185 }
01186
01187
01188 int ast_indications_reload(void)
01189 {
01190 return load_indications(1);
01191 }
01192