Version 0.1.1 from FTP
[asterisk/asterisk.git] / apps / app_dial.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Trivial application to dial a channel
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <asterisk/file.h>
15 #include <asterisk/logger.h>
16 #include <asterisk/channel.h>
17 #include <asterisk/pbx.h>
18 #include <asterisk/options.h>
19 #include <asterisk/module.h>
20 #include <asterisk/translate.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <sys/time.h>
28
29 #include <pthread.h>
30
31
32 static char *tdesc = "Trivial Dialing Application";
33
34 static char *app = "Dial";
35
36 /* We define a customer "local user" structure because we
37    use it not only for keeping track of what is in use but
38    also for keeping track of who we're dialing. */
39
40 struct localuser {
41         struct ast_channel *chan;
42         int stillgoing;
43         int allowredirect;
44         struct localuser *next;
45 };
46
47 LOCAL_USER_DECL;
48
49 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
50 {
51         /* Hang up a tree of stuff */
52         struct localuser *oo;
53         while(outgoing) {
54                 /* Hangup any existing lines we have open */
55                 if (outgoing->chan != exception)
56                         ast_hangup(outgoing->chan);
57                 oo = outgoing;
58                 outgoing=outgoing->next;
59                 free(oo);
60         }
61 }
62
63 static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir)
64 {
65         fd_set rfds, efds;
66         struct localuser *o;
67         int found;
68         int numlines;
69         int numbusies = 0;
70         int orig = *to;
71         struct timeval tv;
72         struct ast_frame *f;
73         struct ast_channel *peer = NULL;
74         /* Watch all outgoing channels looking for an answer of some sort.  */
75         tv.tv_sec = *to / 1000;
76         tv.tv_usec = (*to % 1000) * 1000;
77         while((tv.tv_sec || tv.tv_usec) && !peer) {
78                 FD_ZERO(&rfds);
79                 FD_ZERO(&efds);
80                 /* Always watch the input fd */
81                 FD_SET(in->fd, &rfds);
82                 FD_SET(in->fd, &efds);
83                 o = outgoing;
84                 found = -1;
85                 numlines = 0;
86                 while(o) {
87                         if (o->stillgoing) {
88                                 /* Pay attention to this one */
89                                 CHECK_BLOCKING(o->chan);
90                                 FD_SET(o->chan->fd, &rfds);
91                                 FD_SET(o->chan->fd, &efds);
92                                 if (o->chan->fd > found)
93                                         found = o->chan->fd;
94                         }
95                         numlines++;
96                         o = o->next;
97                 }
98                 /* If nobody is left, just go ahead and stop */
99                 if (found<0) {
100                         if (numlines == numbusies) {
101                                 if (option_verbose > 2)
102                                         ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy at this time\n");
103                                 /* See if there is a special busy message */
104                                 if (ast_exists_extension(in, in->context, in->exten, in->priority + 101)) 
105                                         in->priority+=100;
106                         } else {
107                                 if (option_verbose > 2)
108                                         ast_verbose( VERBOSE_PREFIX_2 "No one is available to answer at this time\n");
109                         }
110                         break;
111                 }
112                 if (in->fd > found)
113                         found = in->fd;
114                 if (*to > -1) 
115                         found = select(found + 1, &rfds, NULL, &efds, &tv);
116                 else
117                         found = select(found + 1, &rfds, NULL, &efds, NULL);
118                 if (found < 0) {
119                         ast_log(LOG_WARNING, "select failed, returned %d (%s)\n", errno, strerror(errno));
120                         *to = -1;
121                         o = outgoing;
122                         while(o) {
123                                 if (o->stillgoing) {
124                                         o->chan->blocking = 0;
125                                 }
126                                 o = o->next;
127                         }
128                         return NULL;
129                 }
130                 o = outgoing;
131                 while(o) {
132                         if (o->stillgoing) {
133                                 o->chan->blocking = 0;
134                                 if (FD_ISSET(o->chan->fd, &rfds) || FD_ISSET(o->chan->fd, &efds)) {
135                                         f = ast_read(o->chan);
136                                         if (f) {
137                                                 if (f->frametype == AST_FRAME_CONTROL) {
138                                                         switch(f->subclass) {
139                                                     case AST_CONTROL_ANSWER:
140                                                                 /* This is our guy if someone answered. */
141                                                                 if (!peer) {
142                                                                         if (option_verbose > 2)
143                                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
144                                                                         peer = o->chan;
145                                                                         *allowredir = o->allowredirect;
146                                                                 }
147                                                                 break;
148                                                         case AST_CONTROL_BUSY:
149                                                                 if (option_verbose > 2)
150                                                                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
151                                                                 o->stillgoing = 0;
152                                                                 numbusies++;
153                                                                 break;
154                                                         case AST_CONTROL_RINGING:
155                                                                 if (option_verbose > 2)
156                                                                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
157                                                                 break;
158                                                         case AST_CONTROL_OFFHOOK:
159                                                                 /* Ignore going off hook */
160                                                                 break;
161                                                         default:
162                                                                 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
163                                                         }
164                                                 }
165                                                 ast_frfree(f);
166                                         } else {
167                                                 o->stillgoing = 0;
168                                         }
169                                         
170                                 }
171                         }
172                         o = o->next;
173                 }
174                 if (FD_ISSET(in->fd, &rfds) || FD_ISSET(in->fd, &efds)) {
175                         /* After unblocking the entirity of the list, check for the main channel */
176                         f = ast_read(in);
177 #if 0
178                         if (f && (f->frametype != AST_FRAME_VOICE))
179                                         printf("Frame type: %d, %d\n", f->frametype, f->subclass);
180 #endif
181                         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass = AST_CONTROL_HANGUP))) {
182                                 /* Got hung up */
183                                 *to=-1;
184                                 return NULL;
185                         }
186                 }
187                 
188         }
189         if (!(tv.tv_sec || tv.tv_usec) && (option_verbose > 2))
190                 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
191         *to = 0;
192         return peer;
193 }
194
195 static int bridge_call(struct ast_channel *chan, struct ast_channel *peer, int allowredirect)
196 {
197         /* Copy voice back and forth between the two channels.  Give the peer
198            the ability to transfer calls with '#<extension' syntax. */
199         struct ast_channel *cs[3];
200         int to = -1, len;
201         struct ast_frame *f;
202         struct ast_channel *who;
203         char newext[256], *ptr;
204         int res;
205         /* Answer if need be */
206         if (chan->state != AST_STATE_UP)
207                 if (ast_answer(chan))
208                         return -1;
209         peer->appl = "Bridged Call";
210         peer->data = chan->name;
211         cs[0] = chan;
212         cs[1] = peer;
213         for (/* ever */;;) {
214                 who = ast_waitfor_n(cs, 2, &to);
215                 if (!who) {
216                         ast_log(LOG_WARNING, "Nobody there??\n");
217                         continue;
218                 }
219                 f = ast_read(who);
220                 if (!f || ((f->frametype == AST_FRAME_CONTROL) && 
221                                         ((f->subclass == AST_CONTROL_HANGUP) ||
222                                          (f->subclass == AST_CONTROL_BUSY)))) 
223                         return -1;
224                 if ((f->frametype == AST_FRAME_VOICE) ||
225                     (f->frametype == AST_FRAME_DTMF)) {
226                         if ((f->frametype == AST_FRAME_DTMF) && (who == peer) && allowredirect) {
227                                 if (f->subclass == '#') {
228                                         memset(newext, 0, sizeof(newext));
229                                         ptr = newext;
230                                         len = ast_pbx_longest_extension(chan->context) + 1;
231
232                                         /* Transfer */
233                                         if ((res=ast_streamfile(peer, "pbx-transfer")))
234                                                 break;
235                                         if ((res=ast_waitstream(peer, AST_DIGIT_ANY)) < 0)
236                                                 break;
237                                         ast_stopstream(peer);
238                                         if (res > 0) {
239                                                 /* If they've typed a digit already, handle it */
240                                                 newext[0] = res;
241                                                 ptr++;
242                                                 len --;
243                                         }
244                                         res = ast_readstring(peer, ptr, len, 3000, 2000, "#");
245                                         if (res)
246                                                 break;
247                                         if (ast_exists_extension(chan, chan->context, newext, 1)) {
248                                                 /* Set the channel's new extension, since it exists */
249                                                 strncpy(chan->exten, newext, sizeof(chan->exten));
250                                                 chan->priority = 0;
251                                                 ast_frfree(f);
252                                                 res=0;
253                                                 break;
254                                         }
255                                         res = ast_streamfile(peer, "pbx-invalid");
256                                         if (res)
257                                                 break;
258                                         res = ast_waitstream(peer, AST_DIGIT_ANY);
259                                         ast_stopstream(peer);
260                                         res = 0;
261                                 }
262                         } else {
263 #if 0
264                                 ast_log(LOG_DEBUG, "Read from %s\n", who->name);
265 #endif
266                                 if (who == chan) 
267                                         ast_write(peer, f);
268                                 else 
269                                         ast_write(chan, f);
270                         }
271                         ast_frfree(f);
272                         
273                 } else
274                         ast_frfree(f);
275                 /* Swap who gets priority */
276                 cs[2] = cs[0];
277                 cs[0] = cs[1];
278                 cs[1] = cs[2];
279         }
280         return res;
281 }
282
283 static int dial_exec(struct ast_channel *chan, void *data)
284 {
285         int res=-1;
286         struct localuser *u;
287         char *info, *peers, *timeout, *tech, *number, *rest, *cur;
288         struct localuser *outgoing=NULL, *tmp;
289         struct ast_channel *peer, *npeer;
290         int to;
291         int allowredir=0;
292         char numsubst[AST_MAX_EXTENSION];
293         char *newnum;
294         
295         if (!data) {
296                 ast_log(LOG_WARNING, "Dial requires an argument (technology1/number1&technology2/number2...|optional timeout)\n");
297                 return -1;
298         }
299         
300         LOCAL_USER_ADD(u);
301         
302         /* Parse our arguments XXX Check for failure XXX */
303         info = malloc(strlen((char *)data) + AST_MAX_EXTENSION);
304         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION);
305         peers = strtok(info, "|");
306         if (!peers) {
307                 ast_log(LOG_WARNING, "Dial argument takes format (technology1/number1&technology2/number2...|optional timeout)\n");
308                 goto out;
309         }
310         timeout = strtok(NULL, "|");
311         rest = peers;
312         do {
313                 cur = strtok(rest, "&");
314                 /* Remember where to start next time */
315                 rest = strtok(NULL, "\128");
316                 /* Get a technology/[device:]number pair */
317                 tech = strtok(cur, "/");
318                 number = strtok(NULL, "&");
319                 if (!number) {
320                         ast_log(LOG_WARNING, "Dial argument takes format (technology1/[device:]number1&technology2/[device:]number2...|optional timeout)\n");
321                         goto out;
322                 }
323                 tmp = malloc(sizeof(struct localuser));
324                 if (!tmp) {
325                         ast_log(LOG_WARNING, "Out of memory\n");
326                         goto out;
327                 }
328                 tmp->allowredirect = 1;
329                 strncpy(numsubst, number, sizeof(numsubst));
330                 /* If we're dialing by extension, look at the extension to know what to dial */
331                 if ((newnum = strstr(numsubst, "BYEXTENSION"))) {
332                         snprintf(newnum, sizeof(numsubst) - (newnum - numsubst), "%s", chan->exten);
333                         /* By default, if we're dialing by extension, don't permit redirecting */
334                         tmp->allowredirect = 0;
335                         if (option_debug)
336                                 ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst);
337                 }
338                 /* Request the peer */
339                 tmp->chan = ast_request(tech, chan->format, numsubst);
340                 if (!tmp->chan) {
341                         /* If we can't, just go on to the next call */
342                         ast_log(LOG_WARNING, "Unable to create channel of type '%s'\n", tech);
343                         free(tmp);
344                         continue;
345                 }
346                 tmp->chan->appl = "AppDial";
347                 tmp->chan->data = "(Outgoing Line)";
348                 /* Place the call, but don't wait on the answer */
349                 res = ast_call(tmp->chan, numsubst, 0);
350                 if (res) {
351                         /* Again, keep going even if there's an error */
352                         if (option_debug)
353                                 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
354                         else if (option_verbose > 2)
355                                 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
356                         ast_hangup(tmp->chan);
357                         free(tmp);
358                         continue;
359                 } else
360                         if (option_verbose > 2)
361                                 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
362                 /* Put them in the list of outgoing thingies...  We're ready now. 
363                    XXX If we're forcibly removed, these outgoing calls won't get
364                    hung up XXX */
365                 tmp->stillgoing = -1;
366                 tmp->next = outgoing;
367                 outgoing = tmp;
368         } while(rest);
369         if (timeout)
370                 to = atoi(timeout) * 1000;
371         else
372                 to = -1;
373         peer = wait_for_answer(chan, outgoing, &to, &allowredir);
374         if (!peer) {
375                 if (to) 
376                         /* Musta gotten hung up */
377                         res = -1;
378                  else 
379                         /* Nobody answered, next please? */
380                         res=0;
381                 
382                 goto out;
383         }
384         if (peer) {
385                 /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
386                    we will always return with -1 so that it is hung up properly after the 
387                    conversation.  */
388                 hanguptree(outgoing, peer);
389                 outgoing = NULL;
390                 /* Build a translator if necessary */
391                 if (peer->format & chan->format) 
392                         npeer = peer;
393                 else
394                         npeer = ast_translator_create(peer, chan->format, AST_DIRECTION_BOTH);
395                 res = bridge_call(chan, npeer, allowredir);
396                 if (npeer != peer)
397                         ast_translator_destroy(npeer);
398                 ast_hangup(peer);
399         }       
400 out:
401         hanguptree(outgoing, NULL);
402         free(info);
403         LOCAL_USER_REMOVE(u);
404         return res;
405 }
406
407 int unload_module(void)
408 {
409         STANDARD_HANGUP_LOCALUSERS;
410         return ast_unregister_application(app);
411 }
412
413 int load_module(void)
414 {
415         return ast_register_application(app, dial_exec);
416 }
417
418 char *description(void)
419 {
420         return tdesc;
421 }
422
423 int usecount(void)
424 {
425         int res;
426         STANDARD_USECOUNT(res);
427         return res;
428 }