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