Mon Mar 10 07:00:00 CET 2003
[asterisk/asterisk.git] / apps / app_dial.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Trivial application to dial a channel and send an URL on answer
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/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/options.h>
20 #include <asterisk/module.h>
21 #include <asterisk/translate.h>
22 #include <asterisk/say.h>
23 #include <asterisk/parking.h>
24 #include <asterisk/musiconhold.h>
25 #include <asterisk/callerid.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <sys/time.h>
33 #include <sys/signal.h>
34 #include <netinet/in.h>
35
36 #include <pthread.h>
37
38 static char *tdesc = "Dialing Application";
39
40 static char *app = "Dial";
41
42 static char *synopsis = "Place an call and connect to the current channel";
43
44 static char *descrip =
45 "  Dial(Technology/resource[&Technology2/resource2...][|timeout][|options][|URL]):\n"
46 "Requests  one  or more channels and places specified outgoing calls on them.\n"
47 "As soon as a  channel  answers, the  Dial  app  will  answer the originating\n"
48 "channel (if it needs to be answered) and will bridge a call with the channel\n"
49 "which first answered. All other calls placed by the Dial app will be hunp up\n"
50 "If a timeout is not specified, the Dial  application  will wait indefinitely\n"
51 "until either one of the  called channels  answers, the user hangs up, or all\n"
52 "channels return busy or  error. In general,  the dialler will return 0 if it\n"
53 "was  unable  to  place  the  call, or the timeout expired.  However, if  all\n"
54 "channels were busy, and there exists an extension with priority n+101 (where\n"
55 "n is the priority of  the  dialler  instance), then  it  will  be  the  next\n"
56 "executed extension (this allows you to setup different behavior on busy from\n"
57 "no-answer).\n"
58 "  This application returns -1 if the originating channel hangs up, or if the\n"
59 "call is bridged and  either of the parties in the bridge terminate the call.\n"
60 "The option string may contain zero or more of the following characters:\n"
61 "      't' -- allow the called user transfer the calling user\n"
62 "      'T' -- to allow the calling user to transfer the call.\n"
63 "      'r' -- indicate ringing to the calling party, pass no audio until answered.\n"
64 "      'm' -- provide hold music to the calling party until answered.\n"
65 "      'd' -- data-quality (modem) call (minimum delay).\n"
66 "      'H' -- allow caller to hang up by hitting *.\n"
67 "      'C' -- reset call detail record for this call.\n"
68 "      'P[(x)]' -- privacy mode, using 'x' as database if provided.\n"
69 "  In addition to transferring the call, a call may be parked and then picked\n"
70 "up by another user.\n"
71 "  The optionnal URL will be sent to the called party if the channel supports\n"
72 "it.\n";
73
74 /* We define a customer "local user" structure because we
75    use it not only for keeping track of what is in use but
76    also for keeping track of who we're dialing. */
77
78 struct localuser {
79         struct ast_channel *chan;
80         int stillgoing;
81         int allowredirect;
82         int ringbackonly;
83         int musiconhold;
84         int dataquality;
85         int allowdisconnect;
86         struct localuser *next;
87 };
88
89 LOCAL_USER_DECL;
90
91 static void hanguptree(struct localuser *outgoing, struct ast_channel *exception)
92 {
93         /* Hang up a tree of stuff */
94         struct localuser *oo;
95         while(outgoing) {
96                 /* Hangup any existing lines we have open */
97                 if (outgoing->chan != exception)
98                         ast_hangup(outgoing->chan);
99                 oo = outgoing;
100                 outgoing=outgoing->next;
101                 free(oo);
102         }
103 }
104
105 #define MAX 256
106
107 static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir, int *allowdisconnect)
108 {
109         struct localuser *o;
110         int found;
111         int numlines;
112         int sentringing = 0;
113         int numbusies = 0;
114         int orig = *to;
115         struct ast_frame *f;
116         struct ast_channel *peer = NULL;
117         struct ast_channel *watchers[MAX];
118         int pos;
119         int single;
120         int moh=0;
121         int ringind=0;
122         struct ast_channel *winner;
123         
124         single = (outgoing && !outgoing->next && !outgoing->musiconhold && !outgoing->ringbackonly);
125         
126         if (single) {
127                 /* If we are calling a single channel, make them compatible for in-band tone purpose */
128                 ast_channel_make_compatible(outgoing->chan, in);
129         }
130         
131         if (outgoing) {
132                 moh = outgoing->musiconhold;
133                 ringind = outgoing->ringbackonly;
134                 if (outgoing->musiconhold) {
135                         ast_moh_start(in, NULL);
136                 } else if (outgoing->ringbackonly) {
137                         ast_indicate(in, AST_CONTROL_RINGING);
138                 }
139         }
140         
141         while(*to && !peer) {
142                 o = outgoing;
143                 found = -1;
144                 pos = 1;
145                 numlines = 0;
146                 watchers[0] = in;
147                 while(o) {
148                         /* Keep track of important channels */
149                         if (o->stillgoing) {
150                                 watchers[pos++] = o->chan;
151                                 found = 1;
152                         }
153                         o = o->next;
154                         numlines++;
155                 }
156                 if (found < 0) {
157                         if (numlines == numbusies) {
158                                 if (option_verbose > 2)
159                                         ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy at this time\n");
160                                 /* See if there is a special busy message */
161                                 if (ast_exists_extension(in, in->context, in->exten, in->priority + 101, in->callerid)) 
162                                         in->priority+=100;
163                         } else {
164                                 if (option_verbose > 2)
165                                         ast_verbose( VERBOSE_PREFIX_2 "No one is available to answer at this time\n");
166                         }
167                         *to = 0;
168                         /* if no one available we'd better stop MOH/ringing to */
169                         if (moh) {
170                                 ast_moh_stop(in);
171                         } else if (ringind) {
172                                 ast_indicate(in, -1);
173                         }
174                         return NULL;
175                 }
176                 winner = ast_waitfor_n(watchers, pos, to);
177                 o = outgoing;
178                 while(o) {
179                         if (o->stillgoing && (o->chan->_state == AST_STATE_UP)) {
180                                 if (!peer) {
181                                         if (option_verbose > 2)
182                                                 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
183                                         peer = o->chan;
184                                         *allowredir = o->allowredirect;
185                                         *allowdisconnect = o->allowdisconnect;
186                                 }
187                         } else if (o->chan == winner) {
188                                 f = ast_read(winner);
189                                 if (f) {
190                                         if (f->frametype == AST_FRAME_CONTROL) {
191                                                 switch(f->subclass) {
192                                             case AST_CONTROL_ANSWER:
193                                                         /* This is our guy if someone answered. */
194                                                         if (!peer) {
195                                                                 if (option_verbose > 2)
196                                                                         ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
197                                                                 peer = o->chan;
198                                                                 *allowredir = o->allowredirect;
199                                                                 *allowdisconnect = o->allowdisconnect;
200                                                         }
201                                                         break;
202                                                 case AST_CONTROL_BUSY:
203                                                         if (option_verbose > 2)
204                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
205                                                         o->stillgoing = 0;
206                                                         if (in->cdr)
207                                                                 ast_cdr_busy(in->cdr);
208                                                         numbusies++;
209                                                         break;
210                                                 case AST_CONTROL_CONGESTION:
211                                                         if (option_verbose > 2)
212                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
213                                                         o->stillgoing = 0;
214                                                         if (in->cdr)
215                                                                 ast_cdr_busy(in->cdr);
216                                                         numbusies++;
217                                                         break;
218                                                 case AST_CONTROL_RINGING:
219                                                         if (option_verbose > 2)
220                                                                 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
221                                                         if (!sentringing) {
222                                                                 ast_indicate(in, AST_CONTROL_RINGING);
223                                                                 sentringing++;
224                                                                 ringind++;
225                                                         }
226                                                         break;
227                                                 case AST_CONTROL_OFFHOOK:
228                                                         /* Ignore going off hook */
229                                                         break;
230                                                 default:
231                                                         ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
232                                                 }
233                                         } else if (single && (f->frametype == AST_FRAME_VOICE) && 
234                                                                 !(outgoing->ringbackonly || outgoing->musiconhold)) {
235                                                 if (ast_write(in, f)) 
236                                                         ast_log(LOG_WARNING, "Unable to forward frame\n");
237                                         } else if (single && (f->frametype == AST_FRAME_IMAGE) && 
238                                                                 !(outgoing->ringbackonly || outgoing->musiconhold)) {
239                                                 if (ast_write(in, f))
240                                                         ast_log(LOG_WARNING, "Unable to forward image\n");
241                                         }
242                                         ast_frfree(f);
243                                 } else {
244                                         o->stillgoing = 0;
245                                 }
246                         }
247                         o = o->next;
248                 }
249                 if (winner == in) {
250                         f = ast_read(in);
251 #if 0
252                         if (f && (f->frametype != AST_FRAME_VOICE))
253                                         printf("Frame type: %d, %d\n", f->frametype, f->subclass);
254                         else if (!f || (f->frametype != AST_FRAME_VOICE))
255                                 printf("Hangup received on %s\n", in->name);
256 #endif
257                         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
258                                 /* Got hung up */
259                                 *to=-1;
260                                 return NULL;
261                         }
262                         if (f && (f->frametype == AST_FRAME_DTMF) && allowdisconnect &&
263                                 (f->subclass == '*')) {
264                             if (option_verbose > 3)
265                                 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
266                                 *to=0;
267                                 return NULL;
268                         }
269                         if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF)))  {
270                                 if (ast_write(outgoing->chan, f))
271                                         ast_log(LOG_WARNING, "Unable to forward voice\n");
272                         }
273                 }
274                 if (!*to && (option_verbose > 2))
275                         ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
276         }
277         if (moh) {
278                 ast_moh_stop(in);
279         } else if (ringind) {
280                 ast_indicate(in, -1);
281         }
282
283         return peer;
284         
285 }
286
287 static int dial_exec(struct ast_channel *chan, void *data)
288 {
289         int res=-1;
290         struct localuser *u;
291         char info[256], *peers, *timeout, *tech, *number, *rest, *cur;
292         char  privdb[256] = "", *s;
293         struct localuser *outgoing=NULL, *tmp;
294         struct ast_channel *peer;
295         int to;
296         int allowredir=0;
297         int allowdisconnect=0;
298         int privacy=0;
299         int resetcdr=0;
300         char numsubst[AST_MAX_EXTENSION];
301         char restofit[AST_MAX_EXTENSION];
302         char *transfer = NULL;
303         char *newnum;
304         char callerid[256], *l, *n;
305         char *url=NULL; /* JDG */
306         struct ast_var_t *current;
307         struct varshead *headp, *newheadp;
308         struct ast_var_t *newvar;
309         
310         if (!data) {
311                 ast_log(LOG_WARNING, "Dial requires an argument (technology1/number1&technology2/number2...|optional timeout)\n");
312                 return -1;
313         }
314         
315         LOCAL_USER_ADD(u);
316         
317         /* Parse our arguments XXX Check for failure XXX */
318         strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
319         peers = info;
320         if (peers) {
321                 timeout = strchr(info, '|');
322                 if (timeout) {
323                         *timeout = '\0';
324                         timeout++;
325                         transfer = strchr(timeout, '|');
326                         if (transfer) {
327                                 *transfer = '\0';
328                                 transfer++;
329                                 /* JDG */
330                                 url = strchr(transfer, '|');
331                                 if (url) {
332                                         *url = '\0';
333                                         url++;
334                                         ast_log(LOG_DEBUG, "DIAL WITH URL=%s_\n", url);
335                                 } else 
336                                         ast_log(LOG_DEBUG, "SIMPLE DIAL (NO URL)\n");
337                                 /* /JDG */
338                         }
339                 }
340         } else
341                 timeout = NULL;
342         if (!peers || !strlen(peers)) {
343                 ast_log(LOG_WARNING, "Dial argument takes format (technology1/number1&technology2/number2...|optional timeout)\n");
344                 goto out;
345         }
346         
347
348         if (transfer) {
349                 /* Extract privacy info from transfer */
350                 if ((s = strstr(transfer, "P("))) {
351                         privacy = 1;
352                         strncpy(privdb, s + 2, sizeof(privdb) - 1);
353                         /* Overwrite with X's what was the privacy info */
354                         while(*s && (*s != ')')) 
355                                 *(s++) = 'X';
356                         if (*s)
357                                 *s = 'X';
358                         /* Now find the end of the privdb */
359                         s = strchr(privdb, ')');
360                         if (s)
361                                 *s = '\0';
362                         else {
363                                 ast_log(LOG_WARNING, "Transfer with privacy lacking trailing '('\n");
364                                 privacy = 0;
365                         }
366                 } else if (strchr(transfer, 'P')) {
367                         /* No specified privdb */
368                         privacy = 1;
369                 } else if (strchr(transfer, 'C')) {
370                         resetcdr = 1;
371                 }
372         }
373         if (resetcdr && chan->cdr)
374                 ast_cdr_reset(chan->cdr, 0);
375         if (!strlen(privdb) && privacy) {
376                 /* If privdb is not specified and we are using privacy, copy from extension */
377                 strncpy(privdb, chan->exten, sizeof(privdb) - 1);
378         }
379         if (privacy) {
380                 if (chan->callerid)
381                         strncpy(callerid, chan->callerid, sizeof(callerid));
382                 else
383                         strcpy(callerid, "");
384                 ast_callerid_parse(callerid, &n, &l);
385                 if (l) {
386                         ast_shrink_phone_number(l);
387                 } else
388                         l = "";
389                 ast_log(LOG_NOTICE, "Privacy DB is '%s', privacy is %d, clid is '%s'\n", privdb, privacy, l);
390         }
391         cur = peers;
392         do {
393                 /* Remember where to start next time */
394                 rest = strchr(cur, '&');
395                 if (rest) {
396                         *rest = 0;
397                         rest++;
398                 }
399                 /* Get a technology/[device:]number pair */
400                 tech = cur;
401                 number = strchr(tech, '/');
402                 if (!number) {
403                         ast_log(LOG_WARNING, "Dial argument takes format (technology1/[device:]number1&technology2/[device:]number2...|optional timeout)\n");
404                         goto out;
405                 }
406                 *number = '\0';
407                 number++;
408                 tmp = malloc(sizeof(struct localuser));
409                 if (!tmp) {
410                         ast_log(LOG_WARNING, "Out of memory\n");
411                         goto out;
412                 }
413                 memset(tmp, 0, sizeof(struct localuser));
414                 if (transfer) {
415                         if (strchr(transfer, 't'))
416                                 tmp->allowredirect = 1;
417                         else    tmp->allowredirect = 0;
418                         if (strchr(transfer, 'r'))
419                                 tmp->ringbackonly = 1;
420                         else    tmp->ringbackonly = 0;
421                         if (strchr(transfer, 'm'))
422                                 tmp->musiconhold = 1;
423                         else    tmp->musiconhold = 0;
424                         if (strchr(transfer, 'd'))
425                                 tmp->dataquality = 1;
426                         else    tmp->dataquality = 0;
427                         if (strchr(transfer, 'H'))
428                                 tmp->allowdisconnect = 1;
429                         else    tmp->allowdisconnect = 0;
430                 }
431                 strncpy(numsubst, number, sizeof(numsubst)-1);
432                 /* If we're dialing by extension, look at the extension to know what to dial */
433                 if ((newnum = strstr(numsubst, "BYEXTENSION"))) {
434                         strncpy(restofit, newnum + strlen("BYEXTENSION"), sizeof(restofit)-1);
435                         snprintf(newnum, sizeof(numsubst) - (newnum - numsubst), "%s%s", chan->exten,restofit);
436                         if (option_debug)
437                                 ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst);
438                 }
439                 /* Request the peer */
440                 tmp->chan = ast_request(tech, chan->nativeformats, numsubst);
441                 if (!tmp->chan) {
442                         /* If we can't, just go on to the next call */
443                         ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", tech);
444                         if (chan->cdr)
445                                 ast_cdr_busy(chan->cdr);
446                         free(tmp);
447                         cur = rest;
448                         continue;
449                 }
450                 if (strlen(tmp->chan->call_forward)) {
451                         if (option_verbose > 2)
452                                 ast_verbose(VERBOSE_PREFIX_3 "Forwarding call to '%s@%s'\n", tmp->chan->call_forward, tmp->chan->context);
453                         /* Setup parameters */
454                         strncpy(chan->exten, tmp->chan->call_forward, sizeof(chan->exten));
455                         strncpy(chan->context, tmp->chan->context, sizeof(chan->context));
456                         chan->priority = 0;
457                         to = 0;
458                         ast_hangup(tmp->chan);
459                         free(tmp);
460                         cur = rest;
461                         break;
462                 }
463                 /* If creating a SIP channel, look for a variable called */
464                 /* VXML_URL in the calling channel and copy it to the    */
465                 /* new channel.                                          */
466                 if (strcasecmp(tech,"SIP")==0)
467                 {
468                         headp=&chan->varshead;
469                         AST_LIST_TRAVERSE(headp,current,entries) {
470                                 if (strcasecmp(ast_var_name(current),"VXML_URL")==0)
471                                 {
472                                         newvar=ast_var_assign(ast_var_name(current),ast_var_value(current));
473                                         newheadp=&tmp->chan->varshead;
474                                         AST_LIST_INSERT_HEAD(newheadp,newvar,entries);
475                                         break;
476                                 }
477                         }
478                 }
479                 
480                 tmp->chan->appl = "AppDial";
481                 tmp->chan->data = "(Outgoing Line)";
482                 tmp->chan->whentohangup = 0;
483                 if (tmp->chan->callerid)
484                         free(tmp->chan->callerid);
485                 if (tmp->chan->ani)
486                         free(tmp->chan->ani);
487                 if (chan->callerid)
488                         tmp->chan->callerid = strdup(chan->callerid);
489                 else
490                         tmp->chan->callerid = NULL;
491                 if (chan->ani)
492                         tmp->chan->ani = strdup(chan->ani);
493                 else
494                         tmp->chan->ani = NULL;
495                 /* Presense of ADSI CPE on outgoing channel follows ours */
496                 tmp->chan->adsicpe = chan->adsicpe;
497                 /* Place the call, but don't wait on the answer */
498                 res = ast_call(tmp->chan, numsubst, 0);
499                 if (res) {
500                         /* Again, keep going even if there's an error */
501                         if (option_debug)
502                                 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
503                         else if (option_verbose > 2)
504                                 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
505                         ast_hangup(tmp->chan);
506                         free(tmp);
507                         cur = rest;
508                         continue;
509                 } else
510                         if (option_verbose > 2)
511                                 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
512                 /* Put them in the list of outgoing thingies...  We're ready now. 
513                    XXX If we're forcibly removed, these outgoing calls won't get
514                    hung up XXX */
515                 tmp->stillgoing = -1;
516                 tmp->next = outgoing;
517                 outgoing = tmp;
518                 /* If this line is up, don't try anybody else */
519                 if (outgoing->chan->_state == AST_STATE_UP)
520                         break;
521                 cur = rest;
522         } while(cur);
523         
524         if (timeout && strlen(timeout))
525                 to = atoi(timeout) * 1000;
526         else
527                 to = -1;
528         peer = wait_for_answer(chan, outgoing, &to, &allowredir, &allowdisconnect);
529         if (!peer) {
530                 if (to) 
531                         /* Musta gotten hung up */
532                         res = -1;
533                  else 
534                         /* Nobody answered, next please? */
535                         res=0;
536                 
537                 goto out;
538         }
539         if (peer) {
540                 /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
541                    we will always return with -1 so that it is hung up properly after the 
542                    conversation.  */
543                 if (!strcmp(chan->type,"Zap"))
544                 {
545                         int x = 2;
546                         if (tmp->dataquality) x = 0;
547                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
548                 }                       
549                 if (!strcmp(peer->type,"Zap"))
550                 {
551                         int x = 2;
552                         if (tmp->dataquality) x = 0;
553                         ast_channel_setoption(peer,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
554                 }                       
555                 hanguptree(outgoing, peer);
556                 outgoing = NULL;
557                 /* If appropriate, log that we have a destination channel */
558                 if (chan->cdr)
559                         ast_cdr_setdestchan(chan->cdr, peer->name);
560                 /* Make sure channels are compatible */
561                 res = ast_channel_make_compatible(chan, peer);
562                 if (res < 0) {
563                         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", chan->name, peer->name);
564                         ast_hangup(peer);
565                         return -1;
566                 }
567                 /* JDG: sendurl */
568                 if( url && strlen(url) && ast_channel_supports_html(peer) ) {
569                         ast_log(LOG_DEBUG, "app_dial: sendurl=%s.\n", url);
570                         ast_channel_sendurl( peer, url );
571                 } /* /JDG */
572                 res = ast_bridge_call(chan, peer, allowredir, allowdisconnect);
573                 ast_hangup(peer);
574         }       
575 out:
576         hanguptree(outgoing, NULL);
577         LOCAL_USER_REMOVE(u);
578         return res;
579 }
580
581 int unload_module(void)
582 {
583         STANDARD_HANGUP_LOCALUSERS;
584         return ast_unregister_application(app);
585 }
586
587 int load_module(void)
588 {
589         int res;
590         res = ast_register_application(app, dial_exec, synopsis, descrip);
591         return res;
592 }
593
594 char *description(void)
595 {
596         return tdesc;
597 }
598
599 int usecount(void)
600 {
601         int res;
602         STANDARD_USECOUNT(res);
603         return res;
604 }
605
606 char *key()
607 {
608         return ASTERISK_GPL_KEY;
609 }