Merged revisions 19348 via svnmerge from
[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 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #include "asterisk.h"
35
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
38 #include "asterisk/options.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/module.h"
43 #include "asterisk/file.h"
44 #include "asterisk/app.h"
45 #include "asterisk/chanvars.h"
46 #include "asterisk/utils.h"
47
48 static const char *tdesc = "Page Multiple Phones";
49
50 static const char *app_page= "Page";
51
52 static const char *page_synopsis = "Pages phones";
53
54 static const char *page_descrip =
55 "Page(Technology/Resource&Technology2/Resource2[|options])\n"
56 "  Places outbound calls to the given technology / resource and dumps\n"
57 "them into a conference bridge as muted participants.  The original\n"
58 "caller is dumped into the conference as a speaker and the room is\n"
59 "destroyed when the original caller leaves.  Valid options are:\n"
60 "        d - full duplex audio\n"
61 "        q - quiet, do not play beep to caller\n";
62
63 LOCAL_USER_DECL;
64
65 enum {
66         PAGE_DUPLEX = (1 << 0),
67         PAGE_QUIET = (1 << 1),
68 } page_opt_flags;
69
70 AST_APP_OPTIONS(page_opts, {
71         AST_APP_OPTION('d', PAGE_DUPLEX),
72         AST_APP_OPTION('q', PAGE_QUIET),
73 });
74
75 struct calloutdata {
76         char cidnum[64];
77         char cidname[64];
78         char tech[64];
79         char resource[256];
80         char meetmeopts[64];
81         struct ast_variable *variables;
82 };
83
84 static void *page_thread(void *data)
85 {
86         struct calloutdata *cd = data;
87         ast_pbx_outgoing_app(cd->tech, AST_FORMAT_SLINEAR, cd->resource, 30000,
88                 "MeetMe", cd->meetmeopts, NULL, 0, cd->cidnum, cd->cidname, cd->variables, NULL, NULL);
89         free(cd);
90         return NULL;
91 }
92
93 static void launch_page(struct ast_channel *chan, const char *meetmeopts, const char *tech, const char *resource)
94 {
95         struct calloutdata *cd;
96         const char *varname;
97         struct ast_variable *lastvar = NULL;
98         struct ast_var_t *varptr;
99         pthread_t t;
100         pthread_attr_t attr;
101         if ((cd = ast_calloc(1, sizeof(*cd)))) {
102                 ast_copy_string(cd->cidnum, chan->cid.cid_num ? chan->cid.cid_num : "", sizeof(cd->cidnum));
103                 ast_copy_string(cd->cidname, chan->cid.cid_name ? chan->cid.cid_name : "", sizeof(cd->cidname));
104                 ast_copy_string(cd->tech, tech, sizeof(cd->tech));
105                 ast_copy_string(cd->resource, resource, sizeof(cd->resource));
106                 ast_copy_string(cd->meetmeopts, meetmeopts, sizeof(cd->meetmeopts));
107
108                 AST_LIST_TRAVERSE(&chan->varshead, varptr, entries) {
109                         if (!(varname = ast_var_full_name(varptr)))
110                                 continue;
111                         if (varname[0] == '_') {
112                                 struct ast_variable *newvar = NULL;
113
114                                 if (varname[1] == '_') {
115                                         newvar = ast_variable_new(varname, ast_var_value(varptr));
116                                 } else {
117                                         newvar = ast_variable_new(&varname[1], ast_var_value(varptr));
118                                 }
119
120                                 if (newvar) {
121                                         if (lastvar)
122                                                 lastvar->next = newvar;
123                                         else
124                                                 cd->variables = newvar;
125                                         lastvar = newvar;
126                                 }
127                         }
128                 }
129
130                 pthread_attr_init(&attr);
131                 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
132                 if (ast_pthread_create(&t, &attr, page_thread, cd)) {
133                         ast_log(LOG_WARNING, "Unable to create paging thread: %s\n", strerror(errno));
134                         free(cd);
135                 }
136         }
137 }
138
139 static int page_exec(struct ast_channel *chan, void *data)
140 {
141         struct localuser *u;
142         char *options;
143         char *tech, *resource;
144         char meetmeopts[80];
145         struct ast_flags flags = { 0 };
146         unsigned int confid = ast_random();
147         struct ast_app *app;
148         char *tmp;
149         int res=0;
150         char originator[AST_CHANNEL_NAME];
151
152         if (ast_strlen_zero(data)) {
153                 ast_log(LOG_WARNING, "This application requires at least one argument (destination(s) to page)\n");
154                 return -1;
155         }
156
157         LOCAL_USER_ADD(u);
158
159         if (!(app = pbx_findapp("MeetMe"))) {
160                 ast_log(LOG_WARNING, "There is no MeetMe application available!\n");
161                 LOCAL_USER_REMOVE(u);
162                 return -1;
163         };
164
165         if (!(options = ast_strdupa(data))) {
166                 LOCAL_USER_REMOVE(u);
167                 return -1;
168         }
169
170         tmp = strsep(&options, "|");
171         if (options)
172                 ast_app_parse_options(page_opts, &flags, NULL, options);
173
174         snprintf(meetmeopts, sizeof(meetmeopts), "%ud|%sqxdw", confid, ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "m");
175
176         ast_copy_string(originator, chan->name, sizeof(originator));
177         if ((tmp = strchr(originator, '-')))
178                 *tmp = '\0';
179
180         while ((tech = strsep(&tmp, "&"))) {
181                 /* don't call the originating device */
182                 if (!strcasecmp(tech, originator))
183                         continue;
184
185                 if ((resource = strchr(tech, '/'))) {
186                         *resource++ = '\0';
187                         launch_page(chan, meetmeopts, tech, resource);
188                 } else {
189                         ast_log(LOG_WARNING, "Incomplete destination '%s' supplied.\n", tech);
190                 }
191         }
192
193         if (!ast_test_flag(&flags, PAGE_QUIET)) {
194                 res = ast_streamfile(chan, "beep", chan->language);
195                 if (!res)
196                         res = ast_waitstream(chan, "");
197         }
198         if (!res) {
199                 snprintf(meetmeopts, sizeof(meetmeopts), "%ud|A%sqxd", confid, ast_test_flag(&flags, PAGE_DUPLEX) ? "" : "t");
200                 pbx_exec(chan, app, meetmeopts);
201         }
202
203         LOCAL_USER_REMOVE(u);
204
205         return -1;
206 }
207
208 int unload_module(void)
209 {
210         int res;
211
212         res =  ast_unregister_application(app_page);
213
214         STANDARD_HANGUP_LOCALUSERS;
215
216         return res;
217 }
218
219 int load_module(void)
220 {
221         return ast_register_application(app_page, page_exec, page_synopsis, page_descrip);
222 }
223
224 const char *description(void)
225 {
226         return (char *) tdesc;
227 }
228
229 int usecount(void)
230 {
231         int res;
232
233         STANDARD_USECOUNT(res);
234
235         return res;
236 }
237
238 const char *key()
239 {
240         return ASTERISK_GPL_KEY;
241 }