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