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: 360190 $")
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 struct ast_format 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 ast_format_copy(&ps->origwfmt, ast_channel_writeformat(chan));
00151
00152 if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR)) {
00153 ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", ast_channel_name(chan));
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(ast_channel_flags(chan), AST_FLAG_WRITE_INT);
00167 } else {
00168 ast_clear_flag(ast_channel_flags(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 ast_format_set(&ps->f.subclass.format, AST_FORMAT_SLINEAR, 0);
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
00421 static int ast_set_indication_country(const char *country)
00422 {
00423 struct ast_tone_zone *zone = NULL;
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 ao2_iterator_destroy(&i);
00662
00663 return res;
00664 }
00665
00666 static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00667 {
00668 struct ast_tone_zone *tz;
00669 int created_country = 0;
00670 char *res = CLI_SUCCESS;
00671
00672 switch (cmd) {
00673 case CLI_INIT:
00674 e->command = "indication add";
00675 e->usage =
00676 "Usage: indication add <country> <indication> \"<tonelist>\"\n"
00677 " Add the given indication to the country.\n";
00678 return NULL;
00679 case CLI_GENERATE:
00680 if (a->pos == 2) {
00681 return complete_country(a);
00682 } else {
00683 return NULL;
00684 }
00685 }
00686
00687 if (a->argc != 5) {
00688 return CLI_SHOWUSAGE;
00689 }
00690
00691 if (!(tz = ast_get_indication_zone(a->argv[2]))) {
00692
00693 ast_log(LOG_NOTICE, "Country '%s' does not exist, creating it.\n", a->argv[2]);
00694
00695 if (!(tz = ast_tone_zone_alloc())) {
00696 return CLI_FAILURE;
00697 }
00698
00699 ast_copy_string(tz->country, a->argv[2], sizeof(tz->country));
00700
00701 if (ast_register_indication_country(tz)) {
00702 ast_log(LOG_WARNING, "Unable to register new country\n");
00703 tz = ast_tone_zone_unref(tz);
00704 return CLI_FAILURE;
00705 }
00706
00707 created_country = 1;
00708 }
00709
00710 ast_tone_zone_lock(tz);
00711
00712 if (ast_register_indication(tz, a->argv[3], a->argv[4])) {
00713 ast_log(LOG_WARNING, "Unable to register indication %s/%s\n", a->argv[2], a->argv[3]);
00714 if (created_country) {
00715 ast_unregister_indication_country(a->argv[2]);
00716 }
00717 res = CLI_FAILURE;
00718 }
00719
00720 ast_tone_zone_unlock(tz);
00721
00722 tz = ast_tone_zone_unref(tz);
00723
00724 return res;
00725 }
00726
00727 static char *complete_indications(struct ast_cli_args *a)
00728 {
00729 char *res = NULL;
00730 int which = 0;
00731 size_t wordlen;
00732 struct ast_tone_zone_sound *ts;
00733 struct ast_tone_zone *tz, tmp_tz = {
00734 .nrringcadence = 0,
00735 };
00736
00737 ast_copy_string(tmp_tz.country, a->argv[a->pos - 1], sizeof(tmp_tz.country));
00738
00739 if (!(tz = ao2_find(ast_tone_zones, &tmp_tz, OBJ_POINTER))) {
00740 return NULL;
00741 }
00742
00743 wordlen = strlen(a->word);
00744
00745 ast_tone_zone_lock(tz);
00746 AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
00747 if (!strncasecmp(a->word, ts->name, wordlen) && ++which > a->n) {
00748 res = ast_strdup(ts->name);
00749 break;
00750 }
00751 }
00752 ast_tone_zone_unlock(tz);
00753
00754 tz = ast_tone_zone_unref(tz);
00755
00756 return res;
00757 }
00758
00759 static char *handle_cli_indication_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00760 {
00761 struct ast_tone_zone *tz;
00762 char *res = CLI_SUCCESS;
00763
00764 switch (cmd) {
00765 case CLI_INIT:
00766 e->command = "indication remove";
00767 e->usage =
00768 "Usage: indication remove <country> [indication]\n"
00769 " Remove the given indication from the country.\n";
00770 return NULL;
00771 case CLI_GENERATE:
00772 if (a->pos == 2) {
00773 return complete_country(a);
00774 } else if (a->pos == 3) {
00775 return complete_indications(a);
00776 }
00777 }
00778
00779 if (a->argc != 3 && a->argc != 4) {
00780 return CLI_SHOWUSAGE;
00781 }
00782
00783 if (a->argc == 3) {
00784
00785 if (ast_unregister_indication_country(a->argv[2])) {
00786 ast_log(LOG_WARNING, "Unable to unregister indication country %s\n", a->argv[2]);
00787 return CLI_FAILURE;
00788 }
00789
00790 return CLI_SUCCESS;
00791 }
00792
00793 if (!(tz = ast_get_indication_zone(a->argv[2]))) {
00794 ast_log(LOG_WARNING, "Unable to unregister indication %s/%s, country does not exists\n", a->argv[2], a->argv[3]);
00795 return CLI_FAILURE;
00796 }
00797
00798 if (ast_unregister_indication(tz, a->argv[3])) {
00799 ast_log(LOG_WARNING, "Unable to unregister indication %s/%s\n", a->argv[2], a->argv[3]);
00800 res = CLI_FAILURE;
00801 }
00802
00803 tz = ast_tone_zone_unref(tz);
00804
00805 return res;
00806 }
00807
00808 static char *handle_cli_indication_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00809 {
00810 struct ast_tone_zone *tz = NULL;
00811 struct ast_str *buf;
00812 int found_country = 0;
00813 int i;
00814
00815 switch (cmd) {
00816 case CLI_INIT:
00817 e->command = "indication show";
00818 e->usage =
00819 "Usage: indication show [<country> ...]\n"
00820 " Display either a condensed summary of all countries and indications, or a\n"
00821 " more verbose list of indications for the specified countries.\n";
00822 return NULL;
00823 case CLI_GENERATE:
00824 return complete_country(a);
00825 }
00826
00827 if (a->argc == 2) {
00828 struct ao2_iterator iter;
00829
00830 ast_cli(a->fd, "Country Description\n");
00831 ast_cli(a->fd, "===========================\n");
00832 iter = ast_tone_zone_iterator_init();
00833 while ((tz = ao2_iterator_next(&iter))) {
00834 ast_tone_zone_lock(tz);
00835 ast_cli(a->fd, "%-7.7s %s\n", tz->country, tz->description);
00836 ast_tone_zone_unlock(tz);
00837 tz = ast_tone_zone_unref(tz);
00838 }
00839 ao2_iterator_destroy(&iter);
00840 return CLI_SUCCESS;
00841 }
00842
00843 buf = ast_str_alloca(256);
00844
00845 for (i = 2; i < a->argc; i++) {
00846 struct ast_tone_zone zone_arg = {
00847 .nrringcadence = 0,
00848 };
00849 struct ast_tone_zone_sound *ts;
00850 int j;
00851
00852 ast_copy_string(zone_arg.country, a->argv[i], sizeof(zone_arg.country));
00853
00854 if (!(tz = ao2_find(ast_tone_zones, &zone_arg, OBJ_POINTER))) {
00855 continue;
00856 }
00857
00858 if (!found_country) {
00859 found_country = 1;
00860 ast_cli(a->fd, "Country Indication PlayList\n");
00861 ast_cli(a->fd, "=====================================\n");
00862 }
00863
00864 ast_tone_zone_lock(tz);
00865
00866 ast_str_set(&buf, 0, "%-7.7s %-15.15s ", tz->country, "<ringcadence>");
00867 for (j = 0; j < tz->nrringcadence; j++) {
00868 ast_str_append(&buf, 0, "%d%s", tz->ringcadence[j],
00869 (j == tz->nrringcadence - 1) ? "" : ",");
00870 }
00871 ast_str_append(&buf, 0, "\n");
00872 ast_cli(a->fd, "%s", buf->str);
00873
00874 AST_LIST_TRAVERSE(&tz->tones, ts, entry) {
00875 ast_cli(a->fd, "%-7.7s %-15.15s %s\n", tz->country, ts->name, ts->data);
00876 }
00877
00878 ast_tone_zone_unlock(tz);
00879 tz = ast_tone_zone_unref(tz);
00880 }
00881
00882 if (!found_country) {
00883 ast_cli(a->fd, "No countries matched your criteria.\n");
00884 }
00885
00886 return CLI_SUCCESS;
00887 }
00888
00889 static int is_valid_tone_zone(struct ast_tone_zone *zone)
00890 {
00891 int res;
00892
00893 ast_tone_zone_lock(zone);
00894 res = (!ast_strlen_zero(zone->description) && !AST_LIST_EMPTY(&zone->tones));
00895 ast_tone_zone_unlock(zone);
00896
00897 return res;
00898 }
00899
00900
00901
00902
00903
00904 static void store_tone_zone_ring_cadence(struct ast_tone_zone *zone, const char *val)
00905 {
00906 char buf[1024];
00907 char *ring, *c = buf;
00908
00909 ast_copy_string(buf, val, sizeof(buf));
00910
00911 while ((ring = strsep(&c, ","))) {
00912 int *tmp, val;
00913
00914 ring = ast_strip(ring);
00915
00916 if (!isdigit(ring[0]) || (val = atoi(ring)) == -1) {
00917 ast_log(LOG_WARNING, "Invalid ringcadence given '%s'.\n", ring);
00918 continue;
00919 }
00920
00921 if (!(tmp = ast_realloc(zone->ringcadence, (zone->nrringcadence + 1) * sizeof(int)))) {
00922 return;
00923 }
00924
00925 zone->ringcadence = tmp;
00926 tmp[zone->nrringcadence] = val;
00927 zone->nrringcadence++;
00928 }
00929 }
00930
00931 static void store_config_tone_zone(struct ast_tone_zone *zone, const char *var,
00932 const char *value)
00933 {
00934 CV_START(var, value);
00935
00936 CV_STR("description", zone->description);
00937 CV_F("ringcadence", store_tone_zone_ring_cadence(zone, value));
00938 CV_F("ringcadance", store_tone_zone_ring_cadence(zone, value));
00939
00940 ast_register_indication(zone, var, value);
00941
00942 CV_END;
00943 }
00944
00945 static void reset_tone_zone(struct ast_tone_zone *zone)
00946 {
00947 ast_tone_zone_lock(zone);
00948
00949 zone->killme = 0;
00950
00951 if (zone->nrringcadence) {
00952 zone->nrringcadence = 0;
00953 ast_free(zone->ringcadence);
00954 zone->ringcadence = NULL;
00955 }
00956
00957 ast_tone_zone_unlock(zone);
00958 }
00959
00960 static int parse_tone_zone(struct ast_config *cfg, const char *country)
00961 {
00962 struct ast_variable *v;
00963 struct ast_tone_zone *zone;
00964 struct ast_tone_zone tmp_zone = {
00965 .nrringcadence = 0,
00966 };
00967 int allocd = 0;
00968
00969 ast_copy_string(tmp_zone.country, country, sizeof(tmp_zone.country));
00970
00971 if ((zone = ao2_find(ast_tone_zones, &tmp_zone, OBJ_POINTER))) {
00972 reset_tone_zone(zone);
00973 } else if ((zone = ast_tone_zone_alloc())) {
00974 allocd = 1;
00975 ast_copy_string(zone->country, country, sizeof(zone->country));
00976 } else {
00977 return -1;
00978 }
00979
00980 ast_tone_zone_lock(zone);
00981 for (v = ast_variable_browse(cfg, country); v; v = v->next) {
00982 store_config_tone_zone(zone, v->name, v->value);
00983 }
00984 ast_tone_zone_unlock(zone);
00985
00986 if (allocd) {
00987 if (!is_valid_tone_zone(zone)) {
00988 ast_log(LOG_WARNING, "Indication country '%s' is invalid\n", country);
00989 } else if (ast_register_indication_country(zone)) {
00990 ast_log(LOG_WARNING, "Unable to register indication country '%s'.\n",
00991 country);
00992 }
00993 }
00994
00995 zone = ast_tone_zone_unref(zone);
00996
00997 return 0;
00998 }
00999
01000
01001
01002
01003
01004 static int tone_zone_mark(void *obj, void *arg, int flags)
01005 {
01006 struct ast_tone_zone *zone = obj;
01007 struct ast_tone_zone_sound *s;
01008
01009 ast_tone_zone_lock(zone);
01010
01011 zone->killme = 1;
01012
01013 AST_LIST_TRAVERSE(&zone->tones, s, entry) {
01014 s->killme = 1;
01015 }
01016
01017 ast_tone_zone_unlock(zone);
01018
01019 return 0;
01020 }
01021
01022
01023
01024
01025
01026 static int prune_tone_zone(void *obj, void *arg, int flags)
01027 {
01028 struct ast_tone_zone *zone = obj;
01029 struct ast_tone_zone_sound *s;
01030
01031 ast_tone_zone_lock(zone);
01032
01033 AST_LIST_TRAVERSE_SAFE_BEGIN(&zone->tones, s, entry) {
01034 if (s->killme) {
01035 AST_LIST_REMOVE_CURRENT(entry);
01036 s = ast_tone_zone_sound_unref(s);
01037 }
01038 }
01039 AST_LIST_TRAVERSE_SAFE_END;
01040
01041 ast_tone_zone_unlock(zone);
01042
01043 return zone->killme ? CMP_MATCH : 0;
01044 }
01045
01046
01047 static int load_indications(int reload)
01048 {
01049 struct ast_config *cfg;
01050 const char *cxt = NULL;
01051 const char *country = NULL;
01052 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01053 int res = -1;
01054
01055 cfg = ast_config_load2(config, "indications", config_flags);
01056
01057 if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
01058 ast_log(LOG_WARNING, "Can't find indications config file %s.\n", config);
01059 return 0;
01060 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
01061 return 0;
01062 }
01063
01064
01065 ao2_lock(ast_tone_zones);
01066
01067 ao2_callback(ast_tone_zones, OBJ_NODATA, tone_zone_mark, NULL);
01068
01069
01070 while ((cxt = ast_category_browse(cfg, cxt))) {
01071
01072 if (!strcasecmp(cxt, "general")) {
01073 continue;
01074 }
01075
01076 if (parse_tone_zone(cfg, cxt)) {
01077 goto return_cleanup;
01078 }
01079 }
01080
01081 ao2_callback(ast_tone_zones, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK,
01082 prune_tone_zone, NULL);
01083
01084
01085 country = ast_variable_retrieve(cfg, "general", "country");
01086 if (ast_strlen_zero(country) || ast_set_indication_country(country)) {
01087 ast_log(LOG_WARNING, "Unable to set the default country (for indication tones)\n");
01088 }
01089
01090 res = 0;
01091
01092 return_cleanup:
01093 ao2_unlock(ast_tone_zones);
01094 ast_config_destroy(cfg);
01095
01096 return res;
01097 }
01098
01099
01100 static struct ast_cli_entry cli_indications[] = {
01101 AST_CLI_DEFINE(handle_cli_indication_add, "Add the given indication to the country"),
01102 AST_CLI_DEFINE(handle_cli_indication_remove, "Remove the given indication from the country"),
01103 AST_CLI_DEFINE(handle_cli_indication_show, "Display a list of all countries/indications")
01104 };
01105
01106 static int ast_tone_zone_hash(const void *obj, const int flags)
01107 {
01108 const struct ast_tone_zone *zone = obj;
01109
01110 return ast_str_case_hash(zone->country);
01111 }
01112
01113 static int ast_tone_zone_cmp(void *obj, void *arg, int flags)
01114 {
01115 struct ast_tone_zone *zone = obj;
01116 struct ast_tone_zone *zone_arg = arg;
01117
01118 return (!strcasecmp(zone->country, zone_arg->country)) ?
01119 CMP_MATCH | CMP_STOP : 0;
01120 }
01121
01122 int ast_tone_zone_data_add_structure(struct ast_data *tree, struct ast_tone_zone *zone)
01123 {
01124 struct ast_data *data_zone_sound;
01125 struct ast_tone_zone_sound *s;
01126
01127 ast_data_add_structure(ast_tone_zone, tree, zone);
01128
01129 if (AST_LIST_EMPTY(&zone->tones)) {
01130 return 0;
01131 }
01132
01133 data_zone_sound = ast_data_add_node(tree, "tones");
01134 if (!data_zone_sound) {
01135 return -1;
01136 }
01137
01138 ast_tone_zone_lock(zone);
01139
01140 AST_LIST_TRAVERSE(&zone->tones, s, entry) {
01141 ast_data_add_structure(ast_tone_zone_sound, data_zone_sound, s);
01142 }
01143
01144 ast_tone_zone_unlock(zone);
01145
01146 return 0;
01147 }
01148
01149
01150 int ast_indications_init(void)
01151 {
01152 if (!(ast_tone_zones = ao2_container_alloc(NUM_TONE_ZONE_BUCKETS,
01153 ast_tone_zone_hash, ast_tone_zone_cmp))) {
01154 return -1;
01155 }
01156
01157 if (load_indications(0)) {
01158 return -1;
01159 }
01160
01161 ast_cli_register_multiple(cli_indications, ARRAY_LEN(cli_indications));
01162
01163 return 0;
01164 }
01165
01166
01167 int ast_indications_reload(void)
01168 {
01169 return load_indications(1);
01170 }
01171