00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "asterisk.h"
00027
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 61774 $")
00029
00030 #include <stdio.h>
00031 #include <stdlib.h>
00032 #include <string.h>
00033 #include <sys/time.h>
00034 #include <signal.h>
00035 #include <errno.h>
00036 #include <unistd.h>
00037
00038 #include "asterisk/logger.h"
00039 #include "asterisk/channel.h"
00040 #include "asterisk/options.h"
00041 #include "asterisk/utils.h"
00042 #include "asterisk/lock.h"
00043 #include "asterisk/linkedlists.h"
00044 #include "asterisk/dial.h"
00045 #include "asterisk/pbx.h"
00046
00047
00048 struct ast_dial {
00049 int num;
00050 enum ast_dial_result state;
00051 void *options[AST_DIAL_OPTION_MAX];
00052 ast_dial_state_callback state_callback;
00053 AST_LIST_HEAD_NOLOCK(, ast_dial_channel) channels;
00054 pthread_t thread;
00055 };
00056
00057
00058 struct ast_dial_channel {
00059 int num;
00060 const char *tech;
00061 const char *device;
00062 void *options[AST_DIAL_OPTION_MAX];
00063 int cause;
00064 struct ast_channel *owner;
00065 AST_LIST_ENTRY(ast_dial_channel) list;
00066 };
00067
00068
00069 typedef void *(*ast_dial_option_cb_enable)(void *data);
00070
00071
00072 typedef int (*ast_dial_option_cb_disable)(void *data);
00073
00074
00075 struct answer_exec_struct {
00076 char app[AST_MAX_APP];
00077 char *args;
00078 };
00079
00080
00081 static void *answer_exec_enable(void *data)
00082 {
00083 struct answer_exec_struct *answer_exec = NULL;
00084 char *app = ast_strdupa((char*)data), *args = NULL;
00085
00086
00087 if (ast_strlen_zero(app))
00088 return NULL;
00089
00090
00091 if (!(answer_exec = ast_calloc(1, sizeof(*answer_exec))))
00092 return NULL;
00093
00094
00095 if ((args = strchr(app, '|'))) {
00096 *args++ = '\0';
00097 answer_exec->args = ast_strdup(args);
00098 }
00099
00100
00101 ast_copy_string(answer_exec->app, app, sizeof(answer_exec->app));
00102
00103 return answer_exec;
00104 }
00105
00106
00107 static int answer_exec_disable(void *data)
00108 {
00109 struct answer_exec_struct *answer_exec = data;
00110
00111
00112 if (!answer_exec)
00113 return -1;
00114
00115
00116 if (answer_exec->args)
00117 free(answer_exec->args);
00118
00119
00120 free(answer_exec);
00121
00122 return 0;
00123 }
00124
00125
00126 static void answer_exec_run(struct ast_channel *chan, char *app, char *args)
00127 {
00128 struct ast_app *ast_app = pbx_findapp(app);
00129
00130
00131 if (!ast_app)
00132 return;
00133
00134
00135 pbx_exec(chan, ast_app, args);
00136
00137 return;
00138 }
00139
00140
00141 static const struct ast_option_types {
00142 enum ast_dial_option option;
00143 ast_dial_option_cb_enable enable;
00144 ast_dial_option_cb_disable disable;
00145 } option_types[] = {
00146 { AST_DIAL_OPTION_RINGING, NULL, NULL },
00147 { AST_DIAL_OPTION_ANSWER_EXEC, answer_exec_enable, answer_exec_disable },
00148 { AST_DIAL_OPTION_MAX, NULL, NULL },
00149 };
00150
00151
00152 #define S_REPLACE(s, new_val) \
00153 do { \
00154 if (s) \
00155 free(s); \
00156 s = (new_val); \
00157 } while (0)
00158
00159
00160 #define AST_MAX_WATCHERS 256
00161
00162
00163 #define FIND_RELATIVE_OPTION(dial, dial_channel, ast_dial_option) (dial_channel->options[ast_dial_option] ? dial_channel->options[ast_dial_option] : dial->options[ast_dial_option])
00164
00165
00166 #define IS_CALLER(chan, owner) (chan == owner ? 1 : 0)
00167
00168
00169
00170
00171
00172 struct ast_dial *ast_dial_create(void)
00173 {
00174 struct ast_dial *dial = NULL;
00175
00176
00177 if (!(dial = ast_calloc(1, sizeof(*dial))))
00178 return NULL;
00179
00180
00181 AST_LIST_HEAD_INIT_NOLOCK(&dial->channels);
00182
00183
00184 dial->thread = AST_PTHREADT_NULL;
00185
00186 return dial;
00187 }
00188
00189
00190
00191
00192
00193 int ast_dial_append(struct ast_dial *dial, const char *tech, const char *device)
00194 {
00195 struct ast_dial_channel *channel = NULL;
00196
00197
00198 if (!dial || !tech || !device)
00199 return -1;
00200
00201
00202 if (!(channel = ast_calloc(1, sizeof(*channel))))
00203 return -1;
00204
00205
00206 channel->tech = tech;
00207 channel->device = device;
00208
00209
00210 channel->num = ast_atomic_fetchadd_int(&dial->num, +1);
00211
00212
00213 AST_LIST_INSERT_TAIL(&dial->channels, channel, list);
00214
00215 return channel->num;
00216 }
00217
00218
00219 static int begin_dial(struct ast_dial *dial, struct ast_channel *chan)
00220 {
00221 struct ast_dial_channel *channel = NULL;
00222 int success = 0, res = 0;
00223
00224
00225 AST_LIST_TRAVERSE(&dial->channels, channel, list) {
00226 char numsubst[AST_MAX_EXTENSION];
00227
00228
00229 ast_copy_string(numsubst, channel->device, sizeof(numsubst));
00230
00231
00232 if (!(channel->owner = ast_request(channel->tech,
00233 chan ? chan->nativeformats : AST_FORMAT_AUDIO_MASK, numsubst, &channel->cause))) {
00234 continue;
00235 }
00236
00237 channel->owner->appl = "AppDial2";
00238 channel->owner->data = "(Outgoing Line)";
00239 channel->owner->whentohangup = 0;
00240
00241
00242 if (chan) {
00243 ast_channel_inherit_variables(chan, channel->owner);
00244
00245
00246 S_REPLACE(channel->owner->cid.cid_num, ast_strdup(chan->cid.cid_num));
00247 S_REPLACE(channel->owner->cid.cid_name, ast_strdup(chan->cid.cid_name));
00248 S_REPLACE(channel->owner->cid.cid_ani, ast_strdup(chan->cid.cid_ani));
00249 S_REPLACE(channel->owner->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis));
00250
00251 ast_string_field_set(channel->owner, language, chan->language);
00252 ast_string_field_set(channel->owner, accountcode, chan->accountcode);
00253 channel->owner->cdrflags = chan->cdrflags;
00254 if (ast_strlen_zero(channel->owner->musicclass))
00255 ast_string_field_set(channel->owner, musicclass, chan->musicclass);
00256
00257 channel->owner->cid.cid_pres = chan->cid.cid_pres;
00258 channel->owner->cid.cid_ton = chan->cid.cid_ton;
00259 channel->owner->cid.cid_tns = chan->cid.cid_tns;
00260 channel->owner->adsicpe = chan->adsicpe;
00261 channel->owner->transfercapability = chan->transfercapability;
00262 }
00263
00264
00265 if ((res = ast_call(channel->owner, numsubst, 0))) {
00266 ast_hangup(channel->owner);
00267 channel->owner = NULL;
00268 } else {
00269 success++;
00270 if (option_verbose > 2)
00271 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst);
00272 }
00273 }
00274
00275
00276 return success;
00277 }
00278
00279
00280 static struct ast_dial_channel *find_relative_dial_channel(struct ast_dial *dial, struct ast_channel *owner)
00281 {
00282 struct ast_dial_channel *channel = NULL;
00283
00284 AST_LIST_TRAVERSE(&dial->channels, channel, list) {
00285 if (channel->owner == owner)
00286 break;
00287 }
00288
00289 return channel;
00290 }
00291
00292 static void set_state(struct ast_dial *dial, enum ast_dial_result state)
00293 {
00294 dial->state = state;
00295
00296 if (dial->state_callback)
00297 dial->state_callback(dial);
00298 }
00299
00300
00301 static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel, struct ast_frame *fr, struct ast_channel *chan)
00302 {
00303 if (fr->frametype == AST_FRAME_CONTROL) {
00304 switch (fr->subclass) {
00305 case AST_CONTROL_ANSWER:
00306 if (option_verbose > 2)
00307 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", channel->owner->name, chan->name);
00308 AST_LIST_REMOVE(&dial->channels, channel, list);
00309 AST_LIST_INSERT_HEAD(&dial->channels, channel, list);
00310 set_state(dial, AST_DIAL_RESULT_ANSWERED);
00311 break;
00312 case AST_CONTROL_BUSY:
00313 if (option_verbose > 2)
00314 ast_verbose(VERBOSE_PREFIX_3 "%s is busy\n", channel->owner->name);
00315 ast_hangup(channel->owner);
00316 channel->owner = NULL;
00317 break;
00318 case AST_CONTROL_CONGESTION:
00319 if (option_verbose > 2)
00320 ast_verbose(VERBOSE_PREFIX_3 "%s is circuit-busy\n", channel->owner->name);
00321 ast_hangup(channel->owner);
00322 channel->owner = NULL;
00323 break;
00324 case AST_CONTROL_RINGING:
00325 if (option_verbose > 2)
00326 ast_verbose(VERBOSE_PREFIX_3 "%s is ringing\n", channel->owner->name);
00327 ast_indicate(chan, AST_CONTROL_RINGING);
00328 set_state(dial, AST_DIAL_RESULT_RINGING);
00329 break;
00330 case AST_CONTROL_PROGRESS:
00331 if (option_verbose > 2)
00332 ast_verbose (VERBOSE_PREFIX_3 "%s is making progress, passing it to %s\n", channel->owner->name, chan->name);
00333 ast_indicate(chan, AST_CONTROL_PROGRESS);
00334 set_state(dial, AST_DIAL_RESULT_PROGRESS);
00335 break;
00336 case AST_CONTROL_VIDUPDATE:
00337 if (option_verbose > 2)
00338 ast_verbose (VERBOSE_PREFIX_3 "%s requested a video update, passing it to %s\n", channel->owner->name, chan->name);
00339 ast_indicate(chan, AST_CONTROL_VIDUPDATE);
00340 break;
00341 case AST_CONTROL_PROCEEDING:
00342 if (option_verbose > 2)
00343 ast_verbose (VERBOSE_PREFIX_3 "%s is proceeding, passing it to %s\n", channel->owner->name, chan->name);
00344 ast_indicate(chan, AST_CONTROL_PROCEEDING);
00345 set_state(dial, AST_DIAL_RESULT_PROCEEDING);
00346 break;
00347 case AST_CONTROL_HOLD:
00348 if (option_verbose > 2)
00349 ast_verbose(VERBOSE_PREFIX_3 "Call on %s placed on hold\n", chan->name);
00350 ast_indicate(chan, AST_CONTROL_HOLD);
00351 break;
00352 case AST_CONTROL_UNHOLD:
00353 if (option_verbose > 2)
00354 ast_verbose(VERBOSE_PREFIX_3 "Call on %s left from hold\n", chan->name);
00355 ast_indicate(chan, AST_CONTROL_UNHOLD);
00356 break;
00357 case AST_CONTROL_OFFHOOK:
00358 case AST_CONTROL_FLASH:
00359 break;
00360 case -1:
00361
00362 ast_indicate(chan, -1);
00363 break;
00364 default:
00365 break;
00366 }
00367 }
00368
00369 return;
00370 }
00371
00372
00373 static void handle_frame_ownerless(struct ast_dial *dial, struct ast_dial_channel *channel, struct ast_frame *fr)
00374 {
00375
00376 if (fr->frametype != AST_FRAME_CONTROL)
00377 return;
00378
00379 switch (fr->subclass) {
00380 case AST_CONTROL_ANSWER:
00381 if (option_verbose > 2)
00382 ast_verbose( VERBOSE_PREFIX_3 "%s answered\n", channel->owner->name);
00383 AST_LIST_REMOVE(&dial->channels, channel, list);
00384 AST_LIST_INSERT_HEAD(&dial->channels, channel, list);
00385 set_state(dial, AST_DIAL_RESULT_ANSWERED);
00386 break;
00387 case AST_CONTROL_BUSY:
00388 if (option_verbose > 2)
00389 ast_verbose(VERBOSE_PREFIX_3 "%s is busy\n", channel->owner->name);
00390 ast_hangup(channel->owner);
00391 channel->owner = NULL;
00392 break;
00393 case AST_CONTROL_CONGESTION:
00394 if (option_verbose > 2)
00395 ast_verbose(VERBOSE_PREFIX_3 "%s is circuit-busy\n", channel->owner->name);
00396 ast_hangup(channel->owner);
00397 channel->owner = NULL;
00398 break;
00399 case AST_CONTROL_RINGING:
00400 if (option_verbose > 2)
00401 ast_verbose(VERBOSE_PREFIX_3 "%s is ringing\n", channel->owner->name);
00402 set_state(dial, AST_DIAL_RESULT_RINGING);
00403 break;
00404 case AST_CONTROL_PROGRESS:
00405 if (option_verbose > 2)
00406 ast_verbose (VERBOSE_PREFIX_3 "%s is making progress\n", channel->owner->name);
00407 set_state(dial, AST_DIAL_RESULT_PROGRESS);
00408 break;
00409 case AST_CONTROL_PROCEEDING:
00410 if (option_verbose > 2)
00411 ast_verbose (VERBOSE_PREFIX_3 "%s is proceeding\n", channel->owner->name);
00412 set_state(dial, AST_DIAL_RESULT_PROCEEDING);
00413 break;
00414 default:
00415 break;
00416 }
00417
00418 return;
00419 }
00420
00421
00422 static enum ast_dial_result monitor_dial(struct ast_dial *dial, struct ast_channel *chan)
00423 {
00424 int timeout = -1, count = 0;
00425 struct ast_channel *cs[AST_MAX_WATCHERS], *who = NULL;
00426 struct ast_dial_channel *channel = NULL;
00427 struct answer_exec_struct *answer_exec = NULL;
00428
00429 set_state(dial, AST_DIAL_RESULT_TRYING);
00430
00431
00432 if (dial->options[AST_DIAL_OPTION_RINGING]) {
00433 set_state(dial, AST_DIAL_RESULT_RINGING);
00434 if (chan)
00435 ast_indicate(chan, AST_CONTROL_RINGING);
00436 }
00437
00438
00439 while ((dial->state != AST_DIAL_RESULT_UNANSWERED) && (dial->state != AST_DIAL_RESULT_ANSWERED) && (dial->state != AST_DIAL_RESULT_HANGUP) && (dial->state != AST_DIAL_RESULT_TIMEOUT)) {
00440 int pos = 0;
00441 struct ast_frame *fr = NULL;
00442
00443
00444 pos = count = 0;
00445 if (chan)
00446 cs[pos++] = chan;
00447
00448
00449 AST_LIST_TRAVERSE(&dial->channels, channel, list) {
00450 if (channel->owner) {
00451 cs[pos++] = channel->owner;
00452 count++;
00453 }
00454 }
00455
00456
00457 if (!count) {
00458 set_state(dial, AST_DIAL_RESULT_UNANSWERED);
00459 break;
00460 }
00461
00462
00463 if (dial->thread == AST_PTHREADT_STOP)
00464 break;
00465
00466
00467 who = ast_waitfor_n(cs, pos, &timeout);
00468
00469
00470 if (dial->thread == AST_PTHREADT_STOP)
00471 break;
00472
00473
00474 if (!who)
00475 continue;
00476
00477
00478 if (!chan || !IS_CALLER(chan, who))
00479 channel = find_relative_dial_channel(dial, who);
00480
00481
00482 if (!(fr = ast_read(who))) {
00483
00484 if (chan && IS_CALLER(chan, who)) {
00485 set_state(dial, AST_DIAL_RESULT_HANGUP);
00486 break;
00487 }
00488 ast_hangup(who);
00489 channel->owner = NULL;
00490 continue;
00491 }
00492
00493
00494 if (chan)
00495 handle_frame(dial, channel, fr, chan);
00496 else
00497 handle_frame_ownerless(dial, channel, fr);
00498
00499
00500 ast_frfree(fr);
00501 }
00502
00503
00504 if (dial->state == AST_DIAL_RESULT_ANSWERED) {
00505
00506 AST_LIST_TRAVERSE(&dial->channels, channel, list) {
00507 if (!channel->owner || channel->owner == who)
00508 continue;
00509 ast_hangup(channel->owner);
00510 channel->owner = NULL;
00511 }
00512
00513 if ((channel = find_relative_dial_channel(dial, who)) && (answer_exec = FIND_RELATIVE_OPTION(dial, channel, AST_DIAL_OPTION_ANSWER_EXEC)))
00514 answer_exec_run(who, answer_exec->app, answer_exec->args);
00515 } else if (dial->state == AST_DIAL_RESULT_HANGUP) {
00516
00517 AST_LIST_TRAVERSE(&dial->channels, channel, list) {
00518 if (!channel->owner)
00519 continue;
00520 ast_hangup(channel->owner);
00521 channel->owner = NULL;
00522 }
00523 }
00524
00525 return dial->state;
00526 }
00527
00528
00529 static void *async_dial(void *data)
00530 {
00531 struct ast_dial *dial = data;
00532
00533
00534 monitor_dial(dial, NULL);
00535
00536 return NULL;
00537 }
00538
00539
00540
00541
00542
00543 enum ast_dial_result ast_dial_run(struct ast_dial *dial, struct ast_channel *chan, int async)
00544 {
00545 enum ast_dial_result res = AST_DIAL_RESULT_TRYING;
00546
00547
00548 if (!dial || (!chan && !async)) {
00549 ast_log(LOG_DEBUG, "invalid #1\n");
00550 return AST_DIAL_RESULT_INVALID;
00551 }
00552
00553
00554 if (AST_LIST_EMPTY(&dial->channels)) {
00555 ast_log(LOG_DEBUG, "invalid #2\n");
00556 return AST_DIAL_RESULT_INVALID;
00557 }
00558
00559
00560 if (!begin_dial(dial, chan))
00561 return AST_DIAL_RESULT_FAILED;
00562
00563
00564 if (async) {
00565 dial->state = AST_DIAL_RESULT_TRYING;
00566
00567 if (ast_pthread_create(&dial->thread, NULL, async_dial, dial)) {
00568
00569 ast_dial_hangup(dial);
00570 res = AST_DIAL_RESULT_FAILED;
00571 }
00572 } else {
00573 res = monitor_dial(dial, chan);
00574 }
00575
00576 return res;
00577 }
00578
00579
00580
00581
00582
00583 struct ast_channel *ast_dial_answered(struct ast_dial *dial)
00584 {
00585 if (!dial)
00586 return NULL;
00587
00588 return ((dial->state == AST_DIAL_RESULT_ANSWERED) ? AST_LIST_FIRST(&dial->channels)->owner : NULL);
00589 }
00590
00591
00592
00593
00594
00595 enum ast_dial_result ast_dial_state(struct ast_dial *dial)
00596 {
00597 return dial->state;
00598 }
00599
00600
00601
00602
00603
00604 enum ast_dial_result ast_dial_join(struct ast_dial *dial)
00605 {
00606 pthread_t thread;
00607
00608
00609 if (dial->thread == AST_PTHREADT_NULL)
00610 return AST_DIAL_RESULT_FAILED;
00611
00612
00613 thread = dial->thread;
00614
00615
00616 dial->thread = AST_PTHREADT_STOP;
00617
00618
00619 pthread_kill(thread, SIGURG);
00620
00621
00622 pthread_join(thread, NULL);
00623
00624
00625 dial->thread = AST_PTHREADT_NULL;
00626
00627 return dial->state;
00628 }
00629
00630
00631
00632
00633
00634 void ast_dial_hangup(struct ast_dial *dial)
00635 {
00636 struct ast_dial_channel *channel = NULL;
00637
00638 if (!dial)
00639 return;
00640
00641 AST_LIST_TRAVERSE(&dial->channels, channel, list) {
00642 if (channel->owner) {
00643 ast_hangup(channel->owner);
00644 channel->owner = NULL;
00645 }
00646 }
00647
00648 return;
00649 }
00650
00651
00652
00653
00654
00655
00656 int ast_dial_destroy(struct ast_dial *dial)
00657 {
00658 int i = 0;
00659 struct ast_dial_channel *channel = NULL;
00660
00661 if (!dial)
00662 return -1;
00663
00664
00665 AST_LIST_TRAVERSE(&dial->channels, channel, list) {
00666
00667 for (i = 0; i < AST_DIAL_OPTION_MAX; i++) {
00668 if (!channel->options[i])
00669 continue;
00670 if (option_types[i].disable)
00671 option_types[i].disable(channel->options[i]);
00672 channel->options[i] = NULL;
00673 }
00674
00675 if (channel->owner) {
00676 ast_hangup(channel->owner);
00677 channel->owner = NULL;
00678 }
00679
00680 free(channel);
00681 }
00682
00683
00684 for (i = 0; i < AST_DIAL_OPTION_MAX; i++) {
00685 if (!dial->options[i])
00686 continue;
00687 if (option_types[i].disable)
00688 option_types[i].disable(dial->options[i]);
00689 dial->options[i] = NULL;
00690 }
00691
00692
00693 free(dial);
00694
00695 return 0;
00696 }
00697
00698
00699
00700
00701
00702
00703
00704 int ast_dial_option_global_enable(struct ast_dial *dial, enum ast_dial_option option, void *data)
00705 {
00706
00707 if (dial->options[option])
00708 return -1;
00709
00710
00711 if (option_types[option].enable)
00712 dial->options[option] = option_types[option].enable(data);
00713 else
00714 dial->options[option] = (void*)1;
00715
00716 return 0;
00717 }
00718
00719
00720
00721
00722
00723
00724
00725
00726 int ast_dial_option_enable(struct ast_dial *dial, int num, enum ast_dial_option option, void *data)
00727 {
00728 struct ast_dial_channel *channel = NULL;
00729
00730
00731 if (!dial || AST_LIST_EMPTY(&dial->channels))
00732 return -1;
00733
00734
00735 if (AST_LIST_LAST(&dial->channels)->num != num) {
00736 AST_LIST_TRAVERSE(&dial->channels, channel, list) {
00737 if (channel->num == num)
00738 break;
00739 }
00740 } else {
00741 channel = AST_LIST_LAST(&dial->channels);
00742 }
00743
00744
00745 if (!channel)
00746 return -1;
00747
00748
00749 if (channel->options[option])
00750 return -1;
00751
00752
00753 if (option_types[option].enable)
00754 channel->options[option] = option_types[option].enable(data);
00755 else
00756 channel->options[option] = (void*)1;
00757
00758 return 0;
00759 }
00760
00761
00762
00763
00764
00765
00766 int ast_dial_option_global_disable(struct ast_dial *dial, enum ast_dial_option option)
00767 {
00768
00769 if (!dial->options[option])
00770 return -1;
00771
00772
00773 if (option_types[option].disable)
00774 option_types[option].disable(dial->options[option]);
00775
00776
00777 dial->options[option] = NULL;
00778
00779 return 0;
00780 }
00781
00782
00783
00784
00785
00786
00787
00788 int ast_dial_option_disable(struct ast_dial *dial, int num, enum ast_dial_option option)
00789 {
00790 struct ast_dial_channel *channel = NULL;
00791
00792
00793 if (!dial || AST_LIST_EMPTY(&dial->channels))
00794 return -1;
00795
00796
00797 if (AST_LIST_LAST(&dial->channels)->num != num) {
00798 AST_LIST_TRAVERSE(&dial->channels, channel, list) {
00799 if (channel->num == num)
00800 break;
00801 }
00802 } else {
00803 channel = AST_LIST_LAST(&dial->channels);
00804 }
00805
00806
00807 if (!channel)
00808 return -1;
00809
00810
00811 if (!channel->options[option])
00812 return -1;
00813
00814
00815 if (option_types[option].disable)
00816 option_types[option].disable(channel->options[option]);
00817
00818
00819 channel->options[option] = NULL;
00820
00821 return 0;
00822 }
00823
00824 void ast_dial_set_state_callback(struct ast_dial *dial, ast_dial_state_callback callback)
00825 {
00826 dial->state_callback = callback;
00827 }