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