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