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