Version 0.1.11 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 <asterisk/say.h>
22 #include <asterisk/parking.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <sys/time.h>
30 #include <sys/signal.h>
31 #include <netinet/in.h>
32
33 #include <pthread.h>
34
35 static char *tdesc = "Dialing Application";
36
37 static char *app = "Dial";
38
39 static char *synopsis = "Place an call and connect to the current channel";
40
41 static char *descrip =
42 "  Dial(Technology/resource[&Technology2/resource2...][|timeout][|transfer]):\n"
43 "Requests  one  or more channels and places specified outgoing calls on them.\n"
44 "As soon as a  channel  answers, the  Dial  app  will  answer the originating\n"
45 "channel (if it needs to be answered) and will bridge a call with the channel\n"
46 "which first answered. All other calls placed by the Dial app will be hunp up\n"
47 "If a timeout is not specified, the Dial  application  will wait indefinitely\n"
48 "until either one of the  called channels  answers, the user hangs up, or all\n"
49 "channels return busy or  error. In general,  the dialler will return 0 if it\n"
50 "was  unable  to  place  the  call, or the timeout expired.  However, if  all\n"
51 "channels were busy, and there exists an extension with priority n+101 (where\n"
52 "n is the priority of  the  dialler  instance), then  it  will  be  the  next\n"
53 "executed extension (this allows you to setup different behavior on busy from\n"
54 "no-answer).\n"
55 "  This application returns -1 if the originating channel hangs up, or if the\n"
56 "call is bridged and  either of the parties in the bridge terminate the call.\n"
57 "The transfer string may contain  a  't' to  allow the called user transfer a\n"
58 "call or 'T' to allow the calling user to transfer the call.\n"
59 "  In addition to transferring the call, a call may be parked and then picked\n"
60 "up by another user.\n";
61
62 /* We define a customer "local user" structure because we
63    use it not only for keeping track of what is in use but
64    also for keeping track of who we're dialing. */
65
66 struct localuser {
67         struct ast_channel *chan;
68         int stillgoing;
69         int allowredirect;
70         struct localuser *next;
71 };
72
73 LOCAL_USER_DECL;
74
75 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
76 {
77         /* Hang up a tree of stuff */
78         struct localuser *oo;
79         while(outgoing) {
80                 /* Hangup any existing lines we have open */
81                 if (outgoing->chan != exception)
82                         ast_hangup(outgoing->chan);
83                 oo = outgoing;
84                 outgoing=outgoing->next;
85                 free(oo);
86         }
87 }
88
89 #define MAX 256
90
91 static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir)
92 {
93         struct localuser *o;
94         int found;
95         int numlines;
96         int sentringing = 0;
97         int numbusies = 0;
98         int orig = *to;
99         struct ast_frame *f;
100         struct ast_channel *peer = NULL;
101         struct ast_channel *watchers[MAX];
102         int pos;
103         int single;
104         struct ast_channel *winner;
105         
106         single = (outgoing && !outgoing->next);
107         
108         if (single) {
109                 /* If we are calling a single channel, make them compatible for in-band tone purpose */
110                 ast_channel_make_compatible(outgoing->chan, in);
111         }
112         
113         while(*to && !peer) {
114                 o = outgoing;
115                 found = -1;
116                 pos = 1;
117                 numlines = 0;
118                 watchers[0] = in;
119                 while(o) {
120                         /* Keep track of important channels */
121                         if (o->stillgoing) {
122                                 watchers[pos++] = o->chan;
123                                 found = 1;
124                         }
125                         o = o->next;
126                         numlines++;
127                 }
128                 if (found < 0) {
129                         if (numlines == numbusies) {
130                                 if (option_verbose > 2)
131                                         ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy at this time\n");
132                                 /* See if there is a special busy message */
133                                 if (ast_exists_extension(in, in->context, in->exten, in->priority + 101, in->callerid)) 
134                                         in->priority+=100;
135                         } else {
136                                 if (option_verbose > 2)
137                                         ast_verbose( VERBOSE_PREFIX_2 "No one is available to answer at this time\n");
138                         }
139                         *to = 0;
140                         return NULL;
141                 }
142                 winner = ast_waitfor_n(watchers, pos, to);
143                 o = outgoing;
144                 while(o) {
145                         if (o->chan == winner) {
146                                 f = ast_read(winner);
147                                 if (f) {
148                                         if (f->frametype == AST_FRAME_CONTROL) {
149                                                 switch(f->subclass) {
150                                             case AST_CONTROL_ANSWER:
151                                                         /* This is our guy if someone answered. */
152                                                         if (!peer) {
153                                                                 if (option_verbose > 2)
154                                                                         ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
155                                                                 peer = o->chan;
156                                                                 *allowredir = o->allowredirect;
157                                                         }
158                                                         break;
159                                                 case AST_CONTROL_BUSY:
160                                                         if (option_verbose > 2)
161                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
162                                                         o->stillgoing = 0;
163                                                         if (in->cdr)
164                                                                 ast_cdr_busy(in->cdr);
165                                                         numbusies++;
166                                                         break;
167                                                 case AST_CONTROL_CONGESTION:
168                                                         if (option_verbose > 2)
169                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
170                                                         o->stillgoing = 0;
171                                                         if (in->cdr)
172                                                                 ast_cdr_busy(in->cdr);
173                                                         numbusies++;
174                                                         break;
175                                                 case AST_CONTROL_RINGING:
176                                                         if (option_verbose > 2)
177                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
178                                                         if (!sentringing) {
179                                                                 ast_indicate(in, AST_CONTROL_RINGING);
180                                                                 sentringing++;
181                                                         }
182                                                         break;
183                                                 case AST_CONTROL_OFFHOOK:
184                                                         /* Ignore going off hook */
185                                                         break;
186                                                 default:
187                                                         ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
188                                                 }
189                                         } else if (single && (f->frametype == AST_FRAME_VOICE)) {
190                                                 if (ast_write(in, f)) 
191                                                         ast_log(LOG_WARNING, "Unable to forward frame\n");
192                                         } else if (single && (f->frametype == AST_FRAME_IMAGE)) {
193                                                 if (ast_write(in, f))
194                                                         ast_log(LOG_WARNING, "Unable to forward image\n");
195                                         }
196                                         ast_frfree(f);
197                                 } else {
198                                         o->stillgoing = 0;
199                                 }
200                         }
201                         o = o->next;
202                 }
203                 if (winner == in) {
204                         f = ast_read(in);
205 #if 0
206                         if (f && (f->frametype != AST_FRAME_VOICE))
207                                         printf("Frame type: %d, %d\n", f->frametype, f->subclass);
208                         else if (!f || (f->frametype != AST_FRAME_VOICE))
209                                 printf("Hangup received on %s\n", in->name);
210 #endif
211                         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
212                                 /* Got hung up */
213                                 *to=-1;
214                                 return NULL;
215                         }
216                 }
217                 if (!*to && (option_verbose > 2))
218                         ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
219         }
220         return peer;
221         
222 }
223
224 static int dial_exec(struct ast_channel *chan, void *data)
225 {
226         int res=-1;
227         struct localuser *u;
228         char *info, *peers, *timeout, *tech, *number, *rest, *cur;
229         struct localuser *outgoing=NULL, *tmp;
230         struct ast_channel *peer;
231         int to;
232         int allowredir=0;
233         char numsubst[AST_MAX_EXTENSION];
234         char restofit[AST_MAX_EXTENSION];
235         char *transfer = NULL;
236         char *newnum;
237         
238         if (!data) {
239                 ast_log(LOG_WARNING, "Dial requires an argument (technology1/number1&technology2/number2...|optional timeout)\n");
240                 return -1;
241         }
242         
243         LOCAL_USER_ADD(u);
244         
245         /* Parse our arguments XXX Check for failure XXX */
246         info = malloc(strlen((char *)data) + AST_MAX_EXTENSION);
247         if (!info) {
248                 ast_log(LOG_WARNING, "Out of memory\n");
249                 return -1;
250         }
251         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
252         peers = info;
253         if (peers) {
254                 timeout = strchr(info, '|');
255                 if (timeout) {
256                         *timeout = '\0';
257                         timeout++;
258                         transfer = strchr(timeout, '|');
259                         if (transfer) {
260                                 *transfer = '\0';
261                                 transfer++;
262                         }
263                 }
264         } else
265                 timeout = NULL;
266         if (!peers || !strlen(peers)) {
267                 ast_log(LOG_WARNING, "Dial argument takes format (technology1/number1&technology2/number2...|optional timeout)\n");
268                 goto out;
269         }
270         
271         cur = peers;
272         do {
273                 /* Remember where to start next time */
274                 rest = strchr(cur, '&');
275                 if (rest) {
276                         *rest = 0;
277                         rest++;
278                 }
279                 /* Get a technology/[device:]number pair */
280                 tech = cur;
281                 number = strchr(tech, '/');
282                 if (!number) {
283                         ast_log(LOG_WARNING, "Dial argument takes format (technology1/[device:]number1&technology2/[device:]number2...|optional timeout)\n");
284                         goto out;
285                 }
286                 *number = '\0';
287                 number++;
288                 tmp = malloc(sizeof(struct localuser));
289                 if (!tmp) {
290                         ast_log(LOG_WARNING, "Out of memory\n");
291                         goto out;
292                 }
293                 if (transfer && (strchr(transfer, 't')))
294                         tmp->allowredirect = 1;
295                 else
296                         tmp->allowredirect = 0;
297                 strncpy(numsubst, number, sizeof(numsubst)-1);
298                 /* If we're dialing by extension, look at the extension to know what to dial */
299                 if ((newnum = strstr(numsubst, "BYEXTENSION"))) {
300                         strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
301                         snprintf(newnum, sizeof(numsubst) - (newnum - numsubst), "%s%s", chan->exten,restofit);
302                         if (option_debug)
303                                 ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst);
304                 }
305                 /* Request the peer */
306                 tmp->chan = ast_request(tech, chan->nativeformats, numsubst);
307                 if (!tmp->chan) {
308                         /* If we can't, just go on to the next call */
309                         ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", tech);
310                         if (chan->cdr)
311                                 ast_cdr_busy(chan->cdr);
312                         free(tmp);
313                         cur = rest;
314                         continue;
315                 }
316                 if (strlen(tmp->chan->call_forward)) {
317                         if (option_verbose > 2)
318                                 ast_verbose(VERBOSE_PREFIX_3 "Forwarding call to '%s@%s'\n", tmp->chan->call_forward, tmp->chan->context);
319                         /* Setup parameters */
320                         strncpy(chan->exten, tmp->chan->call_forward, sizeof(chan->exten));
321                         strncpy(chan->context, tmp->chan->context, sizeof(chan->context));
322                         chan->priority = 0;
323                         to = 0;
324                         ast_hangup(tmp->chan);
325                         free(tmp);
326                         cur = rest;
327                         break;
328                 }
329                 tmp->chan->appl = "AppDial";
330                 tmp->chan->data = "(Outgoing Line)";
331                 tmp->chan->whentohangup = 0;
332                 if (tmp->chan->callerid)
333                         free(tmp->chan->callerid);
334                 if (tmp->chan->ani)
335                         free(tmp->chan->ani);
336                 if (chan->callerid)
337                         tmp->chan->callerid = strdup(chan->callerid);
338                 else
339                         tmp->chan->callerid = NULL;
340                 if (chan->ani)
341                         tmp->chan->ani = strdup(chan->ani);
342                 else
343                         tmp->chan->ani = NULL;
344                 /* Presense of ADSI CPE on outgoing channel follows ours */
345                 tmp->chan->adsicpe = chan->adsicpe;
346                 /* Place the call, but don't wait on the answer */
347                 res = ast_call(tmp->chan, numsubst, 0);
348                 if (res) {
349                         /* Again, keep going even if there's an error */
350                         if (option_debug)
351                                 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
352                         else if (option_verbose > 2)
353                                 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
354                         ast_hangup(tmp->chan);
355                         free(tmp);
356                         cur = rest;
357                         continue;
358                 } else
359                         if (option_verbose > 2)
360                                 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
361                 /* Put them in the list of outgoing thingies...  We're ready now. 
362                    XXX If we're forcibly removed, these outgoing calls won't get
363                    hung up XXX */
364                 tmp->stillgoing = -1;
365                 tmp->next = outgoing;
366                 outgoing = tmp;
367                 cur = rest;
368         } while(cur);
369         
370         if (timeout && strlen(timeout))
371                 to = atoi(timeout) * 1000;
372         else
373                 to = -1;
374         peer = wait_for_answer(chan, outgoing, &to, &allowredir);
375         if (!peer) {
376                 if (to) 
377                         /* Musta gotten hung up */
378                         res = -1;
379                  else 
380                         /* Nobody answered, next please? */
381                         res=0;
382                 
383                 goto out;
384         }
385         if (peer) {
386                 /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
387                    we will always return with -1 so that it is hung up properly after the 
388                    conversation.  */
389                 hanguptree(outgoing, peer);
390                 outgoing = NULL;
391                 /* If appropriate, log that we have a destination channel */
392                 if (chan->cdr)
393                         ast_cdr_setdestchan(chan->cdr, peer->name);
394                 /* Make sure channels are compatible */
395                 res = ast_channel_make_compatible(chan, peer);
396                 if (res < 0) {
397                         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", chan->name, peer->name);
398                         ast_hangup(peer);
399                         return -1;
400                 }
401                 if (!strcmp(chan->type,"Zap")) {
402                         int x = 2;
403                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
404                 }                       
405                 if (!strcmp(peer->type,"Zap")) {
406                         int x = 2;
407                         ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
408                 }                       
409                 res = ast_bridge_call(chan, peer, allowredir);
410                 ast_hangup(peer);
411         }       
412 out:
413         hanguptree(outgoing, NULL);
414         free(info);
415         LOCAL_USER_REMOVE(u);
416         return res;
417 }
418
419 int unload_module(void)
420 {
421         STANDARD_HANGUP_LOCALUSERS;
422         return ast_unregister_application(app);
423 }
424
425 int load_module(void)
426 {
427         int res;
428         res = ast_register_application(app, dial_exec, synopsis, descrip);
429         return res;
430 }
431
432 char *description(void)
433 {
434         return tdesc;
435 }
436
437 int usecount(void)
438 {
439         int res;
440         STANDARD_USECOUNT(res);
441         return res;
442 }
443
444 char *key()
445 {
446         return ASTERISK_GPL_KEY;
447 }