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