a957b7828248e69e6e15e6955646f6af96802fa6
[asterisk/asterisk.git] / res / res_indications.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2002, Pauline Middelink
5  *
6  *
7  * See http://www.asterisk.org for more information about
8  * the Asterisk project. Please do not directly contact
9  * any of the maintainers of this project for assistance;
10  * the project provides a web site, mailing lists and IRC
11  * channels for your use.
12  *
13  * This program is free software, distributed under the terms of
14  * the GNU General Public License Version 2. See the LICENSE file
15  * at the top of the source tree.
16  */
17
18 /*! \file res_indications.c 
19  *
20  * \brief Load the indications
21  * 
22  * \author Pauline Middelink <middelink@polyware.nl>
23  *
24  * Load the country specific dialtones into the asterisk PBX.
25  */
26  
27 #include "asterisk.h"
28
29 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30
31 #include <ctype.h>
32 #include <sys/stat.h>
33
34 #include "asterisk/lock.h"
35 #include "asterisk/file.h"
36 #include "asterisk/cli.h"
37 #include "asterisk/config.h"
38 #include "asterisk/channel.h"
39 #include "asterisk/pbx.h"
40 #include "asterisk/module.h"
41 #include "asterisk/translate.h"
42 #include "asterisk/indications.h"
43 #include "asterisk/utils.h"
44
45 /* Globals */
46 static const char config[] = "indications.conf";
47
48 char *playtones_desc=
49 "  PlayTones(arg): Plays a tone list. Execution will continue with the next step immediately,\n"
50 "while the tones continue to play.\n"
51 "Arg is either the tone name defined in the indications.conf configuration file, or a directly\n"
52 "specified list of frequencies and durations.\n"
53 "See the sample indications.conf for a description of the specification of a tonelist.\n\n"
54 "Use the StopPlayTones application to stop the tones playing. \n";
55
56 /*
57  * Implementation of functions provided by this module
58  */
59
60 /*!
61  * \brief Add a country to indication
62  * \param e the ast_cli_entry for this CLI command
63  * \param cmd the reason we are being called
64  * \param a the arguments being passed to us
65  */
66 static char *handle_cli_indication_add(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
67 {
68         struct ind_tone_zone *tz;
69         int created_country = 0;
70
71         switch (cmd) {
72         case CLI_INIT:
73                 e->command = "indication add";
74                 e->usage =
75                         "Usage: indication add <country> <indication> \"<tonelist>\"\n"
76                         "       Add the given indication to the country.\n";
77                 return NULL;
78         case CLI_GENERATE:
79                 return NULL;
80         }
81
82         if (a->argc != 5)
83                 return CLI_SHOWUSAGE;
84
85         tz = ast_get_indication_zone(a->argv[2]);
86         if (!tz) {
87                 /* country does not exist, create it */
88                 ast_log(LOG_NOTICE, "Country '%s' does not exist, creating it.\n", a->argv[2]);
89                 
90                 if (!(tz = ast_calloc(1, sizeof(*tz)))) {
91                         return CLI_FAILURE;
92                 }
93                 ast_copy_string(tz->country, a->argv[2], sizeof(tz->country));
94                 if (ast_register_indication_country(tz)) {
95                         ast_log(LOG_WARNING, "Unable to register new country\n");
96                         ast_free(tz);
97                         return CLI_FAILURE;
98                 }
99                 created_country = 1;
100         }
101         if (ast_register_indication(tz, a->argv[3], a->argv[4])) {
102                 ast_log(LOG_WARNING, "Unable to register indication %s/%s\n", a->argv[2], a->argv[3]);
103                 if (created_country)
104                         ast_unregister_indication_country(a->argv[2]);
105                 return CLI_FAILURE;
106         }
107         return CLI_SUCCESS;
108 }
109
110 /*!
111  * \brief Remove a country from indication
112  * \param e the ast_cli_entry for this CLI command
113  * \param cmd the reason we are being called
114  * \param a the arguments being passed to us
115  */
116 static char *handle_cli_indication_remove(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
117 {
118         struct ind_tone_zone *tz;
119
120         switch (cmd) {
121         case CLI_INIT:
122                 e->command = "indication remove";
123                 e->usage =
124                         "Usage: indication remove <country> <indication>\n"
125                         "       Remove the given indication from the country.\n";
126                 return NULL;
127         case CLI_GENERATE:
128                 return NULL;
129         }
130
131         if (a->argc != 3 && a->argc != 4)
132                 return CLI_SHOWUSAGE;
133
134         if (a->argc == 3) {
135                 /* remove entiry country */
136                 if (ast_unregister_indication_country(a->argv[2])) {
137                         ast_log(LOG_WARNING, "Unable to unregister indication country %s\n", a->argv[2]);
138                         return CLI_FAILURE;
139                 }
140                 return CLI_SUCCESS;
141         }
142
143         tz = ast_get_indication_zone(a->argv[2]);
144         if (!tz) {
145                 ast_log(LOG_WARNING, "Unable to unregister indication %s/%s, country does not exists\n", a->argv[2], a->argv[3]);
146                 return CLI_FAILURE;
147         }
148         if (ast_unregister_indication(tz, a->argv[3])) {
149                 ast_log(LOG_WARNING, "Unable to unregister indication %s/%s\n", a->argv[2], a->argv[3]);
150                 return CLI_FAILURE;
151         }
152         return CLI_SUCCESS;
153 }
154
155 /*!
156  * \brief Show the current indications
157  * \param e the ast_cli_entry for this CLI command
158  * \param cmd the reason we are being called
159  * \param a the arguments being passed to us
160  */
161 static char *handle_cli_indication_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
162 {
163         struct ind_tone_zone *tz = NULL;
164         char buf[256];
165         int found_country = 0;
166
167         switch (cmd) {
168         case CLI_INIT:
169                 e->command = "indication show";
170                 e->usage =
171                         "Usage: indication show [<country> ...]\n"
172                         "       Display either a condensed for of all country/indications, or the\n"
173                         "       indications for the specified countries.\n";
174                 return NULL;
175         case CLI_GENERATE:
176                 return NULL;
177         }
178
179         if (a->argc == 2) {
180                 /* no arguments, show a list of countries */
181                 ast_cli(a->fd, "Country Alias   Description\n");
182                 ast_cli(a->fd, "===========================\n");
183                 while ((tz = ast_walk_indications(tz)))
184                         ast_cli(a->fd, "%-7.7s %-7.7s %s\n", tz->country, tz->alias, tz->description);
185                 return CLI_SUCCESS;
186         }
187         /* there was a request for specific country(ies), lets humor them */
188         while ((tz = ast_walk_indications(tz))) {
189                 int i, j;
190                 for (i = 2; i < a->argc; i++) {
191                         if (strcasecmp(tz->country, a->argv[i]) == 0 && !tz->alias[0]) {
192                                 struct ind_tone_zone_sound* ts;
193                                 if (!found_country) {
194                                         found_country = 1;
195                                         ast_cli(a->fd, "Country Indication      PlayList\n");
196                                         ast_cli(a->fd, "=====================================\n");
197                                 }
198                                 j = snprintf(buf, sizeof(buf), "%-7.7s %-15.15s ", tz->country, "<ringcadence>");
199                                 for (i = 0; i < tz->nrringcadence; i++) {
200                                         j += snprintf(buf + j, sizeof(buf) - j, "%d,", tz->ringcadence[i]);
201                                 }
202                                 if (tz->nrringcadence)
203                                         j--;
204                                 ast_copy_string(buf + j, "\n", sizeof(buf) - j);
205                                 ast_cli(a->fd, buf);
206                                 for (ts = tz->tones; ts; ts = ts->next)
207                                         ast_cli(a->fd, "%-7.7s %-15.15s %s\n", tz->country, ts->name, ts->data);
208                                 break;
209                         }
210                 }
211         }
212         if (!found_country)
213                 ast_cli(a->fd, "No countries matched your criteria.\n");
214         return CLI_SUCCESS;
215 }
216
217 /*!
218  * \brief play tone for indication country
219  * \param chan ast_channel to play the sounds back to
220  * \param data contains tone to play
221  */
222 static int handle_playtones(struct ast_channel *chan, void *data)
223 {
224         struct ind_tone_zone_sound *ts;
225         int res;
226
227         if (!data || !((char*)data)[0]) {
228                 ast_log(LOG_NOTICE,"Nothing to play\n");
229                 return -1;
230         }
231         ts = ast_get_indication_tone(chan->zone, (const char*)data);
232         if (ts && ts->data[0])
233                 res = ast_playtones_start(chan, 0, ts->data, 0);
234         else
235                 res = ast_playtones_start(chan, 0, (const char*)data, 0);
236         if (res)
237                 ast_log(LOG_NOTICE,"Unable to start playtones\n");
238         return res;
239 }
240
241 /*!
242  * \brief Stop tones playing
243  * \param chan 
244  * \param data 
245  */
246 static int handle_stopplaytones(struct ast_channel *chan, void *data)
247 {
248         ast_playtones_stop(chan);
249         return 0;
250 }
251
252 /*! \brief load indications module */
253 static int ind_load_module(int reload)
254 {
255         struct ast_config *cfg;
256         struct ast_variable *v;
257         char *cxt;
258         char *c;
259         struct ind_tone_zone *tones;
260         const char *country = NULL;
261         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
262
263         /* that the following cast is needed, is yuk! */
264         /* yup, checked it out. It is NOT written to. */
265         cfg = ast_config_load((char *)config, config_flags);
266         if (!cfg)
267                 return -1;
268         else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
269                 return 0;
270
271         if (reload)
272                 ast_unregister_indication_country(NULL);
273
274         /* Use existing config to populate the Indication table */
275         cxt = ast_category_browse(cfg, NULL);
276         while(cxt) {
277                 /* All categories but "general" are considered countries */
278                 if (!strcasecmp(cxt, "general")) {
279                         cxt = ast_category_browse(cfg, cxt);
280                         continue;
281                 }               
282                 if (!(tones = ast_calloc(1, sizeof(*tones)))) {
283                         ast_config_destroy(cfg);
284                         return -1;
285                 }
286                 ast_copy_string(tones->country,cxt,sizeof(tones->country));
287
288                 v = ast_variable_browse(cfg, cxt);
289                 while(v) {
290                         if (!strcasecmp(v->name, "description")) {
291                                 ast_copy_string(tones->description, v->value, sizeof(tones->description));
292                         } else if ((!strcasecmp(v->name,"ringcadence"))||(!strcasecmp(v->name,"ringcadance"))) {
293                                 char *ring,*rings = ast_strdupa(v->value);
294                                 c = rings;
295                                 ring = strsep(&c,",");
296                                 while (ring) {
297                                         int *tmp, val;
298                                         if (!isdigit(ring[0]) || (val=atoi(ring))==-1) {
299                                                 ast_log(LOG_WARNING,"Invalid ringcadence given '%s' at line %d.\n",ring,v->lineno);
300                                                 ring = strsep(&c,",");
301                                                 continue;
302                                         }                                       
303                                         if (!(tmp = ast_realloc(tones->ringcadence, (tones->nrringcadence + 1) * sizeof(int)))) {
304                                                 ast_config_destroy(cfg);
305                                                 return -1;
306                                         }
307                                         tones->ringcadence = tmp;
308                                         tmp[tones->nrringcadence] = val;
309                                         tones->nrringcadence++;
310                                         /* next item */
311                                         ring = strsep(&c,",");
312                                 }
313                         } else if (!strcasecmp(v->name,"alias")) {
314                                 char *countries = ast_strdupa(v->value);
315                                 c = countries;
316                                 country = strsep(&c,",");
317                                 while (country) {
318                                         struct ind_tone_zone* azone;
319                                         if (!(azone = ast_calloc(1, sizeof(*azone)))) {
320                                                 ast_config_destroy(cfg);
321                                                 return -1;
322                                         }
323                                         ast_copy_string(azone->country, country, sizeof(azone->country));
324                                         ast_copy_string(azone->alias, cxt, sizeof(azone->alias));
325                                         if (ast_register_indication_country(azone)) {
326                                                 ast_log(LOG_WARNING, "Unable to register indication alias at line %d.\n",v->lineno);
327                                                 ast_free(tones);
328                                         }
329                                         /* next item */
330                                         country = strsep(&c,",");
331                                 }
332                         } else {
333                                 /* add tone to country */
334                                 struct ind_tone_zone_sound *ps,*ts;
335                                 for (ps=NULL,ts=tones->tones; ts; ps=ts, ts=ts->next) {
336                                         if (strcasecmp(v->name,ts->name)==0) {
337                                                 /* already there */
338                                                 ast_log(LOG_NOTICE,"Duplicate entry '%s', skipped.\n",v->name);
339                                                 goto out;
340                                         }
341                                 }
342                                 /* not there, add it to the back */                             
343                                 if (!(ts = ast_malloc(sizeof(*ts)))) {
344                                         ast_config_destroy(cfg);
345                                         return -1;
346                                 }
347                                 ts->next = NULL;
348                                 ts->name = ast_strdup(v->name);
349                                 ts->data = ast_strdup(v->value);
350                                 if (ps)
351                                         ps->next = ts;
352                                 else
353                                         tones->tones = ts;
354                         }
355 out:                    v = v->next;
356                 }
357                 if (tones->description[0] || tones->alias[0] || tones->tones) {
358                         if (ast_register_indication_country(tones)) {
359                                 ast_log(LOG_WARNING, "Unable to register indication at line %d.\n",v->lineno);
360                                 ast_free(tones);
361                         }
362                 } else ast_free(tones);
363
364                 cxt = ast_category_browse(cfg, cxt);
365         }
366
367         /* determine which country is the default */
368         country = ast_variable_retrieve(cfg,"general","country");
369         if (!country || !*country || ast_set_indication_country(country))
370                 ast_log(LOG_WARNING,"Unable to set the default country (for indication tones)\n");
371
372         ast_config_destroy(cfg);
373         return 0;
374 }
375
376 /*! \brief CLI entries for commands provided by this module */
377 static struct ast_cli_entry cli_indications[] = {
378         AST_CLI_DEFINE(handle_cli_indication_add,    "Add the given indication to the country"),
379         AST_CLI_DEFINE(handle_cli_indication_remove, "Remove the given indication from the country"),
380         AST_CLI_DEFINE(handle_cli_indication_show,   "Display a list of all countries/indications")
381 };
382
383 /*! \brief Unload indicators module */
384 static int unload_module(void)
385 {
386         /* remove the registed indications... */
387         ast_unregister_indication_country(NULL);
388
389         /* and the functions */
390         ast_cli_unregister_multiple(cli_indications, sizeof(cli_indications) / sizeof(struct ast_cli_entry));
391         ast_unregister_application("PlayTones");
392         ast_unregister_application("StopPlayTones");
393         return 0;
394 }
395
396
397 /*! \brief Load indications module */
398 static int load_module(void)
399 {
400         if (ind_load_module(0))
401                 return AST_MODULE_LOAD_DECLINE; 
402         ast_cli_register_multiple(cli_indications, sizeof(cli_indications) / sizeof(struct ast_cli_entry));
403         ast_register_application("PlayTones", handle_playtones, "Play a tone list", playtones_desc);
404         ast_register_application("StopPlayTones", handle_stopplaytones, "Stop playing a tone list","  StopPlayTones(): Stop playing a tone list");
405
406         return AST_MODULE_LOAD_SUCCESS;
407 }
408
409 /*! \brief Reload indications module */
410 static int reload(void)
411 {
412         return ind_load_module(1);
413 }
414
415 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Region-specific tones",
416                 .load = load_module,
417                 .unload = unload_module,
418                 .reload = reload,
419                );