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