Add 's' option to Page application which checks devicestate before dialing. (issue...
[asterisk/asterisk.git] / apps / app_page.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (c) 2004 - 2006 Digium, Inc.  All rights reserved.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * This code is released under the GNU General Public License
9  * version 2.0.  See LICENSE for more information.
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  */
18
19 /*! \file
20  *
21  * \brief page() - Paging application
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \ingroup applications
26  */
27
28 /*** MODULEINFO
29         <depend>zaptel</depend>
30  ***/
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <string.h>
40 #include <errno.h>
41
42 #include "asterisk/options.h"
43 #include "asterisk/logger.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/file.h"
48 #include "asterisk/app.h"
49 #include "asterisk/chanvars.h"
50 #include "asterisk/utils.h"
51 #include "asterisk/devicestate.h"
52
53 static const char *app_page= "Page";
54
55 static const char *page_synopsis = "Pages phones";
56
57 static const char *page_descrip =
58 "Page(Technology/Resource&Technology2/Resource2[|options])\n"
59 "  Places outbound calls to the given technology / resource and dumps\n"
60 "them into a conference bridge as muted participants.  The original\n"
61 "caller is dumped into the conference as a speaker and the room is\n"
62 "destroyed when the original caller leaves.  Valid options are:\n"
63 "        d - full duplex audio\n"
64 "        q - quiet, do not play beep to caller\n"
65 "        r - record the page into a file (see 'r' for app_meetme)\n"
66 "        s - only dial channel if devicestate says it is not in use\n";
67
68 enum {
69         PAGE_DUPLEX = (1 << 0),
70         PAGE_QUIET = (1 << 1),
71         PAGE_RECORD = (1 << 2),
72         PAGE_SKIP = (1 << 3),
73 } page_opt_flags;
74
75 AST_APP_OPTIONS(page_opts, {
76         AST_APP_OPTION('d', PAGE_DUPLEX),
77         AST_APP_OPTION('q', PAGE_QUIET),
78         AST_APP_OPTION('r', PAGE_RECORD),
79         AST_APP_OPTION('s', PAGE_SKIP),
80 });
81
82 struct calloutdata {
83         char cidnum[64];
84         char cidname[64];
85         char tech[64];
86         char resource[256];
87         char meetmeopts[64];
88         struct ast_variable *variables;
89 };
90
91 static void *page_thread(void *data)
92 {
93         struct calloutdata *cd = data;
94
95         ast_pbx_outgoing_app(cd->tech, AST_FORMAT_SLINEAR, cd->resource, 30000,
96                 "MeetMe", cd->meetmeopts, NULL, 0, cd->cidnum, cd->cidname, cd->variables, NULL, NULL);
97
98         free(cd);
99
100         return NULL;
101 }
102
103 static void launch_page(struct ast_channel *chan, const char *meetmeopts, const char *tech, const char *resource)
104 {
105         struct calloutdata *cd;
106         const char *varname;
107         struct ast_variable *lastvar = NULL;
108         struct ast_var_t *varptr;
109         pthread_t t;
110         pthread_attr_t attr;
111
112         if (!(cd = ast_calloc(1, sizeof(*cd))))
113                 return;
114
115         /* Copy data from our page over */
116         ast_copy_string(cd->cidnum, chan->cid.cid_num ? chan->cid.cid_num : "", sizeof(cd->cidnum));
117         ast_copy_string(cd->cidname, chan->cid.cid_name ? chan->cid.cid_name : "", sizeof(cd->cidname));
118         ast_copy_string(cd->tech, tech, sizeof(cd->tech));
119         ast_copy_string(cd->resource, resource, sizeof(cd->resource));
120         ast_copy_string(cd->meetmeopts, meetmeopts, sizeof(cd->meetmeopts));
121         
122         AST_LIST_TRAVERSE(&chan->varshead, varptr, entries) {
123                 struct ast_variable *newvar = NULL;
124
125                 if (!(varname = ast_var_full_name(varptr)) || (varname[0] != '_'))
126                         continue;
127                         
128                 if (varname[1] == '_')
129                         newvar = ast_variable_new(varname, ast_var_value(varptr));
130                 else
131                         newvar = ast_variable_new(&varname[1], ast_var_value(varptr));
132                 
133                 if (newvar) {
134                         if (lastvar)
135                                 lastvar->next = newvar;
136                         else
137                                 cd->variables = newvar;
138                         lastvar = newvar;
139                 }
140         }
141
142         /* Spawn thread to handle this page */
143         pthread_attr_init(&attr);
144         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
145         if (ast_pthread_create(&t, &attr, page_thread, cd)) {
146                 ast_log(LOG_WARNING, "Unable to create paging thread: %s\n", strerror(errno));
147                 free(cd);
148         }
149
150         return;
151 }
152
153 static int page_exec(struct ast_channel *chan, void *data)
154 {
155         struct ast_module_user *u;
156         char *options;
157         char *tech, *resource;
158         char meetmeopts[80];
159         struct ast_flags flags = { 0 };
160         unsigned int confid = ast_random();
161         struct ast_app *app;
162         char *tmp;
163         int res=0;
164         char originator[AST_CHANNEL_NAME];
165
166         if (ast_strlen_zero(data)) {
167                 ast_log(LOG_WARNING, "This application requires at least one argument (destination(s) to page)\n");
168                 return -1;
169         }
170
171         u = ast_module_user_add(chan);
172
173         if (!(app = pbx_findapp("MeetMe"))) {
174                 ast_log(LOG_WARNING, "There is no MeetMe application available!\n");
175                 ast_module_user_remove(u);
176                 return -1;
177         };
178
179         options = ast_strdupa(data);
180
181         ast_copy_string(originator, chan->name, sizeof(originator));
182         if ((tmp = strchr(originator, '-')))
183                 *tmp = '\0';
184
185         tmp = strsep(&options, "|");
186         if (options)
187                 ast_app_parse_options(page_opts, &flags, NULL, options);
188
189         snprintf(meetmeopts, sizeof(meetmeopts), "%ud|%s%sqxdw(5)", confid, (ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "m"),
190                 (ast_test_flag(&flags, PAGE_RECORD) ? "r" : "") );
191
192         while ((tech = strsep(&tmp, "&"))) {
193                 int state = 0;
194
195                 /* don't call the originating device */
196                 if (!strcasecmp(tech, originator))
197                         continue;
198
199                 if (!(resource = strchr(tech, '/'))) {
200                         ast_log(LOG_WARNING, "Incomplete destination '%s' supplied.\n", tech);
201                         continue;
202                 }
203
204                 /* Ensure device is not in use if skip option is enabled */
205                 if (ast_test_flag(&flags, PAGE_SKIP) && (state = ast_device_state(tech)) != AST_DEVICE_NOT_INUSE) {
206                         ast_log(LOG_WARNING, "Destination '%s' has device state '%s'.\n", tech, devstate2str(state));
207                         continue;
208                 }
209                 
210                 *resource++ = '\0';
211                 launch_page(chan, meetmeopts, tech, resource);
212         }
213
214         if (!ast_test_flag(&flags, PAGE_QUIET)) {
215                 res = ast_streamfile(chan, "beep", chan->language);
216                 if (!res)
217                         res = ast_waitstream(chan, "");
218         }
219
220         if (!res) {
221                 snprintf(meetmeopts, sizeof(meetmeopts), "%ud|A%s%sqxd", confid, (ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "t"), 
222                         (ast_test_flag(&flags, PAGE_RECORD) ? "r" : "") );
223                 pbx_exec(chan, app, meetmeopts);
224         }
225
226         ast_module_user_remove(u);
227
228         return -1;
229 }
230
231 static int unload_module(void)
232 {
233         int res;
234
235         res =  ast_unregister_application(app_page);
236
237         ast_module_user_hangup_all();
238
239         return res;
240 }
241
242 static int load_module(void)
243 {
244         return ast_register_application(app_page, page_exec, page_synopsis, page_descrip);
245 }
246
247 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Page Multiple Phones");
248