aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2009-01-16 15:51:43 +0000
committerrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2009-01-16 15:51:43 +0000
commit52acc4c45786fc04da9536c2801eb2b483cc2009 (patch)
tree3e0b68760b7b6d49c72de4fd5b747ed4d5726ad3
parent5d9d64e584ec5220835eb10a124b555d449fa3d3 (diff)
parentcaebf8461f9849f484eb5bbd649880e457c20e31 (diff)
Creating tag for the release of asterisk-1.4.23-rc4
git-svn-id: http://svn.digium.com/svn/asterisk/tags/1.4.23-rc4@168755 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--.cleancount1
-rw-r--r--BUGS22
-rw-r--r--CHANGES370
-rw-r--r--COPYING341
-rw-r--r--CREDITS215
-rw-r--r--LICENSE68
-rw-r--r--Makefile772
-rw-r--r--Makefile.moddir_rules112
-rw-r--r--Makefile.rules129
-rw-r--r--README262
-rw-r--r--UPGRADE-1.2.txt210
-rw-r--r--UPGRADE.txt500
-rw-r--r--Zaptel-to-DAHDI.txt105
-rw-r--r--agi/DialAnMp3.agi82
-rw-r--r--agi/Makefile47
-rw-r--r--agi/agi-test.agi79
-rw-r--r--agi/eagi-sphinx-test.c227
-rw-r--r--agi/eagi-test.c169
-rw-r--r--agi/fastagi-test94
-rwxr-xr-xagi/jukebox.agi488
-rw-r--r--agi/numeralize44
-rw-r--r--apps/Makefile42
-rw-r--r--apps/app_adsiprog.c1592
-rw-r--r--apps/app_alarmreceiver.c841
-rw-r--r--apps/app_amd.c430
-rw-r--r--apps/app_authenticate.c254
-rw-r--r--apps/app_cdr.c78
-rw-r--r--apps/app_chanisavail.c173
-rw-r--r--apps/app_channelredirect.c140
-rw-r--r--apps/app_chanspy.c878
-rw-r--r--apps/app_controlplayback.c168
-rw-r--r--apps/app_dahdibarge.c358
-rw-r--r--apps/app_dahdiras.c288
-rw-r--r--apps/app_dahdiscan.c389
-rw-r--r--apps/app_db.c167
-rw-r--r--apps/app_dial.c2015
-rw-r--r--apps/app_dictate.c349
-rw-r--r--apps/app_directed_pickup.c181
-rw-r--r--apps/app_directory.c707
-rw-r--r--apps/app_disa.c399
-rw-r--r--apps/app_dumpchan.c176
-rw-r--r--apps/app_echo.c104
-rw-r--r--apps/app_exec.c221
-rw-r--r--apps/app_externalivr.c585
-rw-r--r--apps/app_festival.c566
-rw-r--r--apps/app_flash.c136
-rw-r--r--apps/app_followme.c1138
-rw-r--r--apps/app_forkcdr.c267
-rw-r--r--apps/app_getcpeid.c148
-rw-r--r--apps/app_hasnewvoicemail.c225
-rw-r--r--apps/app_ices.c233
-rw-r--r--apps/app_image.c125
-rw-r--r--apps/app_ivrdemo.c132
-rw-r--r--apps/app_lookupblacklist.c160
-rw-r--r--apps/app_lookupcidname.c103
-rw-r--r--apps/app_macro.c592
-rw-r--r--apps/app_meetme.c5037
-rw-r--r--apps/app_milliwatt.c194
-rw-r--r--apps/app_mixmonitor.c446
-rw-r--r--apps/app_morsecode.c179
-rw-r--r--apps/app_mp3.c255
-rw-r--r--apps/app_nbscat.c237
-rw-r--r--apps/app_osplookup.c1677
-rw-r--r--apps/app_page.c217
-rw-r--r--apps/app_parkandannounce.c260
-rw-r--r--apps/app_playback.c494
-rw-r--r--apps/app_privacy.c232
-rw-r--r--apps/app_queue.c5140
-rw-r--r--apps/app_random.c108
-rw-r--r--apps/app_read.c234
-rw-r--r--apps/app_readfile.c120
-rw-r--r--apps/app_realtime.c263
-rw-r--r--apps/app_record.c386
-rw-r--r--apps/app_rpt.c11930
-rw-r--r--apps/app_sayunixtime.c126
-rw-r--r--apps/app_senddtmf.c143
-rw-r--r--apps/app_sendtext.c131
-rw-r--r--apps/app_setcallerid.c162
-rw-r--r--apps/app_setcdruserfield.c175
-rw-r--r--apps/app_settransfercapability.c136
-rw-r--r--apps/app_skel.c133
-rw-r--r--apps/app_sms.c1538
-rw-r--r--apps/app_softhangup.c121
-rw-r--r--apps/app_speech_utils.c875
-rw-r--r--apps/app_stack.c174
-rw-r--r--apps/app_system.c162
-rw-r--r--apps/app_talkdetect.c227
-rw-r--r--apps/app_test.c512
-rw-r--r--apps/app_transfer.c156
-rw-r--r--apps/app_url.c173
-rw-r--r--apps/app_userevent.c109
-rw-r--r--apps/app_verbose.c168
-rw-r--r--apps/app_voicemail.c9268
-rw-r--r--apps/app_waitforring.c136
-rw-r--r--apps/app_waitforsilence.c207
-rw-r--r--apps/app_while.c339
-rw-r--r--apps/app_zapateller.c120
-rw-r--r--apps/enter.h287
-rw-r--r--apps/leave.h207
-rw-r--r--apps/rpt_flow.pdfbin0 -> 51935 bytes
-rw-r--r--autoconf/acx_pthread.m4242
-rw-r--r--autoconf/ast_c_compile_check.m431
-rw-r--r--autoconf/ast_c_define_check.m433
-rw-r--r--autoconf/ast_check_gnu_make.m420
-rw-r--r--autoconf/ast_check_openh323.m4126
-rw-r--r--autoconf/ast_check_pwlib.m4255
-rw-r--r--autoconf/ast_ext_lib.m482
-rw-r--r--autoconf/ast_func_fork.m459
-rw-r--r--autoconf/ast_gcc_attribute.m416
-rw-r--r--autoconf/ast_prog_egrep.m411
-rw-r--r--autoconf/ast_prog_ld.m483
-rw-r--r--autoconf/ast_prog_ld_gnu.m416
-rw-r--r--autoconf/ast_prog_sed.m421
-rwxr-xr-xbootstrap.sh40
-rw-r--r--build_tools/cflags-devmode.xml17
-rw-r--r--build_tools/cflags.xml21
-rw-r--r--build_tools/embed_modules.xml26
-rw-r--r--build_tools/get_makeopts3
-rw-r--r--build_tools/get_moduleinfo3
-rwxr-xr-xbuild_tools/make_build_h20
-rwxr-xr-xbuild_tools/make_buildopts_h29
-rwxr-xr-xbuild_tools/make_defaults_h26
-rwxr-xr-xbuild_tools/make_sample_voicemail25
-rwxr-xr-xbuild_tools/make_version83
-rwxr-xr-xbuild_tools/make_version_h25
-rw-r--r--build_tools/menuselect-deps.in41
-rwxr-xr-xbuild_tools/mkpkgconfig50
-rwxr-xr-xbuild_tools/prep_tarball9
-rwxr-xr-xbuild_tools/strip_nonapi37
-rw-r--r--cdr/Makefile32
-rw-r--r--cdr/cdr_csv.c365
-rw-r--r--cdr/cdr_custom.c174
-rw-r--r--cdr/cdr_manager.c170
-rw-r--r--cdr/cdr_odbc.c481
-rw-r--r--cdr/cdr_pgsql.c336
-rw-r--r--cdr/cdr_radius.c273
-rw-r--r--cdr/cdr_sqlite.c217
-rw-r--r--cdr/cdr_tds.c630
-rw-r--r--channels/DialTone.h252
-rw-r--r--channels/Makefile123
-rw-r--r--channels/answer.h237
-rw-r--r--channels/busy_tone.h55
-rw-r--r--channels/chan_agent.c2870
-rw-r--r--channels/chan_alsa.c1391
-rw-r--r--channels/chan_dahdi.c11992
-rw-r--r--channels/chan_features.c580
-rw-r--r--channels/chan_gtalk.c2067
-rw-r--r--channels/chan_h323.c3274
-rw-r--r--channels/chan_iax2.c11336
-rw-r--r--channels/chan_local.c808
-rw-r--r--channels/chan_mgcp.c4430
-rw-r--r--channels/chan_misdn.c5775
-rw-r--r--channels/chan_nbs.c302
-rw-r--r--channels/chan_oss.c1899
-rw-r--r--channels/chan_phone.c1433
-rw-r--r--channels/chan_sip.c18893
-rw-r--r--channels/chan_skinny.c5016
-rw-r--r--channels/chan_vpb.cc2905
-rw-r--r--channels/gentone-ulaw.c157
-rw-r--r--channels/gentone.c95
-rw-r--r--channels/h323/ChangeLog43
-rw-r--r--channels/h323/INSTALL.openh32318
-rw-r--r--channels/h323/Makefile.in48
-rw-r--r--channels/h323/README144
-rw-r--r--channels/h323/TODO9
-rw-r--r--channels/h323/ast_h323.cxx2487
-rw-r--r--channels/h323/ast_h323.h166
-rw-r--r--channels/h323/caps_h323.cxx239
-rw-r--r--channels/h323/caps_h323.h124
-rw-r--r--channels/h323/chan_h323.h254
-rw-r--r--channels/h323/cisco-h225.asn74
-rw-r--r--channels/h323/cisco-h225.cxx853
-rw-r--r--channels/h323/cisco-h225.h299
-rw-r--r--channels/h323/compat_h323.cxx138
-rw-r--r--channels/h323/compat_h323.h80
-rw-r--r--channels/h323/noexport.map5
-rw-r--r--channels/iax2-parser.c1053
-rw-r--r--channels/iax2-parser.h160
-rw-r--r--channels/iax2-provision.c540
-rw-r--r--channels/iax2-provision.h53
-rw-r--r--channels/iax2.h237
-rw-r--r--channels/misdn/Makefile17
-rw-r--r--channels/misdn/chan_misdn_config.h152
-rw-r--r--channels/misdn/ie.c1422
-rw-r--r--channels/misdn/isdn_lib.c4661
-rw-r--r--channels/misdn/isdn_lib.h674
-rw-r--r--channels/misdn/isdn_lib_intern.h142
-rw-r--r--channels/misdn/isdn_msg_parser.c1348
-rw-r--r--channels/misdn/portinfo.c198
-rw-r--r--channels/misdn_config.c1160
-rw-r--r--channels/ring10.h1752
-rw-r--r--channels/ring_tone.h30
-rw-r--r--codecs/Makefile61
-rw-r--r--codecs/adpcm_slin_ex.h25
-rw-r--r--codecs/codec_a_mu.c168
-rw-r--r--codecs/codec_adpcm.c404
-rw-r--r--codecs/codec_alaw.c189
-rw-r--r--codecs/codec_dahdi.c480
-rw-r--r--codecs/codec_g726.c964
-rw-r--r--codecs/codec_gsm.c290
-rw-r--r--codecs/codec_ilbc.c248
-rw-r--r--codecs/codec_lpc10.c317
-rw-r--r--codecs/codec_speex.c522
-rw-r--r--codecs/codec_ulaw.c201
-rw-r--r--codecs/g726_slin_ex.h25
-rw-r--r--codecs/gsm/COPYRIGHT16
-rw-r--r--codecs/gsm/Makefile543
-rw-r--r--codecs/gsm/README37
-rw-r--r--codecs/gsm/inc/config.h51
-rw-r--r--codecs/gsm/inc/gsm.h71
-rw-r--r--codecs/gsm/inc/private.h312
-rw-r--r--codecs/gsm/inc/proto.h65
-rw-r--r--codecs/gsm/inc/unproto.h23
-rw-r--r--codecs/gsm/libgsm.vcproj253
-rw-r--r--codecs/gsm/src/add.c235
-rw-r--r--codecs/gsm/src/code.c97
-rw-r--r--codecs/gsm/src/debug.c76
-rw-r--r--codecs/gsm/src/decode.c62
-rw-r--r--codecs/gsm/src/gsm_create.c45
-rw-r--r--codecs/gsm/src/gsm_decode.c361
-rw-r--r--codecs/gsm/src/gsm_destroy.c26
-rw-r--r--codecs/gsm/src/gsm_encode.c451
-rw-r--r--codecs/gsm/src/gsm_explode.c417
-rw-r--r--codecs/gsm/src/gsm_implode.c515
-rw-r--r--codecs/gsm/src/gsm_option.c69
-rw-r--r--codecs/gsm/src/gsm_print.c167
-rw-r--r--codecs/gsm/src/k6opt.h84
-rw-r--r--codecs/gsm/src/k6opt.s739
-rw-r--r--codecs/gsm/src/long_term.c955
-rw-r--r--codecs/gsm/src/lpc.c372
-rw-r--r--codecs/gsm/src/preprocess.c127
-rw-r--r--codecs/gsm/src/rpe.c490
-rw-r--r--codecs/gsm/src/short_term.c448
-rw-r--r--codecs/gsm/src/table.c63
-rw-r--r--codecs/gsm_slin_ex.h16
-rw-r--r--codecs/ilbc/Makefile20
-rw-r--r--codecs/ilbc_slin_ex.h17
-rw-r--r--codecs/log2comp.h74
-rw-r--r--codecs/lpc10/Makefile77
-rw-r--r--codecs/lpc10/README89
-rw-r--r--codecs/lpc10/analys.c649
-rw-r--r--codecs/lpc10/bsynz.c447
-rw-r--r--codecs/lpc10/chanwr.c232
-rw-r--r--codecs/lpc10/dcbias.c107
-rw-r--r--codecs/lpc10/decode.c625
-rw-r--r--codecs/lpc10/deemp.c154
-rw-r--r--codecs/lpc10/difmag.c133
-rw-r--r--codecs/lpc10/dyptrk.c405
-rw-r--r--codecs/lpc10/encode.c373
-rw-r--r--codecs/lpc10/energy.c103
-rw-r--r--codecs/lpc10/f2c.h325
-rw-r--r--codecs/lpc10/f2clib.c85
-rw-r--r--codecs/lpc10/ham84.c126
-rw-r--r--codecs/lpc10/hp100.c169
-rw-r--r--codecs/lpc10/invert.c193
-rw-r--r--codecs/lpc10/irc2pc.c151
-rw-r--r--codecs/lpc10/ivfilt.c136
-rw-r--r--codecs/lpc10/liblpc10.vcproj305
-rw-r--r--codecs/lpc10/lpc10.h256
-rw-r--r--codecs/lpc10/lpcdec.c297
-rw-r--r--codecs/lpc10/lpcenc.c181
-rw-r--r--codecs/lpc10/lpcini.c446
-rw-r--r--codecs/lpc10/lpfilt.c125
-rw-r--r--codecs/lpc10/median.c89
-rw-r--r--codecs/lpc10/mload.c163
-rw-r--r--codecs/lpc10/onset.c324
-rw-r--r--codecs/lpc10/pitsyn.c583
-rw-r--r--codecs/lpc10/placea.c242
-rw-r--r--codecs/lpc10/placev.c275
-rw-r--r--codecs/lpc10/preemp.c144
-rw-r--r--codecs/lpc10/prepro.c116
-rw-r--r--codecs/lpc10/random.c125
-rw-r--r--codecs/lpc10/rcchk.c119
-rw-r--r--codecs/lpc10/synths.c425
-rw-r--r--codecs/lpc10/tbdm.c188
-rw-r--r--codecs/lpc10/voicin.c786
-rw-r--r--codecs/lpc10/vparms.c255
-rw-r--r--codecs/lpc10_slin_ex.h13
-rw-r--r--codecs/slin_adpcm_ex.h25
-rw-r--r--codecs/slin_g726_ex.h25
-rw-r--r--codecs/slin_gsm_ex.h28
-rw-r--r--codecs/slin_ilbc_ex.h28
-rw-r--r--codecs/slin_lpc10_ex.h21
-rw-r--r--codecs/slin_speex_ex.h262
-rw-r--r--codecs/slin_ulaw_ex.h25
-rw-r--r--codecs/speex_slin_ex.h16
-rw-r--r--codecs/ulaw_slin_ex.h25
-rwxr-xr-xconfig.guess1495
-rwxr-xr-xconfig.sub1609
-rw-r--r--configs/adsi.conf.sample8
-rw-r--r--configs/adtranvofr.conf.sample39
-rw-r--r--configs/agents.conf.sample107
-rw-r--r--configs/alarmreceiver.conf.sample80
-rw-r--r--configs/alsa.conf.sample62
-rw-r--r--configs/amd.conf.sample18
-rw-r--r--configs/asterisk.adsi159
-rw-r--r--configs/cdr.conf.sample148
-rw-r--r--configs/cdr_custom.conf.sample10
-rw-r--r--configs/cdr_manager.conf.sample6
-rw-r--r--configs/cdr_odbc.conf.sample12
-rw-r--r--configs/cdr_pgsql.conf.sample9
-rw-r--r--configs/cdr_tds.conf.sample11
-rw-r--r--configs/chan_dahdi.conf.sample675
-rw-r--r--configs/codecs.conf.sample65
-rw-r--r--configs/dnsmgr.conf.sample5
-rw-r--r--configs/dundi.conf.sample239
-rw-r--r--configs/enum.conf.sample22
-rw-r--r--configs/extconfig.conf.sample59
-rw-r--r--configs/extensions.ael.sample448
-rw-r--r--configs/extensions.conf.sample614
-rw-r--r--configs/features.conf.sample100
-rw-r--r--configs/festival.conf.sample35
-rw-r--r--configs/followme.conf.sample86
-rw-r--r--configs/func_odbc.conf.sample41
-rw-r--r--configs/gtalk.conf.sample19
-rw-r--r--configs/h323.conf.sample193
-rw-r--r--configs/http.conf.sample40
-rw-r--r--configs/iax.conf.sample416
-rw-r--r--configs/iaxprov.conf.sample81
-rw-r--r--configs/indications.conf.sample733
-rw-r--r--configs/jabber.conf.sample18
-rw-r--r--configs/logger.conf.sample69
-rw-r--r--configs/manager.conf.sample56
-rw-r--r--configs/meetme.conf.sample26
-rw-r--r--configs/mgcp.conf.sample104
-rw-r--r--configs/misdn.conf.sample438
-rw-r--r--configs/modules.conf.sample35
-rw-r--r--configs/musiconhold.conf.sample66
-rw-r--r--configs/muted.conf.sample39
-rw-r--r--configs/osp.conf.sample72
-rw-r--r--configs/oss.conf.sample75
-rw-r--r--configs/phone.conf.sample49
-rw-r--r--configs/privacy.conf.sample3
-rw-r--r--configs/queues.conf.sample311
-rw-r--r--configs/res_odbc.conf.sample49
-rw-r--r--configs/res_pgsql.conf.sample14
-rw-r--r--configs/res_snmp.conf.sample10
-rw-r--r--configs/rpt.conf.sample193
-rw-r--r--configs/rtp.conf.sample22
-rw-r--r--configs/say.conf.sample171
-rw-r--r--configs/sip.conf.sample685
-rw-r--r--configs/sip_notify.conf.sample22
-rw-r--r--configs/skinny.conf.sample96
-rw-r--r--configs/sla.conf.sample140
-rw-r--r--configs/smdi.conf.sample75
-rw-r--r--configs/telcordia-1.adsi83
-rw-r--r--configs/udptl.conf.sample30
-rw-r--r--configs/users.conf.sample79
-rw-r--r--configs/voicemail.conf.sample248
-rw-r--r--configs/vpb.conf.sample108
-rwxr-xr-xconfigure33094
-rw-r--r--configure.ac1678
-rw-r--r--contrib/README.festival47
-rw-r--r--contrib/asterisk-doxygen-header10
-rw-r--r--contrib/asterisk-ices.xml93
-rw-r--r--contrib/asterisk-ng-doxygen1309
-rw-r--r--contrib/dictionary.digium31
-rw-r--r--contrib/festival-1.4.1-diff76
-rw-r--r--contrib/festival-1.4.2.diff75
-rw-r--r--contrib/festival-1.4.3.diff93
-rw-r--r--contrib/festival-1.95.diff107
-rw-r--r--contrib/firmware/iax/iaxy.binbin0 -> 39402 bytes
-rw-r--r--contrib/i18n.testsuite.conf136
-rwxr-xr-xcontrib/init.d/rc.debian.asterisk95
-rwxr-xr-xcontrib/init.d/rc.gentoo.asterisk26
-rwxr-xr-xcontrib/init.d/rc.mandrake.asterisk195
-rwxr-xr-xcontrib/init.d/rc.mandrake.zaptel108
-rwxr-xr-xcontrib/init.d/rc.redhat.asterisk144
-rwxr-xr-xcontrib/init.d/rc.slackware.asterisk51
-rwxr-xr-xcontrib/init.d/rc.suse.asterisk136
-rw-r--r--contrib/scripts/README.messages-expire20
-rw-r--r--contrib/scripts/agents.php73
-rw-r--r--contrib/scripts/ast_grab_core70
-rw-r--r--contrib/scripts/astgenkey61
-rw-r--r--contrib/scripts/astgenkey.8144
-rw-r--r--contrib/scripts/autosupport255
-rw-r--r--contrib/scripts/autosupport.841
-rwxr-xr-xcontrib/scripts/get_ilbc_source.sh33
-rw-r--r--contrib/scripts/iax-friends.sql54
-rw-r--r--contrib/scripts/loadtest.tcl148
-rw-r--r--contrib/scripts/lookup.agi90
-rw-r--r--contrib/scripts/managerproxy.pl242
-rw-r--r--contrib/scripts/meetme.sql12
-rw-r--r--contrib/scripts/messages-expire.pl96
-rw-r--r--contrib/scripts/qview.pl100
-rw-r--r--contrib/scripts/realtime_pgsql.sql142
-rw-r--r--contrib/scripts/retrieve_extensions_from_mysql.pl113
-rw-r--r--contrib/scripts/retrieve_extensions_from_sql.pl158
-rw-r--r--contrib/scripts/retrieve_sip_conf_from_mysql.pl93
-rw-r--r--contrib/scripts/safe_asterisk178
-rw-r--r--contrib/scripts/safe_asterisk.869
-rw-r--r--contrib/scripts/safe_asterisk_restart110
-rw-r--r--contrib/scripts/sip-friends.sql54
-rw-r--r--contrib/scripts/vmail.cgi1140
-rw-r--r--contrib/scripts/vmdb.sql64
-rw-r--r--contrib/thirdparty/spexxilbcfix_xlite.regbin0 -> 452 bytes
-rw-r--r--contrib/thirdparty/spexxilbcfix_xpro.regbin0 -> 450 bytes
-rw-r--r--contrib/utils/README.rawplayer37
-rw-r--r--contrib/utils/rawplayer.c46
-rw-r--r--contrib/utils/zones2indications.c153
-rw-r--r--contrib/valgrind-RedHat-8.0.supp41
-rw-r--r--doc/00README.1st75
-rw-r--r--doc/CODING-GUIDELINES543
-rw-r--r--doc/PEERING499
-rw-r--r--doc/ael.txt1260
-rw-r--r--doc/ajam.txt91
-rw-r--r--doc/app-sms.txt470
-rw-r--r--doc/apps.txt10
-rw-r--r--doc/asterisk-conf.txt89
-rw-r--r--doc/asterisk-mib.txt748
-rw-r--r--doc/asterisk.8195
-rw-r--r--doc/asterisk.sgml364
-rw-r--r--doc/backtrace.txt197
-rw-r--r--doc/billing.txt105
-rw-r--r--doc/callfiles.txt139
-rw-r--r--doc/callingpres.txt18
-rw-r--r--doc/cdrdriver.txt216
-rw-r--r--doc/chaniax.txt369
-rw-r--r--doc/channels.txt44
-rw-r--r--doc/channelvariables.txt815
-rw-r--r--doc/cli.txt33
-rw-r--r--doc/cliprompt.txt29
-rw-r--r--doc/configuration.txt180
-rw-r--r--doc/cygwin.txt9
-rw-r--r--doc/datastores.txt63
-rw-r--r--doc/digium-mib.txt24
-rw-r--r--doc/dundi.txt26
-rw-r--r--doc/enum.txt308
-rw-r--r--doc/extconfig.txt91
-rw-r--r--doc/extensions.txt58
-rw-r--r--doc/externalivr.txt109
-rw-r--r--doc/freetds.txt18
-rw-r--r--doc/h323.txt22
-rw-r--r--doc/hardware.txt74
-rw-r--r--doc/iax.txt67
-rw-r--r--doc/ices.txt12
-rw-r--r--doc/imapstorage.txt217
-rw-r--r--doc/ip-tos.txt81
-rw-r--r--doc/jabber.txt15
-rw-r--r--doc/jingle.txt11
-rw-r--r--doc/jitterbuffer.txt137
-rw-r--r--doc/lang/hebrew.odsbin0 -> 23290 bytes
-rw-r--r--doc/linkedlists.txt98
-rw-r--r--doc/localchannel.txt49
-rw-r--r--doc/macroexclusive.txt78
-rw-r--r--doc/manager.txt311
-rw-r--r--doc/math.txt69
-rw-r--r--doc/misdn.txt301
-rw-r--r--doc/model.txt15
-rw-r--r--doc/modules.txt26
-rw-r--r--doc/mp3.txt13
-rw-r--r--doc/musiconhold-fpm.txt8
-rw-r--r--doc/mysql.txt15
-rw-r--r--doc/odbcstorage.txt30
-rw-r--r--doc/osp.txt421
-rw-r--r--doc/privacy.txt361
-rw-r--r--doc/queuelog.txt99
-rw-r--r--doc/queues-with-callback-members.txt521
-rw-r--r--doc/radius.txt203
-rw-r--r--doc/realtime.txt138
-rw-r--r--doc/rtp-packetization.txt73
-rw-r--r--doc/security.txt80
-rw-r--r--doc/sip-retransmit.txt126
-rw-r--r--doc/sla.pdfbin0 -> 68499 bytes
-rw-r--r--doc/sla.tex378
-rw-r--r--doc/smdi.txt137
-rw-r--r--doc/sms.txt147
-rw-r--r--doc/snmp.txt39
-rw-r--r--doc/speechrec.txt295
-rw-r--r--doc/valgrind.txt30
-rw-r--r--doc/video.txt46
-rw-r--r--doc/voicemail_odbc_postgresql.txt453
-rw-r--r--formats/Makefile32
-rw-r--r--formats/format_g723.c163
-rw-r--r--formats/format_g726.c277
-rw-r--r--formats/format_g729.c159
-rw-r--r--formats/format_gsm.c183
-rw-r--r--formats/format_h263.c197
-rw-r--r--formats/format_h264.c186
-rw-r--r--formats/format_ilbc.c157
-rw-r--r--formats/format_jpeg.c127
-rw-r--r--formats/format_ogg_vorbis.c572
-rw-r--r--formats/format_pcm.c507
-rw-r--r--formats/format_sln.c141
-rw-r--r--formats/format_vox.c146
-rw-r--r--formats/format_wav.c531
-rw-r--r--formats/format_wav_gsm.c562
-rw-r--r--formats/msgsm.h689
-rw-r--r--funcs/Makefile32
-rw-r--r--funcs/func_audiohookinherit.c282
-rw-r--r--funcs/func_base64.c94
-rw-r--r--funcs/func_callerid.c187
-rw-r--r--funcs/func_cdr.c185
-rw-r--r--funcs/func_channel.c198
-rw-r--r--funcs/func_curl.c215
-rw-r--r--funcs/func_cut.c330
-rw-r--r--funcs/func_db.c231
-rw-r--r--funcs/func_enum.c198
-rw-r--r--funcs/func_env.c158
-rw-r--r--funcs/func_global.c86
-rw-r--r--funcs/func_groupcount.c243
-rw-r--r--funcs/func_language.c90
-rw-r--r--funcs/func_logic.c214
-rw-r--r--funcs/func_math.c268
-rw-r--r--funcs/func_md5.c120
-rw-r--r--funcs/func_moh.c87
-rw-r--r--funcs/func_odbc.c672
-rw-r--r--funcs/func_rand.c104
-rw-r--r--funcs/func_realtime.c175
-rw-r--r--funcs/func_sha1.c83
-rw-r--r--funcs/func_strings.c636
-rw-r--r--funcs/func_timeout.c194
-rw-r--r--funcs/func_uri.c101
-rw-r--r--images/animlogo.gifbin0 -> 63968 bytes
-rw-r--r--images/asterisk-intro.jpgbin0 -> 6143 bytes
-rw-r--r--images/play.gifbin0 -> 341 bytes
-rw-r--r--include/asterisk.h210
-rw-r--r--include/asterisk/abstract_jb.h226
-rw-r--r--include/asterisk/acl.h57
-rw-r--r--include/asterisk/adsi.h353
-rw-r--r--include/asterisk/ael_structs.h195
-rw-r--r--include/asterisk/aes.h170
-rw-r--r--include/asterisk/agi.h57
-rw-r--r--include/asterisk/alaw.h43
-rw-r--r--include/asterisk/app.h434
-rw-r--r--include/asterisk/ast_expr.h32
-rw-r--r--include/asterisk/astdb.h52
-rw-r--r--include/asterisk/astmm.h89
-rw-r--r--include/asterisk/astobj.h824
-rw-r--r--include/asterisk/astobj2.h560
-rw-r--r--include/asterisk/astosp.h31
-rw-r--r--include/asterisk/audiohook.h211
-rw-r--r--include/asterisk/autoconfig.h.in713
-rw-r--r--include/asterisk/callerid.h339
-rw-r--r--include/asterisk/causes.h83
-rw-r--r--include/asterisk/cdr.h342
-rw-r--r--include/asterisk/channel.h1442
-rw-r--r--include/asterisk/chanvars.h42
-rw-r--r--include/asterisk/cli.h183
-rw-r--r--include/asterisk/compat.h131
-rw-r--r--include/asterisk/compiler.h62
-rw-r--r--include/asterisk/config.h206
-rw-r--r--include/asterisk/crypto.h112
-rw-r--r--include/asterisk/dahdi_compat.h458
-rw-r--r--include/asterisk/devicestate.h133
-rw-r--r--include/asterisk/dial.h151
-rw-r--r--include/asterisk/dns.h39
-rw-r--r--include/asterisk/dnsmgr.h62
-rw-r--r--include/asterisk/doxyref.h580
-rw-r--r--include/asterisk/dsp.h124
-rw-r--r--include/asterisk/dundi.h226
-rw-r--r--include/asterisk/endian.h62
-rw-r--r--include/asterisk/enum.h59
-rw-r--r--include/asterisk/features.h101
-rw-r--r--include/asterisk/file.h434
-rw-r--r--include/asterisk/frame.h597
-rw-r--r--include/asterisk/fskmodem.h72
-rw-r--r--include/asterisk/global_datastores.h47
-rw-r--r--include/asterisk/http.h65
-rw-r--r--include/asterisk/image.h96
-rw-r--r--include/asterisk/indications.h89
-rw-r--r--include/asterisk/inline_api.h66
-rw-r--r--include/asterisk/io.h145
-rw-r--r--include/asterisk/jabber.h146
-rw-r--r--include/asterisk/jingle.h45
-rw-r--r--include/asterisk/linkedlists.h761
-rw-r--r--include/asterisk/localtime.h30
-rw-r--r--include/asterisk/lock.h1253
-rw-r--r--include/asterisk/logger.h139
-rw-r--r--include/asterisk/manager.h148
-rw-r--r--include/asterisk/md5.h40
-rw-r--r--include/asterisk/module.h292
-rw-r--r--include/asterisk/monitor.h66
-rw-r--r--include/asterisk/musiconhold.h59
-rw-r--r--include/asterisk/netsock.h68
-rw-r--r--include/asterisk/options.h138
-rw-r--r--include/asterisk/paths.h43
-rw-r--r--include/asterisk/pbx.h915
-rw-r--r--include/asterisk/plc.h161
-rw-r--r--include/asterisk/poll-compat.h111
-rw-r--r--include/asterisk/privacy.h46
-rw-r--r--include/asterisk/res_odbc.h103
-rw-r--r--include/asterisk/rtp.h265
-rw-r--r--include/asterisk/say.h158
-rw-r--r--include/asterisk/sched.h198
-rw-r--r--include/asterisk/sha1.h81
-rw-r--r--include/asterisk/slinfactory.h57
-rw-r--r--include/asterisk/smdi.h195
-rw-r--r--include/asterisk/speech.h152
-rw-r--r--include/asterisk/srv.h45
-rw-r--r--include/asterisk/stringfields.h391
-rw-r--r--include/asterisk/strings.h304
-rw-r--r--include/asterisk/tdd.h82
-rw-r--r--include/asterisk/term.h76
-rw-r--r--include/asterisk/threadstorage.h498
-rw-r--r--include/asterisk/time.h144
-rw-r--r--include/asterisk/tonezone_compat.h35
-rw-r--r--include/asterisk/transcap.h42
-rw-r--r--include/asterisk/translate.h273
-rw-r--r--include/asterisk/udptl.h120
-rw-r--r--include/asterisk/ulaw.h43
-rw-r--r--include/asterisk/unaligned.h102
-rw-r--r--include/asterisk/utils.h578
-rw-r--r--include/jitterbuf.h162
-rw-r--r--include/solaris-compat/compat.h46
-rw-r--r--include/solaris-compat/sys/cdefs.h10
-rw-r--r--include/solaris-compat/sys/queue.h540
-rwxr-xr-xinstall-sh269
-rw-r--r--keys/freeworlddialup.pub6
-rw-r--r--keys/iaxtel.pub6
-rw-r--r--main/Makefile156
-rw-r--r--main/abstract_jb.c822
-rw-r--r--main/acl.c590
-rw-r--r--main/aescrypt.c317
-rw-r--r--main/aeskey.c469
-rw-r--r--main/aesopt.h1029
-rw-r--r--main/aestab.c232
-rw-r--r--main/alaw.c101
-rw-r--r--main/app.c1452
-rw-r--r--main/ast_expr2.c2859
-rw-r--r--main/ast_expr2.fl414
-rw-r--r--main/ast_expr2.h112
-rw-r--r--main/ast_expr2.y1045
-rw-r--r--main/ast_expr2f.c3349
-rw-r--r--main/asterisk.c3223
-rw-r--r--main/astmm.c498
-rw-r--r--main/astobj2.c771
-rw-r--r--main/audiohook.c721
-rw-r--r--main/autoservice.c319
-rw-r--r--main/buildinfo.c33
-rw-r--r--main/callerid.c1117
-rw-r--r--main/cdr.c1499
-rw-r--r--main/channel.c4876
-rw-r--r--main/chanvars.c87
-rw-r--r--main/cli.c2037
-rw-r--r--main/coef_in.h13
-rw-r--r--main/coef_out.h4
-rw-r--r--main/config.c1520
-rw-r--r--main/cryptostub.c95
-rw-r--r--main/db.c592
-rw-r--r--main/db1-ast/Makefile71
-rw-r--r--main/db1-ast/btree/bt_close.c182
-rw-r--r--main/db1-ast/btree/bt_conv.c221
-rw-r--r--main/db1-ast/btree/bt_debug.c329
-rw-r--r--main/db1-ast/btree/bt_delete.c657
-rw-r--r--main/db1-ast/btree/bt_get.c105
-rw-r--r--main/db1-ast/btree/bt_open.c458
-rw-r--r--main/db1-ast/btree/bt_overflow.c228
-rw-r--r--main/db1-ast/btree/bt_page.c100
-rw-r--r--main/db1-ast/btree/bt_put.c321
-rw-r--r--main/db1-ast/btree/bt_search.c213
-rw-r--r--main/db1-ast/btree/bt_seq.c460
-rw-r--r--main/db1-ast/btree/bt_split.c829
-rw-r--r--main/db1-ast/btree/bt_utils.c260
-rw-r--r--main/db1-ast/btree/btree.h391
-rw-r--r--main/db1-ast/btree/extern.h70
-rw-r--r--main/db1-ast/db/db.c103
-rw-r--r--main/db1-ast/hash/README72
-rw-r--r--main/db1-ast/hash/extern.h65
-rw-r--r--main/db1-ast/hash/hash.c999
-rw-r--r--main/db1-ast/hash/hash.h293
-rw-r--r--main/db1-ast/hash/hash_bigkey.c668
-rw-r--r--main/db1-ast/hash/hash_buf.c355
-rw-r--r--main/db1-ast/hash/hash_func.c225
-rw-r--r--main/db1-ast/hash/hash_log2.c56
-rw-r--r--main/db1-ast/hash/hash_page.c946
-rw-r--r--main/db1-ast/hash/hsearch.c107
-rw-r--r--main/db1-ast/hash/ndbm.c235
-rw-r--r--main/db1-ast/hash/page.h92
-rw-r--r--main/db1-ast/hash/search.h51
-rw-r--r--main/db1-ast/include/circ-queue.h131
-rw-r--r--main/db1-ast/include/compat.h49
-rw-r--r--main/db1-ast/include/db.h250
-rw-r--r--main/db1-ast/include/mpool.h115
-rw-r--r--main/db1-ast/include/ndbm.h79
-rw-r--r--main/db1-ast/libdb.map11
-rw-r--r--main/db1-ast/mpool/README7
-rw-r--r--main/db1-ast/mpool/mpool.c498
-rw-r--r--main/db1-ast/recno/extern.h54
-rw-r--r--main/db1-ast/recno/rec_close.c183
-rw-r--r--main/db1-ast/recno/rec_delete.c197
-rw-r--r--main/db1-ast/recno/rec_get.c311
-rw-r--r--main/db1-ast/recno/rec_open.c241
-rw-r--r--main/db1-ast/recno/rec_put.c280
-rw-r--r--main/db1-ast/recno/rec_search.c126
-rw-r--r--main/db1-ast/recno/rec_seq.c131
-rw-r--r--main/db1-ast/recno/rec_utils.c122
-rw-r--r--main/db1-ast/recno/recno.h39
-rw-r--r--main/devicestate.c369
-rw-r--r--main/dial.c894
-rw-r--r--main/dns.c292
-rw-r--r--main/dnsmgr.c424
-rw-r--r--main/dsp.c1819
-rw-r--r--main/ecdisa.h15
-rw-r--r--main/editline/CHANGES42
-rw-r--r--main/editline/INSTALL64
-rw-r--r--main/editline/Makefile.in234
-rw-r--r--main/editline/PLATFORMS13
-rw-r--r--main/editline/README11
-rw-r--r--main/editline/TEST/test.c268
-rw-r--r--main/editline/chared.c695
-rw-r--r--main/editline/chared.h159
-rw-r--r--main/editline/common.c951
-rwxr-xr-xmain/editline/config.guess1449
-rw-r--r--main/editline/config.h.in21
-rwxr-xr-xmain/editline/config.sub1412
-rwxr-xr-xmain/editline/configure2459
-rw-r--r--main/editline/configure.in278
-rw-r--r--main/editline/editline.3646
-rw-r--r--main/editline/editrc.5491
-rw-r--r--main/editline/el.c509
-rw-r--r--main/editline/el.h145
-rw-r--r--main/editline/emacs.c488
-rw-r--r--main/editline/hist.c197
-rw-r--r--main/editline/hist.h80
-rw-r--r--main/editline/histedit.h197
-rw-r--r--main/editline/history.c875
-rwxr-xr-xmain/editline/install-sh250
-rw-r--r--main/editline/key.c687
-rw-r--r--main/editline/key.h79
-rw-r--r--main/editline/makelist.in254
-rw-r--r--main/editline/map.c1418
-rw-r--r--main/editline/map.h79
-rw-r--r--main/editline/np/fgetln.c88
-rw-r--r--main/editline/np/strlcat.c75
-rw-r--r--main/editline/np/strlcpy.c75
-rw-r--r--main/editline/np/unvis.c322
-rw-r--r--main/editline/np/vis.c348
-rw-r--r--main/editline/np/vis.h96
-rw-r--r--main/editline/parse.c259
-rw-r--r--main/editline/parse.h52
-rw-r--r--main/editline/prompt.c174
-rw-r--r--main/editline/prompt.h62
-rw-r--r--main/editline/read.c558
-rw-r--r--main/editline/read.h55
-rw-r--r--main/editline/readline.c1664
-rw-r--r--main/editline/readline/readline.h118
-rw-r--r--main/editline/refresh.c1104
-rw-r--r--main/editline/refresh.h63
-rw-r--r--main/editline/search.c649
-rw-r--r--main/editline/search.h70
-rw-r--r--main/editline/sig.c198
-rw-r--r--main/editline/sig.h72
-rw-r--r--main/editline/sys.h133
-rw-r--r--main/editline/term.c1587
-rw-r--r--main/editline/term.h124
-rw-r--r--main/editline/tokenizer.c397
-rw-r--r--main/editline/tokenizer.h54
-rw-r--r--main/editline/tty.c1182
-rw-r--r--main/editline/tty.h484
-rw-r--r--main/editline/vi.c941
-rw-r--r--main/enum.c671
-rw-r--r--main/file.c1392
-rw-r--r--main/fixedjitterbuf.c351
-rw-r--r--main/fixedjitterbuf.h92
-rw-r--r--main/frame.c1607
-rw-r--r--main/fskmodem.c309
-rw-r--r--main/global_datastores.c113
-rw-r--r--main/http.c752
-rw-r--r--main/image.c225
-rw-r--r--main/indications.c597
-rw-r--r--main/io.c371
-rw-r--r--main/jitterbuf.c839
-rw-r--r--main/loader.c1001
-rw-r--r--main/logger.c939
-rw-r--r--main/manager.c3194
-rw-r--r--main/md5.c267
-rw-r--r--main/netsock.c217
-rw-r--r--main/pbx.c6401
-rw-r--r--main/plc.c251
-rw-r--r--main/poll.c306
-rw-r--r--main/privacy.c119
-rw-r--r--main/rtp.c3833
-rw-r--r--main/say.c7285
-rw-r--r--main/sched.c404
-rw-r--r--main/sha1.c385
-rw-r--r--main/slinfactory.c177
-rw-r--r--main/srv.c246
-rw-r--r--main/stdtime/Makefile29
-rw-r--r--main/stdtime/localtime.c1651
-rw-r--r--main/stdtime/private.h358
-rw-r--r--main/stdtime/test.c21
-rw-r--r--main/stdtime/tzfile.h184
-rw-r--r--main/strcompat.c472
-rw-r--r--main/tdd.c328
-rw-r--r--main/term.c303
-rw-r--r--main/threadstorage.c245
-rw-r--r--main/translate.c986
-rw-r--r--main/udptl.c1247
-rw-r--r--main/ulaw.c106
-rw-r--r--main/utils.c1414
-rw-r--r--makeopts.in194
-rwxr-xr-xmissing198
-rwxr-xr-xmkinstalldirs40
-rw-r--r--pbx/Makefile61
-rw-r--r--pbx/ael/ael-test/ael-ntest10/extensions.ael131
-rw-r--r--pbx/ael/ael-test/ael-ntest12/extensions.ael13
-rw-r--r--pbx/ael/ael-test/ael-ntest22/extensions.ael7
-rw-r--r--pbx/ael/ael-test/ael-ntest22/qq.ael6
-rw-r--r--pbx/ael/ael-test/ael-ntest22/t1/a.ael4
-rw-r--r--pbx/ael/ael-test/ael-ntest22/t1/b.ael6
-rw-r--r--pbx/ael/ael-test/ael-ntest22/t1/c.ael13
-rw-r--r--pbx/ael/ael-test/ael-ntest22/t2/d.ael4
-rw-r--r--pbx/ael/ael-test/ael-ntest22/t2/e.ael6
-rw-r--r--pbx/ael/ael-test/ael-ntest22/t2/f.ael9
-rw-r--r--pbx/ael/ael-test/ael-ntest22/t3/g.ael4
-rw-r--r--pbx/ael/ael-test/ael-ntest22/t3/h.ael6
-rw-r--r--pbx/ael/ael-test/ael-ntest22/t3/i.ael4
-rw-r--r--pbx/ael/ael-test/ael-ntest22/t3/j.ael6
-rw-r--r--pbx/ael/ael-test/ael-ntest23/extensions.ael7
-rw-r--r--pbx/ael/ael-test/ael-ntest23/qq.ael6
-rw-r--r--pbx/ael/ael-test/ael-ntest23/t1/a.ael4
-rw-r--r--pbx/ael/ael-test/ael-ntest23/t1/b.ael6
-rw-r--r--pbx/ael/ael-test/ael-ntest23/t1/c.ael13
-rw-r--r--pbx/ael/ael-test/ael-ntest23/t2/d.ael4
-rw-r--r--pbx/ael/ael-test/ael-ntest23/t2/e.ael6
-rw-r--r--pbx/ael/ael-test/ael-ntest23/t2/f.ael9
-rw-r--r--pbx/ael/ael-test/ael-ntest23/t3/g.ael4
-rw-r--r--pbx/ael/ael-test/ael-ntest23/t3/h.ael6
-rw-r--r--pbx/ael/ael-test/ael-ntest23/t3/i.ael4
-rw-r--r--pbx/ael/ael-test/ael-ntest23/t3/j.ael6
-rwxr-xr-xpbx/ael/ael-test/ael-ntest9/extensions.ael12
-rw-r--r--pbx/ael/ael-test/ael-test1/extensions.ael163
-rw-r--r--pbx/ael/ael-test/ael-test11/extensions.ael56
-rw-r--r--pbx/ael/ael-test/ael-test14/extensions.ael20
-rw-r--r--pbx/ael/ael-test/ael-test15/extensions.ael17
-rw-r--r--pbx/ael/ael-test/ael-test16/extensions.ael4
-rw-r--r--pbx/ael/ael-test/ael-test18/extensions.ael40
-rw-r--r--pbx/ael/ael-test/ael-test19/extensions.ael377
-rw-r--r--pbx/ael/ael-test/ael-test2/apptest.ael2146
-rw-r--r--pbx/ael/ael-test/ael-test2/extensions.ael8
-rw-r--r--pbx/ael/ael-test/ael-test20/extensions.ael8
-rwxr-xr-xpbx/ael/ael-test/ael-test3/extensions.ael3183
-rw-r--r--pbx/ael/ael-test/ael-test3/include1.ael23
-rw-r--r--pbx/ael/ael-test/ael-test3/include2.ael24
-rw-r--r--pbx/ael/ael-test/ael-test3/include3.ael22
-rw-r--r--pbx/ael/ael-test/ael-test3/include4.ael22
-rw-r--r--pbx/ael/ael-test/ael-test3/include5.ael21
-rwxr-xr-xpbx/ael/ael-test/ael-test3/telemarket_torture.ael2812
-rw-r--r--pbx/ael/ael-test/ael-test4/apptest.ael2146
-rw-r--r--pbx/ael/ael-test/ael-test4/extensions.ael8
-rw-r--r--pbx/ael/ael-test/ael-test5/extensions.ael833
-rw-r--r--pbx/ael/ael-test/ael-test6/extensions.ael833
-rw-r--r--pbx/ael/ael-test/ael-test7/extensions.ael460
-rw-r--r--pbx/ael/ael-test/ael-test8/extensions.ael27
-rwxr-xr-xpbx/ael/ael-test/ael-vtest13/extensions.ael3183
-rw-r--r--pbx/ael/ael-test/ael-vtest13/include1.ael23
-rw-r--r--pbx/ael/ael-test/ael-vtest13/include2.ael24
-rw-r--r--pbx/ael/ael-test/ael-vtest13/include3.ael22
-rw-r--r--pbx/ael/ael-test/ael-vtest13/include4.ael22
-rw-r--r--pbx/ael/ael-test/ael-vtest13/include5.ael21
-rwxr-xr-xpbx/ael/ael-test/ael-vtest13/telemarket_torture.ael2812
-rw-r--r--pbx/ael/ael-test/ael-vtest17/extensions.ael116
-rw-r--r--pbx/ael/ael-test/ael-vtest21/extensions.ael14
-rw-r--r--pbx/ael/ael-test/ael-vtest25/extensions.ael8
-rw-r--r--pbx/ael/ael-test/ref.ael-ntest10163
-rw-r--r--pbx/ael/ael-test/ref.ael-ntest1231
-rw-r--r--pbx/ael/ael-test/ref.ael-ntest2255
-rw-r--r--pbx/ael/ael-test/ref.ael-ntest2325
-rw-r--r--pbx/ael/ael-test/ref.ael-ntest924
-rw-r--r--pbx/ael/ael-test/ref.ael-test115
-rw-r--r--pbx/ael/ael-test/ref.ael-test1114
-rw-r--r--pbx/ael/ael-test/ref.ael-test1412
-rw-r--r--pbx/ael/ael-test/ref.ael-test1512
-rw-r--r--pbx/ael/ael-test/ref.ael-test1613
-rw-r--r--pbx/ael/ael-test/ref.ael-test1812
-rw-r--r--pbx/ael/ael-test/ref.ael-test1914
-rw-r--r--pbx/ael/ael-test/ref.ael-test221
-rw-r--r--pbx/ael/ael-test/ref.ael-test2012
-rw-r--r--pbx/ael/ael-test/ref.ael-test318
-rw-r--r--pbx/ael/ael-test/ref.ael-test421
-rw-r--r--pbx/ael/ael-test/ref.ael-test512
-rw-r--r--pbx/ael/ael-test/ref.ael-test616
-rw-r--r--pbx/ael/ael-test/ref.ael-test714
-rw-r--r--pbx/ael/ael-test/ref.ael-test812
-rw-r--r--pbx/ael/ael-test/ref.ael-vtest133027
-rw-r--r--pbx/ael/ael-test/ref.ael-vtest1771
-rw-r--r--pbx/ael/ael-test/ref.ael-vtest219
-rw-r--r--pbx/ael/ael-test/ref.ael-vtest257
-rwxr-xr-xpbx/ael/ael-test/runtests56
-rwxr-xr-xpbx/ael/ael-test/setref7
-rw-r--r--pbx/ael/ael.flex904
-rw-r--r--pbx/ael/ael.tab.c3443
-rw-r--r--pbx/ael/ael.tab.h160
-rw-r--r--pbx/ael/ael.y880
-rw-r--r--pbx/ael/ael_lex.c3462
-rw-r--r--pbx/dundi-parser.c831
-rw-r--r--pbx/dundi-parser.h88
-rw-r--r--pbx/pbx_ael.c4959
-rw-r--r--pbx/pbx_config.c2567
-rw-r--r--pbx/pbx_dundi.c4682
-rw-r--r--pbx/pbx_gtkconsole.c515
-rw-r--r--pbx/pbx_loopback.c191
-rw-r--r--pbx/pbx_realtime.c258
-rw-r--r--pbx/pbx_spool.c532
-rw-r--r--res/Makefile39
-rw-r--r--res/res_adsi.c1132
-rw-r--r--res/res_agi.c2226
-rw-r--r--res/res_clioriginate.c175
-rw-r--r--res/res_config_odbc.c564
-rw-r--r--res/res_config_pgsql.c842
-rw-r--r--res/res_convert.c224
-rw-r--r--res/res_crypto.c631
-rw-r--r--res/res_features.c2755
-rw-r--r--res/res_indications.c406
-rw-r--r--res/res_jabber.c2518
-rw-r--r--res/res_monitor.c714
-rw-r--r--res/res_musiconhold.c1480
-rw-r--r--res/res_odbc.c741
-rw-r--r--res/res_smdi.c1384
-rw-r--r--res/res_snmp.c115
-rw-r--r--res/res_speech.c404
-rw-r--r--res/snmp/agent.c823
-rw-r--r--res/snmp/agent.h40
-rw-r--r--sample.call84
-rw-r--r--sounds/Makefile158
-rw-r--r--sounds/sounds.xml68
-rw-r--r--static-http/ajamdemo.html219
-rw-r--r--static-http/astman.css34
-rw-r--r--static-http/astman.js262
-rw-r--r--static-http/prototype.js1781
-rw-r--r--utils/Makefile132
-rw-r--r--utils/ael_main.c564
-rw-r--r--utils/astman.1102
-rw-r--r--utils/astman.c755
-rw-r--r--utils/check_expr.c355
-rw-r--r--utils/expr2.testinput92
-rw-r--r--utils/frame.c1092
-rw-r--r--utils/frame.h300
-rw-r--r--utils/muted.c709
-rw-r--r--utils/smsq.c768
-rw-r--r--utils/stereorize.c159
-rw-r--r--utils/streamplayer.c124
933 files changed, 452519 insertions, 0 deletions
diff --git a/.cleancount b/.cleancount
new file mode 100644
index 000000000..bb95160cb
--- /dev/null
+++ b/.cleancount
@@ -0,0 +1 @@
+33
diff --git a/BUGS b/BUGS
new file mode 100644
index 000000000..96b55e655
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,22 @@
+Asterisk Bug Tracking Information
+=================================
+
+To learn about and report Asterisk bugs, please visit
+the official Asterisk Bug Tracker at:
+
+ http://bugs.digium.com
+
+For more information on using the bug tracker, or to
+learn how you can contribute by acting as a bug marshal
+please see:
+
+ http://www.asterisk.org/developers/bug-guidelines
+
+If you would like to submit a feature request, please
+resist the temptation to post it to the bug tracker.
+Feature requests should be posted to the asterisk-dev
+mailing list, located at:
+
+ http://lists.digium.com
+
+Thank you!
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 000000000..699674279
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,370 @@
+Changes since Asterisk 1.2:
+
+ * over 4,000 commits since 1.2
+ * queue member naming
+ * CLI commands rework
+ o Change the way CLI commands are structured.
+ o Most commands are now <module> <verb> <args>
+ * chan_h323 update
+ * RTP packetization
+ * SLA (Shared Line Appearance) support
+ * T.38 Passthrough Support for faxing in SIP
+ * Generic channel jitterbuffer (spawned from RTP)
+ * Variable Length DTMF for better DTMF compatibility
+ * Improved chan_iax2 scalability by using multithreading
+ * AEL2 has replaced the original implementation of AEL. The "2" is removed. For more details,
+ read: http://www.voip-info.org/wiki/view/Asterisk+AEL2
+ AEL is no longer considered experimental.
+ * New sounds; English, Spanish, and French prompts, as well as music on hold files, in
+ multiple Asterisk native formats.
+ * IMAP storage of voicemail
+ * Jabber/GoogleTalk integration
+ * New speech recognition API for interfacing to different Voice Recognition software packages
+ * much more customizable and portable build system
+ o also for asterisk-addons
+ * Radius CDR logging
+ * SNMP support
+ * SMDI (Simplified Message Desk Interface) support
+ * Redesign of MusicOnHold configuration settings
+ * Manager over HTTP
+ * Significant chan_skinny updates
+ * Significant chan_misdn updates
+ * Improved SIP transfers
+ * SIP MWI subscription support
+ * Much improved support for SIP video
+ * Control over SIP transfers and subscriptions (enable/disable per device)
+ * ChanSpy whisper mode (Whisper Paging)
+ * Configurable language support for saying dates and times
+ * Significant architecture improvements for memory usage and performance
+ * Media-only IAX2 transfers
+ * Updates to the Radio Repeater app code
+ * Deprecation of AgentCallbackLogin in favor of a dialplan-based solution
+ * uClibc builds supported
+ * Work done for freeBSD portability
+ * Work done for Solaris portability
+ * FreeTDS-based database can be used with Realtime
+ * New internal data structure, stringfields, is implemented in IAX and SIP, reducing memory consumption by about 50%.
+ * Use of thread local storage for reduced memory allocation/freeing and lower stack consumption
+ * Reorganized files into docs/ main/ configs/, including name changes in some cases
+ * Much effort was expended in arranging documentation in source files in doxygen format
+ * Improved IP TOS support for IAX and SIP
+ * Builtin mini HTTP server
+ * Added support for Sigma Designs cards.
+ * Frame header caching to reduce memory allocation/freeing
+ * Passthrough and record/playback support for G.722 wideband audio
+ * using mpg123 to play MP3 files for music-on-hold will be deprecated in 1.4 (start using the "native support")
+ * New Apps:
+ 1. AMD() ;; Answering Machine Detection
+ 2. ChannelRedirect() ;; asynch goto, redirect chan to context/exten/priority
+ 3. ContinueWhile() ;; Addition to the While() suite. Acts like "continue".
+ 4. ExitWhile() ;; Addition to the While() suite. Acts like "break".
+ 5. ExtenSpy() ;; A close cousin to ChanSpy().
+ 6. FollowMe() ;; findme/followme call redirect app
+ 7. Log() ;; Send a message to the log, based on severity level.
+ 8. MacroExclusive() ;; No more than one invocation of this macro allowed at any one time.
+ 9. MorseCode() ;; turns strings into dits and dahs. A playground for ham radio licensees!
+ 10. OSPAuth() ;; OSP authentication
+ 11. QueueLog() ;; allows you to write your own events into the queue log
+ 12. SLAStation() ;; Shared Line Appearance
+ 13. SLATrunk() ;; Shared Line Appearance
+ 14. SpeechCreate() ;; Voice Recognition Engine interface...
+ 15. SpeechActivateGrammar()
+ 16. SpeechStart()
+ 17. SpeechBackground
+ 18. SpeechDeactivateGrammar()
+ 19. SpeechProcessingSound()
+ 20. SpeechDestroy()
+ 21. SpeechLoadGrammar()
+ 22. SpeechUnloadGrammar()
+ 23. StopMixMonitor() ;; to stop the MixMonitor App.
+ 24. TryExec() ;; execute dialplan app without fatal consequences
+ * Apps removed:
+ 1. CheckGroup -- do a comparison to ${GROUP()}
+ 2. Curl -- use the function CURL() instead
+ 3. Cut -- use the function CUT() instead
+ 4. DateTime -- use sayunixtime() app instead.
+ 5. DBget -- deprecated in 1.2, now removed.
+ 6. DBput -- deprecated in 1.2, now removed.
+ 7. Enumlookup -- use the function ENUMLOOKUP() instead
+ 8. Eval -- use the function EVAL() instead
+ 9. GetGroupCount -- use the function GROUP_COUNT() instead
+ 10. GetGroupMatchCount -- use the function GROUP_MATCH_COUNT() instead
+ 11. Intercom -- use the chan_oss module instead
+ 12. Math -- use the function MATH() instead
+ 13. MD5 -- use the function MD5() instead
+ 14. SetCIDname -- use the function CALLERID(name) instead
+ 15. SetCIDnum -- use the function CALLERID(number) instead
+ 16. SetGroup -- use Set(GROUP=group) instead
+ 17. SetRDNIS -- use the function CALLERID(rdnis) instead
+ 18. Sql_postgres -- was deprecated in 1.2, now removed
+ 19. Txtcidname -- use the function TXTCIDNAME instead
+ * New Dialplan Functions:
+ 1. ARRAY()
+ 2. BASE_64_DECODE()
+ 3. BASE_64_ENCODE()
+ 4. CHANNEL()
+ 5. CURL()
+ 6. CUT()
+ 7. DB_DELETE()
+ 8. FILTER()
+ 9. GLOBAL()
+ 10. IFTIME()
+ 11. KEYPADHASH()
+ 12. ODBC()
+ 13. QUOTE()
+ 14. RAND()
+ 15. REALTIME()
+ 16. SHA1()
+ 17. SORT()
+ 18. SPRINTF()
+ 19. SQL_ESC()
+ 20. STAT()
+ 21. STRPTIME()
+ 22. AUDIOHOOK_INHERIT()
+ * Apps that have changes to their interface:
+ 1. Authenticate() -- optional maxdigits argument added.
+ 2. ChanSpy() -- new options:
+ o w -- Enable 'whisper' mode, so the spying channel can talk to...
+ o W -- Enable 'private whisper' mode, so the spying channel can...
+ 3. DBdel() -- now marked as DEPRECATED in favor of the DB_DELETE func
+ 4. Dial()
+ o New Option: O([x]) for Zaptel operator mode
+ o New Option: K/k parking via dtmf tones
+ 5. Dictate() -- optional filename argument added.
+ 6. Directory() -- new option: e - In addition to the name, also read the extension number...
+ 7. ForkCDR() -- new options:
+ o 'a' -- update answer time on new cdr
+ o 'A' -- Lock the orig CDR answer time against changes.
+ o 'D' -- Copy the disposition from the orig to the new CDR.
+ o 'd' -- clear the dstcannel field in the new CDR.
+ o 'e' -- set the end time of the original CDR.
+ o 'R' -- do NOT reset the new CDR.
+ o 's' -- Add/change var in orig CDR.
+ o 'T' -- Force ast_cdr_end, answer to obey LOCKED flag for the orig. CDR.
+ -- ast_cdr_setvar will be forced also (used by the CDR() func in write mode)
+ 8. Meetme() -- new options:
+ o 'I' -- announce user join/leave without review
+ o 'l' -- set listen only mode (Listen only, no talking)
+ o 'o' -- set talker optimization - treats talkers who aren't speaking as...
+ o '1' -- do not play message when first person enters
+ 9. MeetmeAdmin() -- new options:
+ o 'r' -- Reset one user's volume settings
+ o 'R' -- Reset all users volume settings
+ o 's' -- Lower entire conference speaking volume
+ o 'S' -- Raise entire conference speaking volume
+ o 't' -- Lower one user's talk volume
+ o 'T' -- Lower all users talk volume
+ o 'u' -- Lower one user's listen volume
+ o 'U' -- Lower all users listen volume
+ o 'v' -- Lower entire conference listening volume
+ o 'V' -- Raise entire conference listening volume
+ 10. OSPFinish() : now also can return ERROR result.
+ 11. OSPLookup() : Sets more variables, also now returns ERROR result.
+ 12. Page() -- New option: r - record the page into a file (see 'r' for app_meetme)
+ 13. Pickup() -- multiple extensions, PICKUPMARK; read the description!
+ 14. Queue()
+ o New Argument: AGI
+ o New option: i
+ 15. Random() -- is now deprecated in 1.4
+ 16. Read() -- replace 'skip' and 'noanswer' options with 's', 'n', add 'i' option.
+ 17. Record() -- New option: 'x' : ignore all terminator keys (DTMF) and keep recording until hangup
+ 18. UserEvent() -- slight change in behavior. Read the description.
+ 19. VoiceMailMain() -- new a(#) option, goes to folder # directly.
+ 20. WaitForSilence() -- new optional 3rd arg, time delay before returning.
+ * Functions that have changes to their interfaces:
+ 1. CDR -- new options: u and s
+ 2. LANGUAGE -- Deprecated. Use CHANNEL(language) instead.
+ 3. MUSICCLASS -- Deprecated. Use CHANNEL(musicclass) instead.
+ * Configuration File Changes:
+ 1. NEW config files:
+ 1. amd.conf -- Answering Machine Detection parameters
+ 2. followme.conf -- parameters for the findme/followme call forwarding
+ 3. func_odbc.conf -- define sql access functions here
+ 4. gtalk.conf -- how to handle gtalk protocol calls
+ 5. h323.conf -- h323 configuration
+ 6. http.conf -- config for the builtin mini-http server in asterisk
+ 7. jabber.conf -- jabber interface
+ 8. jingle.conf -- jingle protocol interface config
+ 10. res_snmp.conf -- to enable snmp in asterisk, and define full/sub agent status
+ 11. say.conf -- define per-language rules for numbers, dates, etc.
+ 12. skinny.conf -- for those special skinny phones you want to use...
+ 13. sla.conf -- Shared Line Appearance config
+ 14. smdi.conf -- SMDI messaging config
+ 15. udptl.conf -- T38's udptl transport config
+ 16. users.conf -- user config
+ 2. Changes to Existing Config files:
+ 1. In General:
+ o Jitterbuffer support added to several channels. Usually adds these variables to a config file:
+ 1. jbenable
+ 2. jbmaxsize
+ 3. jbresyncthreshold
+ 4. jbimpl
+ 5. jblog
+ o MusicOnHold upgrade introduces two new variables:
+ 1. mohinterpret
+ 2. mohsuggest
+ 2. agents.conf
+ o multiplelogin variable added
+ o maxlogintries variable added
+ o autologoffunavail variable added
+ o endcall variable added
+ o goodbye variable added
+ o createlink variable REMOVED
+ 3. alsa.conf
+ o mohinterpret variable added
+ o Jitterbuffer variables added
+ 4. cdr.conf
+ o endbeforehexten variable added
+ o sections for csv and radius added, with variables usegmtime, loguniqueid,
+ loguserfield, and radiuscfg variables.
+ 5. cdr_tds.conf
+ o table variable added
+ 6. extensions.ael
+ o Many upgrades. See the info at http://www.voip-info.org/wiki/view/Asterisk+AEL2
+ 7. extensions.conf
+ o autofallthru now set to "yes" by default
+ o userscontext variable added
+ o added info/examples on paging and hints.
+ 8. features.conf
+ o parkedplay variable added (who to beep at)
+ o parkedmusicclass
+ o atxfernoanswertimeout variable added
+ o parkcall variable added (one step parking)
+ o improved documentation for dynamic feature declarations!
+ o added parkedcallltransfers option to control builtin transfers with parking
+ 9. iax.conf
+ o adsi variable added
+ o mohinterpret variable added
+ o mohsuggest variable added
+ o jitterbuffer updates
+ o iaxthreadcount variable added
+ o iaxmaxthreadcount variable added
+ o the way to specify TOS has changed.
+ o mailboxdetail variable has been REMOVED.
+ 10. indications.conf
+ o [bg] entry added (Bulgaria).
+ o [il] entry added (Israel)
+ o [in] entry added (India)
+ o [jp] entry added (Japan)
+ o [my] entry added (Malaysia)
+ o [th] entry added (Thailand)
+ 11. manager.conf
+ o webenabled variable added
+ o httptimeout variable added
+ o timestampevents variable added
+ 12. mgcp.conf
+ o Jitterbuffer support added
+ 13. misdn.conf
+ o l1watcher_timeout variable added
+ o pp_l2_check variable added
+ o echocancelwhenbridged variable added
+ o echotraining variable added
+ o max_incoming variable added
+ o max_outgoing variable added
+ 14. modules.conf
+ o a comment for preloading res_speech.so is added
+ o mention of global symbols is removed
+ o obsolesced entries for chan_modem_* and app_intercom have been removed
+ 15. musiconhold.conf
+ o the default is now to do native moh from /var/lib/asterisk/moh
+ 16. osp.conf
+ o authpolicy variable added
+ 17. oss.conf
+ o debug variable added
+ o device variable added
+ o mixer variable added
+ o boost variable added
+ o callerid variable added
+ o autohangup variable added
+ o queuesize variable added
+ o frags variable added
+ o JitterBuffer support
+ o sections to define alternate sound cards
+ 18. queues.conf
+ o autofill variable added
+ o monitor-type variable added
+ o musiconhold is now musicclass, with a difference in interpretation
+ o autofill variable added
+ o autopause variable added
+ o setinterfacevar variable added
+ o ringinuse variable added
+ 19. res_odbc.conf
+ o pooling variable added
+ 20. rpt.conf
+ o duplex variable added
+ o tailmessagetime variable added
+ o tailsquashedtime variable added
+ o tailmessages variable added
+ 21. rtp.conf
+ o rtcpinterval varaible added
+ 22. sip.conf
+ o allowguest variable can't be set to 'osp'
+ o allowoverlap variable added
+ o allowtransfer variable added
+ o limitonpeer variable added
+ o directrtpsetup variable added
+ o buggymwi variable added
+ o ospauth variable REMOVED
+ o notifyhold variable added
+ o autoframing variable added
+ o tos variable REMOVED
+ o tos_sip variable added
+ o tos_audio variable added
+ o tos_video variable added
+ o minexpiry variable added
+ o t1min variable added
+ o musicclass variable REMOVED
+ o mohinterpret variable added
+ o mohsuggest variable added
+ o allowsubscribe variable added
+ o videosupport variable added
+ o maxcallbitrate variable added
+ o g726nonstandard variable added
+ o dumphistory variable added
+ o t38pt_udptl variable added
+ o t38pt_rtp variable added
+ o t38pt_tcp variable added
+ o rfc2833compensate variable added
+ o matchexterniplocally variable added
+ o canreinvite variable can also now be set to 'nonat'
+ o rtsavesysname variable added
+ o JitterBuffer support added
+ o t38pt_usertpsource variable added
+ o regcontext variable can contains multiple contexts separated by an '&'
+ 23. skinny.conf
+ o port variable renamed to bindport
+ o JitterBuffer support added
+ o model variable REMOVED
+ o mohinterpret variable added
+ o mohsuggest variable added
+ o speeddial variable added
+ o addon variable added
+ 24. voicemail.conf
+ o userscontext variable added
+ o smdiport variable added
+ o attachfmt variable added
+ o volgain variable added
+ o tempgreetwarn variable added
+ 25. zapata.conf
+ o pritimer variable has improved documentation
+ o New signalling method: fgccama
+ o New signalling method: fgccamamf
+ o outsignalling variable added
+ o distinctiveringaftercid variable added
+ o cidsignalling now also accepts v23_jp, and smdi
+ o usesmdi variable added
+ o smdiport variable added
+ o mohinterpret variable added
+ o mohsuggest variable added
+ o JitterBuffer support added
+ * Removed Codecs/Channels:
+ 1. codec_g723 was removed because the actual codec implementation it was designed to use is not distributable
+ 2. chan_modem_* and related modules are gone because the kernel support for those interfaces is old, buggy and unsupported
+ * New Utils:
+ 1. aelparse -- compile .ael files outside of asterisk
+ * New manager events:
+ 1. OriginateResponse event comes to replace OriginateSuccess and OriginateFailure
+ * iLBC source code no longer included (see UPGRADE.txt for details)
+ * New CLI command "pri show version" that shows the current version of libpri
+ that the library was built against (requires a version of libpri since this API
+ feature was added).
diff --git a/COPYING b/COPYING
new file mode 100644
index 000000000..aa2ebac66
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,341 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 000000000..a51638214
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,215 @@
+
+=== DEVELOPMENT SUPPORT ===
+We'd like to thank the following companies for helping fund development of
+Asterisk:
+
+Pilosoft, Inc. - for supporting ADSI development in Asterisk
+
+Asterlink, Inc. - for supporting broad Asterisk development
+
+GFS - for supporting ALSA development
+
+Telesthetic - for supporting SIP development
+
+Christos Ricudis - for substantial code contributions
+
+nic.at - ENUM support in Asterisk
+
+Paul Bagyenda, Digital Solutions - for initial Voicetronix driver development
+
+=== WISHLIST CONTRIBUTERS ===
+Jeremy McNamara - SpeeX support
+Nick Seraphin - RDNIS support
+Gary - Phonejack ADSI (in progress)
+Wasim - Hangup detect
+
+=== HARDWARE DONORS ===
+* Thanks to QuickNet Technologies for their donation of an Internet
+PhoneJack and Linejack card to the project. (http://www.quicknet.net)
+
+* Thanks to VoipSupply for their donation of Sipura ATAs to the project for
+T.38 testing. (http://www.voipsupply.com)
+
+* Thanks to Grandstream for their donation of ATAs to the project for
+T.38 testing. (http://www.grandstream.com)
+
+=== MISCELLANEOUS PATCHES ===
+Jim Dixon - Zapata Telephony and app_rpt
+ http://www.zapatatelephony.org/app_rpt.html
+
+Russell Bryant - Asterisk 1.0 maintainer and misc. enhancements
+ russelb@clemson.edu
+
+Anthony Minessale II - Countless big and small fixes, and relentless forward push
+ ChanSpy, ForkCDR, ControlPlayback, While/EndWhile, DumpChan, Dictate,
+ MacroIf, ExecIf, ExecIfTime, RetryDial, MixMonitor applications; many realtime
+ concepts and implementation pieces, including res_config_odbc; format_slin;
+ cdr_custom; several features in Dial including L(), G() and enhancements to
+ M() and D(); several CDR enhancements including CDR variables; attended
+ transfer; one touch record; native MOH; manager eventmask; command line '-t'
+ flag to allow recording/voicemail on nfs shares; #exec command and multiline
+ comments in config files; setvar in iax and sip configs.
+ anthmct@yahoo.com http://www.asterlink.com
+
+James Golovich - Innumerable contributions
+ You can find him and asterisk-perl at http://asterisk.gnuinter.net
+
+Andre Bierwirth - Extension hints and status
+
+Oliver Daudey - ISDN4Linux fixes
+
+Pauline Middelink - ISDN4Linux patches and some general patches.
+ She can be found at http://www.polyware.nl/~middelink/En/
+
+Jean-Denis Girard - Various contributions from the South Pacific Islands
+ jd-girard@esoft.pf http://www.esoft.pf
+
+William Jordan / Vonage - MySQL enhancements to Voicemail
+ wjordan@vonage.com
+
+Jac Kersing - Various fixes
+
+Steven Critchfield - Seek and Trunc functions for playback and recording
+ critch@basesys.com
+
+Jefferson Noxon - app_lookupcidname, app_db, and various other contributions
+
+Klaus-Peter Junghanns - in-band DTMF on SIP and MGCP
+
+Ross Finlayson - Dynamic RTP payload support
+
+Mahmut Fettahlioglu - Audio recording, music-on-hold changes, alaw file
+ format, and various fixes. Can be contacted at mahmut@oa.com.au
+
+James Dennis - Cisco SIP compatibility patches to work with SIP service
+ providers. Can be contacted at asterisk@jdennis.net
+
+Tilghman Lesher - ast_localtime(); ast_say_date_with_format();
+ GotoIfTime, Random, SayUnixTime, HasNewVoicemail applications;
+ CUT, SORT, EVAL, CURL, FIELDQTY, STRFTIME, QUEUEAGENT* functions;
+ and other innumerable bug fixes. http://asterisk.drunkcoder.com/
+
+Jayson Vantuyl - Manager protocol changes, various other bugs.
+ jvantuyl@computingedge.net
+
+Thorsten Lockert - OpenBSD, FreeBSD ports, making MacOS X port run on 10.3,
+ dialplan include verification, route lookup on OpenBSD, SNMP agent
+ support (res_snmp), various other bugs. tholo@sigmasoft.com
+
+Brian West - ODBC support and Bug Marshaling
+
+Josh Roberson - chan_zap reload support, Advanced Voicemail Features, other misc. patches,
+ and Bug Marshalling. - josh@asteriasgi.com, http://www.asteriasgi.com
+
+William Waites - syslog support, SIP NAT traversal for SIP-UA. ww@styx.org
+
+Rich Murphey - Porting to FreeBSD, NetBSD, OpenBSD, and Darwin.
+ rich@whiteoaklabs.com http://whiteoaklabs.com
+
+Simon Lockhart - Porting to Solaris (based on work of Logan ???)
+ simon@slimey.org
+
+Olle E. Johansson - SIP RFC compliance, documentation and testing, testing, testing
+ oej@edvina.net, http://edvina.net
+
+Steve Kann - new jitter buffer for IAX2
+ stevek@stevek.com
+
+Constantine Filin - major contributions to the Asterisk Realtime Architecture
+
+Steve Murphy - privacy support, $[ ] parser upgrade, AEL2 parser upgrade
+
+Claude Patry - bug fixes, feature enhancements, and bug marshalling
+ cpatry@gmail.com
+
+Miroslav Nachev, miro@space-comm.com COSMOS Software Enterprises, Ltd.
+ - for Variable for No Answer Timeout for Attended Transfer
+
+Slav Klenov & Vanheuverzwijn Joachim - development of the generic jitterbuffer
+ Securax Ltd. info@securax.be
+
+Roy Sigurd Karlsbakk - providing funding for generic jitterbuffer development
+ roy@karlsbakk.net, Briiz Telecom AS
+
+Voop A/S, Nuvio Inc, Inotel S.A and Foniris Telecom A/S - funding for rewrite of SIP transfers
+
+Philippe Sultan - RADIUS CDR module
+ INRIA, http://www.inria.fr/
+
+John Martin, Aupix - Improved video support in the SIP channel
+
+Steve Underwood - Provided T.38 pass through support.
+
+George Konstantoulakis - Support for Greek in voicemail added by InAccess Networks (work funded by HOL, www.hol.gr) gkon@inaccessnetworks.com
+
+Daniel Nylander - Support for Swedish and Norwegian languages in voicemail. http://www.danielnylander.se/
+
+Stojan Sljivic - An option for maximum number of messsages per mailbox in voicemail. Also an issue with voicemail synchronization has been fixed. GDS Partners www.gdspartners.com . stojan.sljivic@gdspartners.com
+
+Bartosz Supczinski - Support for Polish added by DIR (www.dir.pl) Bartosz.Supczinski@dir.pl
+
+James Rothenberger - Support for IMAP storage integration added by OneBizTone LLC Work funded by University of Pennsylvania jar@onebiztone.com
+
+Paul Cadach - Bringing chan_h323 up to date, bug fixes, and more!
+
+=== OTHER CONTRIBUTIONS ===
+John Todd - Monkey sounds and associated teletorture prompt
+Michael Jerris - bug marshaling
+Leif Madsen, Jared Smith and Jim van Meggelen - the Asterisk book
+ available under a Creative Commons License at http://www.asteriskdocs.org
+Brian M. Clapper - poll.c emulation
+ This product includes software developed by Brian M. Clapper <bmc@clapper.org>
+
+=== HOLD MUSIC ===
+Music provided by www.freeplaymusic.com
+
+=== OTHER SOURCE CODE IN ASTERISK ===
+Asterisk uses libedit, the lightweight readline replacement from NetBSD.
+The cdr_radius module uses libradiusclient-ng, which is also from NetBSD.
+They are BSD-licensed and require the following statement:
+
+ This product includes software developed by the NetBSD
+ Foundation, Inc. and its contributors.
+
+Digium did not implement the codecs in Asterisk. Here is the copyright on the
+GSM source:
+
+Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
+Technische Universitaet Berlin
+
+Any use of this software is permitted provided that this notice is not
+removed and that neither the authors nor the Technische Universitaet Berlin
+are deemed to have made any representations as to the suitability of this
+software for any purpose nor are held responsible for any defects of
+this software. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+
+As a matter of courtesy, the authors request to be informed about uses
+this software has found, about bugs in this software, and about any
+improvements that may be of general interest.
+
+Berlin, 28.11.1994
+Jutta Degener
+Carsten Bormann
+
+And the copyright on the ADPCM source:
+
+Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The
+Netherlands.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the names of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..77ff5bb16
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,68 @@
+Asterisk is distributed under the GNU General Public License version 2
+and is also available under alternative licenses negotiated directly
+with Digium, Inc. If you obtained Asterisk under the GPL, then the GPL
+applies to all loadable Asterisk modules used on your system as well,
+except as defined below. The GPL (version 2) is included in this
+source tree in the file COPYING.
+
+This package also includes various components that are not part of
+Asterisk itself; these components are in the 'contrib' directory
+and its subdirectories. Most of these components are also
+distributed under the GPL version 2 as well, except for the following:
+
+contrib/firmware/iax/iaxy.bin:
+ This file is Copyright (C) Digium, Inc. and is licensed for
+ use with Digium IAXy hardware devices only. It can be
+ distributed freely as long as the distribution is in the
+ original form present in this package (not reformatted or
+ modified).
+
+Digium, Inc. (formerly Linux Support Services) holds copyright
+and/or sufficient licenses to all components of the Asterisk
+package, and therefore can grant, at its sole discretion, the ability
+for companies, individuals, or organizations to create proprietary or
+Open Source (even if not GPL) modules which may be dynamically linked at
+runtime with the portions of Asterisk which fall under our
+copyright/license umbrella, or are distributed under more flexible
+licenses than GPL.
+
+If you wish to use our code in other GPL programs, don't worry --
+there is no requirement that you provide the same exception in your
+GPL'd products (although if you've written a module for Asterisk we
+would strongly encourage you to make the same exception that we do).
+
+Specific permission is also granted to link Asterisk with OpenSSL, OpenH323
+and/or the UW IMAP Toolkit and distribute the resulting binary files.
+
+In addition, Asterisk implements two management/control protocols: the
+Asterisk Manager Interface (AMI) and the Asterisk Gateway Interface
+(AGI). It is our belief that applications using these protocols to
+manage or control an Asterisk instance do not have to be licensed
+under the GPL or a compatible license, as we believe these protocols
+do not create a 'derivative work' as referred to in the GPL. However,
+should any court or other judiciary body find that these protocols do
+fall under the terms of the GPL, then we hereby grant you a license to
+use these protocols in combination with Asterisk in external
+applications licensed under any license you wish.
+
+The 'Asterisk' name and logos are trademarks owned by Digium, Inc.,
+and use of them is subject to our trademark licensing policies. If you
+wish to use these trademarks for purposes other than simple
+redistribution of Asterisk source code obtained from Digium, you
+should contact our licensing department to determine the necessary
+steps you must take. For more information on this policy, please read
+http://www.digium.com/en/company/profile/trademarkpolicy.php
+
+If you have any questions regarding our licensing policy, please
+contact us:
+
++1.877.344.4861 (via telephone in the USA)
++1.256.428.6000 (via telephone outside the USA)
++1.256.864.0464 (via FAX inside or outside the USA)
+IAX2/pbx.digium.com (via IAX2)
+licensing@digium.com (via email)
+
+Digium, Inc.
+445 Jan Davis Drive
+Huntsville, AL 35806
+USA
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000..185e58f88
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,772 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Top level Makefile
+#
+# Copyright (C) 1999-2006, Digium, Inc.
+#
+# Mark Spencer <markster@digium.com>
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+# All Makefiles use the following variables:
+#
+# ASTCFLAGS - compiler options
+# ASTLDFLAGS - linker flags (not libraries)
+# AST_LIBS - libraries to build binaries XXX
+# LIBS - additional libraries, at top-level for all links,
+# on a single object just for that object
+# SOLINK - linker flags used only for creating shared objects (.so files),
+# used for all .so links
+#
+# Default values fo ASTCFLAGS and ASTLDFLAGS can be specified in the
+# environment when running make, as follows:
+#
+# $ ASTCFLAGS="-Werror" make
+
+export ASTTOPDIR
+export ASTERISKVERSION
+export ASTERISKVERSIONNUM
+export INSTALL_PATH
+export ASTETCDIR
+export ASTVARRUNDIR
+export MODULES_DIR
+export ASTSPOOLDIR
+export ASTVARLIBDIR
+export ASTDATADIR
+export ASTLOGDIR
+export ASTLIBDIR
+export ASTMANDIR
+export ASTHEADERDIR
+export ASTBINDIR
+export ASTSBINDIR
+export AGI_DIR
+export ASTCONFPATH
+export NOISY_BUILD
+export MENUSELECT_CFLAGS
+export CC
+export CXX
+export AR
+export RANLIB
+export HOST_CC
+export STATIC_BUILD
+export INSTALL
+export DESTDIR
+export PROC
+export SOLINK
+export STRIP
+export DOWNLOAD
+export AWK
+export GREP
+export ID
+export OSARCH
+export CURSES_DIR
+export NCURSES_DIR
+export TERMCAP_DIR
+export TINFO_DIR
+export GTK2_LIB
+export GTK2_INCLUDE
+
+# even though we could use '-include makeopts' here, use a wildcard
+# lookup anyway, so that make won't try to build makeopts if it doesn't
+# exist (other rules will force it to be built if needed)
+ifneq ($(wildcard makeopts),)
+ include makeopts
+endif
+
+# Some build systems, such as the one in openwrt, like to pass custom target
+# CFLAGS and LDFLAGS in the COPTS and LDOPTS variables.
+ASTCFLAGS+=$(COPTS)
+ASTLDFLAGS+=$(LDOPTS)
+
+#Uncomment this to see all build commands instead of 'quiet' output
+#NOISY_BUILD=yes
+
+# Create OPTIONS variable
+OPTIONS=
+
+empty:=
+space:=$(empty) $(empty)
+ASTTOPDIR:=$(subst $(space),\$(space),$(CURDIR))
+
+# Overwite config files on "make samples"
+OVERWRITE=y
+
+# Include debug and macro symbols in the executables (-g) and profiling info (-pg)
+DEBUG=-g3
+
+# Staging directory
+# Files are copied here temporarily during the install process
+# For example, make DESTDIR=/tmp/asterisk woud put things in
+# /tmp/asterisk/etc/asterisk
+# !!! Watch out, put no spaces or comments after the value !!!
+#DESTDIR?=/tmp/asterisk
+
+# Define standard directories for various platforms
+# These apply if they are not redefined in asterisk.conf
+ifeq ($(OSARCH),SunOS)
+ ASTETCDIR=/var/etc/asterisk
+ ASTLIBDIR=/opt/asterisk/lib
+ ASTVARLIBDIR=/var/opt/asterisk
+ ASTSPOOLDIR=/var/spool/asterisk
+ ASTLOGDIR=/var/log/asterisk
+ ASTHEADERDIR=/opt/asterisk/include
+ ASTBINDIR=/opt/asterisk/bin
+ ASTSBINDIR=/opt/asterisk/sbin
+ ASTVARRUNDIR=/var/run/asterisk
+ ASTMANDIR=/opt/asterisk/man
+else
+ ASTETCDIR=$(sysconfdir)/asterisk
+ ASTLIBDIR=$(libdir)/asterisk
+ ASTHEADERDIR=$(includedir)/asterisk
+ ASTBINDIR=$(bindir)
+ ASTSBINDIR=$(sbindir)
+ ASTSPOOLDIR=$(localstatedir)/spool/asterisk
+ ASTLOGDIR=$(localstatedir)/log/asterisk
+ ASTVARRUNDIR=$(localstatedir)/run
+ ASTMANDIR=$(mandir)
+ifneq ($(findstring BSD,$(OSARCH)),)
+ ASTVARLIBDIR=$(prefix)/share/asterisk
+ ASTVARRUNDIR=$(localstatedir)/run/asterisk
+else
+ ASTVARLIBDIR=$(localstatedir)/lib/asterisk
+endif
+endif
+ifeq ($(ASTDATADIR),)
+ ASTDATADIR:=$(ASTVARLIBDIR)
+endif
+
+# Asterisk.conf is located in ASTETCDIR or by using the -C flag
+# when starting Asterisk
+ASTCONFPATH=$(ASTETCDIR)/asterisk.conf
+MODULES_DIR=$(ASTLIBDIR)/modules
+AGI_DIR=$(ASTDATADIR)/agi-bin
+
+# If you use Apache, you may determine by a grep 'DocumentRoot' of your httpd.conf file
+HTTP_DOCSDIR=/var/www/html
+# Determine by a grep 'ScriptAlias' of your Apache httpd.conf file
+HTTP_CGIDIR=/var/www/cgi-bin
+
+# Uncomment this to use the older DSP routines
+#ASTCFLAGS+=-DOLD_DSP_ROUTINES
+
+# If the file .asterisk.makeopts is present in your home directory, you can
+# include all of your favorite menuselect options so that every time you download
+# a new version of Asterisk, you don't have to run menuselect to set them.
+# The file /etc/asterisk.makeopts will also be included but can be overridden
+# by the file in your home directory.
+
+GLOBAL_MAKEOPTS=$(wildcard /etc/asterisk.makeopts)
+USER_MAKEOPTS=$(wildcard ~/.asterisk.makeopts)
+
+MOD_SUBDIR_CFLAGS=-I$(ASTTOPDIR)/include
+OTHER_SUBDIR_CFLAGS=-I$(ASTTOPDIR)/include
+
+ifeq ($(OSARCH),linux-gnu)
+ ifeq ($(PROC),x86_64)
+ # You must have GCC 3.4 to use k8, otherwise use athlon
+ PROC=k8
+ #PROC=athlon
+ endif
+
+ ifeq ($(PROC),sparc64)
+ #The problem with sparc is the best stuff is in newer versions of gcc (post 3.0) only.
+ #This works for even old (2.96) versions of gcc and provides a small boost either way.
+ #A ultrasparc cpu is really v9 but the stock debian stable 3.0 gcc doesn't support it.
+ #So we go lowest common available by gcc and go a step down, still a step up from
+ #the default as we now have a better instruction set to work with. - Belgarath
+ PROC=ultrasparc
+ OPTIONS+=$(shell if $(CC) -mtune=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mtune=$(PROC)"; fi)
+ OPTIONS+=$(shell if $(CC) -mcpu=v8 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mcpu=v8"; fi)
+ OPTIONS+=-fomit-frame-pointer
+ endif
+
+ ifeq ($(PROC),arm)
+ # The Cirrus logic is the only heavily shipping arm processor with a real floating point unit
+ ifeq ($(SUB_PROC),maverick)
+ OPTIONS+=-fsigned-char -mcpu=ep9312
+ else
+ ifeq ($(SUB_PROC),xscale)
+ OPTIONS+=-fsigned-char -mcpu=xscale
+ else
+ OPTIONS+=-fsigned-char
+ endif
+ endif
+ endif
+endif
+
+ifeq ($(findstring -save-temps,$(ASTCFLAGS)),)
+ASTCFLAGS+=-pipe
+endif
+
+ASTCFLAGS+=-Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG)
+
+ifeq ($(AST_DEVMODE),yes)
+ ASTCFLAGS+=-Werror
+ ASTCFLAGS+=-Wunused
+ ASTCFLAGS+=$(AST_DECLARATION_AFTER_STATEMENT)
+ ASTCFLAGS+=$(AST_FORTIFY_SOURCE)
+# ASTCFLAGS+=-Wundef
+ ASTCFLAGS+=-Wformat -Wformat-security
+ ASTCFLAGS+=-Wmissing-format-attribute
+# ASTCFLAGS+=-Wformat=2
+endif
+
+ifneq ($(findstring BSD,$(OSARCH)),)
+ ASTCFLAGS+=-I/usr/local/include
+ ASTLDFLAGS+=-L/usr/local/lib
+endif
+
+ifneq ($(PROC),ultrasparc)
+ ASTCFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi)
+endif
+
+ifeq ($(PROC),ppc)
+ ASTCFLAGS+=-fsigned-char
+endif
+
+ifeq ($(OSARCH),FreeBSD)
+ # -V is understood by BSD Make, not by GNU make.
+ BSDVERSION=$(shell make -V OSVERSION -f /usr/share/mk/bsd.port.subdir.mk)
+ ASTCFLAGS+=$(shell if test $(BSDVERSION) -lt 500016 ; then echo "-D_THREAD_SAFE"; fi)
+ AST_LIBS+=$(shell if test $(BSDVERSION) -lt 502102 ; then echo "-lc_r"; else echo "-pthread"; fi)
+endif
+
+ifeq ($(OSARCH),NetBSD)
+ ASTCFLAGS+=-pthread -I/usr/pkg/include
+endif
+
+ifeq ($(OSARCH),OpenBSD)
+ ASTCFLAGS+=-pthread
+endif
+
+ifeq ($(OSARCH),SunOS)
+ ASTCFLAGS+=-Wcast-align -DSOLARIS -I../include/solaris-compat -I/opt/ssl/include -I/usr/local/ssl/include
+endif
+
+ASTERISKVERSION:=$(shell GREP=$(GREP) AWK=$(AWK) build_tools/make_version .)
+
+ifneq ($(wildcard .version),)
+ ASTERISKVERSIONNUM:=$(shell $(AWK) -F. '{printf "%01d%02d%02d", $$1, $$2, $$3}' .version)
+endif
+
+ifneq ($(wildcard .svn),)
+ ASTERISKVERSIONNUM=999999
+endif
+
+ASTCFLAGS+=$(MALLOC_DEBUG)$(BUSYDETECT)$(OPTIONS)
+
+MOD_SUBDIRS:=res channels pbx apps codecs formats cdr funcs main
+OTHER_SUBDIRS:=utils agi
+SUBDIRS:=$(OTHER_SUBDIRS) $(MOD_SUBDIRS)
+SUBDIRS_INSTALL:=$(SUBDIRS:%=%-install)
+SUBDIRS_CLEAN:=$(SUBDIRS:%=%-clean)
+SUBDIRS_DIST_CLEAN:=$(SUBDIRS:%=%-dist-clean)
+SUBDIRS_UNINSTALL:=$(SUBDIRS:%=%-uninstall)
+MOD_SUBDIRS_EMBED_LDSCRIPT:=$(MOD_SUBDIRS:%=%-embed-ldscript)
+MOD_SUBDIRS_EMBED_LDFLAGS:=$(MOD_SUBDIRS:%=%-embed-ldflags)
+MOD_SUBDIRS_EMBED_LIBS:=$(MOD_SUBDIRS:%=%-embed-libs)
+MOD_SUBDIRS_MENUSELECT_TREE:=$(MOD_SUBDIRS:%=%-menuselect-tree)
+
+ifneq ($(findstring darwin,$(OSARCH)),)
+ ASTCFLAGS+=-D__Darwin__
+ AUDIO_LIBS=-framework CoreAudio
+ SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace
+else
+# These are used for all but Darwin
+ SOLINK=-shared
+ ifneq ($(findstring BSD,$(OSARCH)),)
+ LDFLAGS+=-L/usr/local/lib
+ endif
+endif
+
+ifeq ($(OSARCH),SunOS)
+ SOLINK=-shared -fpic -L/usr/local/ssl/lib -lrt
+endif
+
+SUBMAKE=$(MAKE) --quiet --no-print-directory
+
+# This is used when generating the doxygen documentation
+ifneq ($(DOT),:)
+ HAVEDOT=yes
+else
+ HAVEDOT=no
+endif
+
+all: _all
+ @echo " +--------- Asterisk Build Complete ---------+"
+ @echo " + Asterisk has successfully been built, and +"
+ @echo " + can be installed by running: +"
+ @echo " + +"
+ifeq ($(MAKE), gmake)
+ @echo " + $(MAKE) install +"
+else
+ @echo " + $(MAKE) install +"
+endif
+ @echo " +-------------------------------------------+"
+
+_all: cleantest $(SUBDIRS)
+
+makeopts: configure
+ @echo "****"
+ @echo "**** The configure script must be executed before running '$(MAKE)'."
+ @echo "**** Please run \"./configure\"."
+ @echo "****"
+ @exit 1
+
+menuselect.makeopts: menuselect/menuselect menuselect-tree
+ menuselect/menuselect --check-deps menuselect.makeopts $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS)
+
+$(MOD_SUBDIRS_EMBED_LDSCRIPT):
+ @echo "EMBED_LDSCRIPTS+="`$(SUBMAKE) -C $(@:-embed-ldscript=) SUBDIR=$(@:-embed-ldscript=) __embed_ldscript` >> makeopts.embed_rules
+
+$(MOD_SUBDIRS_EMBED_LDFLAGS):
+ @echo "EMBED_LDFLAGS+="`$(SUBMAKE) -C $(@:-embed-ldflags=) SUBDIR=$(@:-embed-ldflags=) __embed_ldflags` >> makeopts.embed_rules
+
+$(MOD_SUBDIRS_EMBED_LIBS):
+ @echo "EMBED_LIBS+="`$(SUBMAKE) -C $(@:-embed-libs=) SUBDIR=$(@:-embed-libs=) __embed_libs` >> makeopts.embed_rules
+
+$(MOD_SUBDIRS_MENUSELECT_TREE):
+ @$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) moduleinfo
+ @$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) makeopts
+
+makeopts.embed_rules: menuselect.makeopts
+ @echo "Generating embedded module rules ..."
+ @rm -f $@
+ @$(MAKE) --no-print-directory $(MOD_SUBDIRS_EMBED_LDSCRIPT)
+ @$(MAKE) --no-print-directory $(MOD_SUBDIRS_EMBED_LDFLAGS)
+ @$(MAKE) --no-print-directory $(MOD_SUBDIRS_EMBED_LIBS)
+
+$(SUBDIRS): include/asterisk/version.h include/asterisk/buildopts.h defaults.h makeopts.embed_rules
+
+# ensure that all module subdirectories are processed before 'main' during
+# a parallel build, since if there are modules selected to be embedded the
+# directories containing them must be completed before the main Asterisk
+# binary can be built
+main: $(filter-out main,$(MOD_SUBDIRS))
+
+$(MOD_SUBDIRS):
+ @ASTCFLAGS="$(MOD_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" AST_LIBS="$(AST_LIBS)" $(MAKE) --no-builtin-rules -C $@ SUBDIR=$@ all
+
+$(OTHER_SUBDIRS):
+ @ASTCFLAGS="$(OTHER_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" AUDIO_LIBS="$(AUDIO_LIBS)" $(MAKE) --no-builtin-rules -C $@ SUBDIR=$@ all
+
+defaults.h: makeopts
+ @build_tools/make_defaults_h > $@.tmp
+ @if cmp -s $@.tmp $@ ; then : ; else \
+ mv $@.tmp $@ ; \
+ fi
+ @rm -f $@.tmp
+
+include/asterisk/version.h: FORCE
+ @build_tools/make_version_h > $@.tmp
+ @if cmp -s $@.tmp $@ ; then : ; else \
+ mv $@.tmp $@ ; \
+ fi
+ @rm -f $@.tmp
+
+include/asterisk/buildopts.h: menuselect.makeopts
+ @build_tools/make_buildopts_h > $@.tmp
+ @if cmp -s $@.tmp $@ ; then : ; else \
+ mv $@.tmp $@ ; \
+ fi
+ @rm -f $@.tmp
+
+$(SUBDIRS_CLEAN):
+ @$(MAKE) --no-print-directory -C $(@:-clean=) clean
+
+$(SUBDIRS_DIST_CLEAN):
+ @$(MAKE) --no-print-directory -C $(@:-dist-clean=) dist-clean
+
+clean: $(SUBDIRS_CLEAN) _clean
+
+_clean:
+ rm -f defaults.h
+ rm -f include/asterisk/build.h
+ rm -f include/asterisk/version.h
+ @$(MAKE) -C menuselect clean
+ cp -f .cleancount .lastclean
+
+dist-clean: distclean
+
+distclean: $(SUBDIRS_DIST_CLEAN) _clean
+ @$(MAKE) -C menuselect dist-clean
+ @$(MAKE) -C sounds dist-clean
+ rm -f menuselect.makeopts makeopts menuselect-tree menuselect.makedeps
+ rm -f makeopts.embed_rules
+ rm -f config.log config.status config.cache
+ rm -rf autom4te.cache
+ rm -f include/asterisk/autoconfig.h
+ rm -f include/asterisk/buildopts.h
+ rm -rf doc/api
+ rm -f build_tools/menuselect-deps
+
+datafiles: _all
+ if [ x`$(ID) -un` = xroot ]; then CFLAGS="$(ASTCFLAGS)" sh build_tools/mkpkgconfig $(DESTDIR)/usr/lib/pkgconfig; fi
+# Should static HTTP be installed during make samples or even with its own target ala
+# webvoicemail? There are portions here that *could* be customized but might also be
+# improved a lot. I'll put it here for now.
+ mkdir -p $(DESTDIR)$(ASTDATADIR)/static-http
+ for x in static-http/*; do \
+ $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTDATADIR)/static-http ; \
+ done
+ mkdir -p $(DESTDIR)$(ASTDATADIR)/images
+ for x in images/*.jpg; do \
+ $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTDATADIR)/images ; \
+ done
+ mkdir -p $(DESTDIR)$(AGI_DIR)
+ $(MAKE) -C sounds install
+
+update:
+ @if [ -d .svn ]; then \
+ echo "Updating from Subversion..." ; \
+ svn update | tee update.out; \
+ rm -f .version; \
+ if [ `grep -c ^C update.out` -gt 0 ]; then \
+ echo ; echo "The following files have conflicts:" ; \
+ grep ^C update.out | cut -b4- ; \
+ fi ; \
+ rm -f update.out; \
+ else \
+ echo "Not under version control"; \
+ fi
+
+NEWHEADERS=$(notdir $(wildcard include/asterisk/*.h))
+OLDHEADERS=$(filter-out $(NEWHEADERS),$(notdir $(wildcard $(DESTDIR)$(ASTHEADERDIR)/*.h)))
+
+installdirs:
+ mkdir -p $(DESTDIR)$(MODULES_DIR)
+ mkdir -p $(DESTDIR)$(ASTSBINDIR)
+ mkdir -p $(DESTDIR)$(ASTETCDIR)
+ mkdir -p $(DESTDIR)$(ASTBINDIR)
+ mkdir -p $(DESTDIR)$(ASTVARRUNDIR)
+ mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/voicemail
+ mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/dictate
+ mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/system
+ mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/tmp
+ mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/meetme
+ mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/monitor
+
+bininstall: _all installdirs $(SUBDIRS_INSTALL)
+ $(INSTALL) -m 755 main/asterisk $(DESTDIR)$(ASTSBINDIR)/
+ $(LN) -sf asterisk $(DESTDIR)$(ASTSBINDIR)/rasterisk
+ $(INSTALL) -m 755 contrib/scripts/astgenkey $(DESTDIR)$(ASTSBINDIR)/
+ $(INSTALL) -m 755 contrib/scripts/autosupport $(DESTDIR)$(ASTSBINDIR)/
+ if [ ! -f $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ]; then \
+ cat contrib/scripts/safe_asterisk | sed 's|__ASTERISK_SBIN_DIR__|$(ASTSBINDIR)|;s|__ASTERISK_VARRUN_DIR__|$(ASTVARRUNDIR)|;' > $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ;\
+ chmod 755 $(DESTDIR)$(ASTSBINDIR)/safe_asterisk;\
+ fi
+ $(INSTALL) -d $(DESTDIR)$(ASTHEADERDIR)
+ $(INSTALL) -m 644 include/asterisk.h $(DESTDIR)$(includedir)
+ $(INSTALL) -m 644 include/asterisk/*.h $(DESTDIR)$(ASTHEADERDIR)
+ if [ -n "$(OLDHEADERS)" ]; then \
+ rm -f $(addprefix $(DESTDIR)$(ASTHEADERDIR)/,$(OLDHEADERS)) ;\
+ fi
+ mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-csv
+ mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-custom
+ mkdir -p $(DESTDIR)$(ASTDATADIR)/keys
+ mkdir -p $(DESTDIR)$(ASTDATADIR)/firmware
+ mkdir -p $(DESTDIR)$(ASTDATADIR)/firmware/iax
+ mkdir -p $(DESTDIR)$(ASTMANDIR)/man8
+ $(INSTALL) -m 644 keys/iaxtel.pub $(DESTDIR)$(ASTDATADIR)/keys
+ $(INSTALL) -m 644 keys/freeworlddialup.pub $(DESTDIR)$(ASTDATADIR)/keys
+ $(INSTALL) -m 644 doc/asterisk.8 $(DESTDIR)$(ASTMANDIR)/man8
+ $(INSTALL) -m 644 contrib/scripts/astgenkey.8 $(DESTDIR)$(ASTMANDIR)/man8
+ $(INSTALL) -m 644 contrib/scripts/autosupport.8 $(DESTDIR)$(ASTMANDIR)/man8
+ $(INSTALL) -m 644 contrib/scripts/safe_asterisk.8 $(DESTDIR)$(ASTMANDIR)/man8
+ if [ -f contrib/firmware/iax/iaxy.bin ] ; then \
+ $(INSTALL) -m 644 contrib/firmware/iax/iaxy.bin $(DESTDIR)$(ASTDATADIR)/firmware/iax/iaxy.bin; \
+ fi
+
+$(SUBDIRS_INSTALL):
+ @DESTDIR="$(DESTDIR)" ASTSBINDIR="$(ASTSBINDIR)" $(MAKE) -C $(@:-install=) install
+
+NEWMODS=$(notdir $(wildcard */*.so))
+DEPMODS=chan_zap.so app_zapras.so app_zapscan.so app_zapbarge.so codec_zap.so
+OLDMODS=$(filter-out $(NEWMODS) $(DEPMODS),$(notdir $(wildcard $(DESTDIR)$(MODULES_DIR)/*.so)))
+
+oldmodcheck:
+ @for f in $(DEPMODS); do rm -f $(DESTDIR)$(MODULES_DIR)/$$f; done
+ @if [ -n "$(OLDMODS)" ]; then \
+ echo " WARNING WARNING WARNING" ;\
+ echo "" ;\
+ echo " Your Asterisk modules directory, located at" ;\
+ echo " $(DESTDIR)$(MODULES_DIR)" ;\
+ echo " contains modules that were not installed by this " ;\
+ echo " version of Asterisk. Please ensure that these" ;\
+ echo " modules are compatible with this version before" ;\
+ echo " attempting to run Asterisk." ;\
+ echo "" ;\
+ for f in $(OLDMODS); do \
+ echo " $$f" ;\
+ done ;\
+ echo "" ;\
+ echo " WARNING WARNING WARNING" ;\
+ fi
+
+badshell:
+ifneq ($(findstring ~,$(DESTDIR)),)
+ @echo "Your shell doesn't do ~ expansion when expected (specifically, when doing \"make install DESTDIR=~/path\")."
+ @echo "Try replacing ~ with \$$HOME, as in \"make install DESTDIR=\$$HOME/path\"."
+ @exit 1
+endif
+
+install: badshell datafiles bininstall
+ @if [ -x /usr/sbin/asterisk-post-install ]; then \
+ /usr/sbin/asterisk-post-install $(DESTDIR) . ; \
+ fi
+ @echo " +---- Asterisk Installation Complete -------+"
+ @echo " + +"
+ @echo " + YOU MUST READ THE SECURITY DOCUMENT +"
+ @echo " + +"
+ @echo " + Asterisk has successfully been installed. +"
+ @echo " + If you would like to install the sample +"
+ @echo " + configuration files (overwriting any +"
+ @echo " + existing config files), run: +"
+ @echo " + +"
+ifeq ($(MAKE), gmake)
+ @echo " + $(MAKE) samples +"
+else
+ @echo " + $(MAKE) samples +"
+endif
+ @echo " + +"
+ @echo " +----------------- or ---------------------+"
+ @echo " + +"
+ @echo " + You can go ahead and install the asterisk +"
+ @echo " + program documentation now or later run: +"
+ @echo " + +"
+ifeq ($(MAKE), gmake)
+ @echo " + $(MAKE) progdocs +"
+else
+ @echo " + $(MAKE) progdocs +"
+endif
+ @echo " + +"
+ @echo " + **Note** This requires that you have +"
+ @echo " + doxygen installed on your local system +"
+ @echo " +-------------------------------------------+"
+ @$(MAKE) -s oldmodcheck
+
+upgrade: bininstall
+
+adsi:
+ mkdir -p $(DESTDIR)$(ASTETCDIR)
+ for x in configs/*.adsi; do \
+ if [ ! -f $(DESTDIR)$(ASTETCDIR)/$$x ]; then \
+ $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x` ; \
+ fi ; \
+ done
+
+samples: adsi
+ mkdir -p $(DESTDIR)$(ASTETCDIR)
+ for x in configs/*.sample; do \
+ if [ -f $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample` ]; then \
+ if [ "$(OVERWRITE)" = "y" ]; then \
+ if cmp -s $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample` $$x ; then \
+ echo "Config file $$x is unchanged"; \
+ continue; \
+ fi ; \
+ mv -f $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample` $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample`.old ; \
+ else \
+ echo "Skipping config file $$x"; \
+ continue; \
+ fi ;\
+ fi ; \
+ $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample` ;\
+ done
+ if [ "$(OVERWRITE)" = "y" ] || [ ! -f $(DESTDIR)$(ASTCONFPATH) ]; then \
+ ( \
+ echo "[directories]" ; \
+ echo "astetcdir => $(ASTETCDIR)" ; \
+ echo "astmoddir => $(MODULES_DIR)" ; \
+ echo "astvarlibdir => $(ASTVARLIBDIR)" ; \
+ echo "astdatadir => $(ASTDATADIR)" ; \
+ echo "astagidir => $(AGI_DIR)" ; \
+ echo "astspooldir => $(ASTSPOOLDIR)" ; \
+ echo "astrundir => $(ASTVARRUNDIR)" ; \
+ echo "astlogdir => $(ASTLOGDIR)" ; \
+ echo "" ; \
+ echo "[options]" ; \
+ echo "languageprefix = yes ; Use the new sound prefix path syntax" ; \
+ echo ";verbose = 3" ; \
+ echo ";debug = 3" ; \
+ echo ";alwaysfork = yes ; same as -F at startup" ; \
+ echo ";nofork = yes ; same as -f at startup" ; \
+ echo ";quiet = yes ; same as -q at startup" ; \
+ echo ";timestamp = yes ; same as -T at startup" ; \
+ echo ";execincludes = yes ; support #exec in config files" ; \
+ echo ";console = yes ; Run as console (same as -c at startup)" ; \
+ echo ";highpriority = yes ; Run realtime priority (same as -p at startup)" ; \
+ echo ";initcrypto = yes ; Initialize crypto keys (same as -i at startup)" ; \
+ echo ";nocolor = yes ; Disable console colors" ; \
+ echo ";dontwarn = yes ; Disable some warnings" ; \
+ echo ";dumpcore = yes ; Dump core on crash (same as -g at startup)" ; \
+ echo ";internal_timing = yes" ; \
+ echo ";systemname = my_system_name ; prefix uniqueid with a system name for global uniqueness issues" ; \
+ echo ";maxcalls = 10 ; Maximum amount of calls allowed" ; \
+ echo ";maxload = 0.9 ; Asterisk stops accepting new calls if the load average exceed this limit" ; \
+ echo ";cache_record_files = yes ; Cache recorded sound files to another directory during recording" ; \
+ echo ";record_cache_dir = /tmp ; Specify cache directory (used in cnjunction with cache_record_files)" ; \
+ echo ";transmit_silence_during_record = yes ; Transmit SLINEAR silence while a channel is being recorded" ; \
+ echo ";transmit_silence = yes ; Transmit SLINEAR silence while a channel is being recorded or DTMF is being generated" ; \
+ echo ";transcode_via_sln = yes ; Build transcode paths via SLINEAR, instead of directly" ; \
+ echo ";runuser = asterisk ; The user to run as" ; \
+ echo ";rungroup = asterisk ; The group to run as" ; \
+ echo ";dahdichanname = yes ; Channels created by chan_dahdi will be called 'DAHDI', otherwise 'Zap'" ; \
+ echo "" ; \
+ echo "; Changing the following lines may compromise your security." ; \
+ echo ";[files]" ; \
+ echo ";astctlpermissions = 0660" ; \
+ echo ";astctlowner = root" ; \
+ echo ";astctlgroup = apache" ; \
+ echo ";astctl = asterisk.ctl" ; \
+ ) > $(DESTDIR)$(ASTCONFPATH) ; \
+ else \
+ echo "Skipping asterisk.conf creation"; \
+ fi
+ mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/voicemail/default/1234/INBOX
+ build_tools/make_sample_voicemail $(DESTDIR)/$(ASTDATADIR) $(DESTDIR)/$(ASTSPOOLDIR)
+
+webvmail:
+ @[ -d $(DESTDIR)$(HTTP_DOCSDIR)/ ] || ( printf "http docs directory not found.\nUpdate assignment of variable HTTP_DOCSDIR in Makefile!\n" && exit 1 )
+ @[ -d $(DESTDIR)$(HTTP_CGIDIR) ] || ( printf "cgi-bin directory not found.\nUpdate assignment of variable HTTP_CGIDIR in Makefile!\n" && exit 1 )
+ $(INSTALL) -m 4755 -o root -g root contrib/scripts/vmail.cgi $(DESTDIR)$(HTTP_CGIDIR)/vmail.cgi
+ mkdir -p $(DESTDIR)$(HTTP_DOCSDIR)/_asterisk
+ for x in images/*.gif; do \
+ $(INSTALL) -m 644 $$x $(DESTDIR)$(HTTP_DOCSDIR)/_asterisk/; \
+ done
+ @echo " +--------- Asterisk Web Voicemail ----------+"
+ @echo " + +"
+ @echo " + Asterisk Web Voicemail is installed in +"
+ @echo " + your cgi-bin directory: +"
+ @echo " + $(DESTDIR)$(HTTP_CGIDIR)"
+ @echo " + IT USES A SETUID ROOT PERL SCRIPT, SO +"
+ @echo " + IF YOU DON'T LIKE THAT, UNINSTALL IT! +"
+ @echo " + +"
+ @echo " + Other static items have been stored in: +"
+ @echo " + $(DESTDIR)$(HTTP_DOCSDIR)"
+ @echo " + +"
+ @echo " + If these paths do not match your httpd +"
+ @echo " + installation, correct the definitions +"
+ @echo " + in your Makefile of HTTP_CGIDIR and +"
+ @echo " + HTTP_DOCSDIR +"
+ @echo " + +"
+ @echo " +-------------------------------------------+"
+
+progdocs:
+ (cat contrib/asterisk-ng-doxygen; echo "HAVE_DOT=$(HAVEDOT)"; \
+ echo "PROJECT_NUMBER=$(ASTERISKVERSION)") | doxygen -
+
+config:
+ @if [ "${OSARCH}" = "linux-gnu" ]; then \
+ if [ -f /etc/redhat-release -o -f /etc/fedora-release ]; then \
+ $(INSTALL) -m 755 contrib/init.d/rc.redhat.asterisk $(DESTDIR)/etc/rc.d/init.d/asterisk; \
+ if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \
+ elif [ -f /etc/debian_version ]; then \
+ $(INSTALL) -m 755 contrib/init.d/rc.debian.asterisk $(DESTDIR)/etc/init.d/asterisk; \
+ if [ -z "$(DESTDIR)" ]; then /usr/sbin/update-rc.d asterisk start 50 2 3 4 5 . stop 91 2 3 4 5 .; fi; \
+ elif [ -f /etc/gentoo-release ]; then \
+ $(INSTALL) -m 755 contrib/init.d/rc.gentoo.asterisk $(DESTDIR)/etc/init.d/asterisk; \
+ if [ -z "$(DESTDIR)" ]; then /sbin/rc-update add asterisk default; fi; \
+ elif [ -f /etc/mandrake-release -o -f /etc/mandriva-release ]; then \
+ $(INSTALL) -m 755 contrib/init.d/rc.mandrake.asterisk $(DESTDIR)/etc/rc.d/init.d/asterisk; \
+ if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \
+ elif [ -f /etc/SuSE-release -o -f /etc/novell-release ]; then \
+ $(INSTALL) -m 755 contrib/init.d/rc.suse.asterisk $(DESTDIR)/etc/init.d/asterisk; \
+ if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \
+ elif [ -f /etc/slackware-version ]; then \
+ echo "Slackware is not currently supported, although an init script does exist for it." \
+ else \
+ echo "We could not install init scripts for your distribution."; \
+ fi \
+ else \
+ echo "We could not install init scripts for your operating system."; \
+ fi
+
+sounds:
+ $(MAKE) -C sounds all
+
+# If the cleancount has been changed, force a make clean.
+# .cleancount is the global clean count, and .lastclean is the
+# last clean count we had
+
+cleantest:
+ @cmp -s .cleancount .lastclean || $(MAKE) clean
+
+$(SUBDIRS_UNINSTALL):
+ @$(MAKE) --no-print-directory -C $(@:-uninstall=) uninstall
+
+_uninstall: $(SUBDIRS_UNINSTALL)
+ rm -f $(DESTDIR)$(MODULES_DIR)/*
+ rm -f $(DESTDIR)$(ASTSBINDIR)/*asterisk*
+ rm -f $(DESTDIR)$(ASTSBINDIR)/astgenkey
+ rm -f $(DESTDIR)$(ASTSBINDIR)/autosupport
+ rm -rf $(DESTDIR)$(ASTHEADERDIR)
+ rm -rf $(DESTDIR)$(ASTDATADIR)/firmware
+ rm -f $(DESTDIR)$(ASTMANDIR)/man8/asterisk.8
+ rm -f $(DESTDIR)$(ASTMANDIR)/man8/astgenkey.8
+ rm -f $(DESTDIR)$(ASTMANDIR)/man8/autosupport.8
+ rm -f $(DESTDIR)$(ASTMANDIR)/man8/safe_asterisk.8
+ $(MAKE) -C sounds uninstall
+
+uninstall: _uninstall
+ @echo " +--------- Asterisk Uninstall Complete -----+"
+ @echo " + Asterisk binaries, sounds, man pages, +"
+ @echo " + headers, modules, and firmware builds, +"
+ @echo " + have all been uninstalled. +"
+ @echo " + +"
+ @echo " + To remove ALL traces of Asterisk, +"
+ @echo " + including configuration, spool +"
+ @echo " + directories, and logs, run the following +"
+ @echo " + command: +"
+ @echo " + +"
+ifeq ($(MAKE), gmake)
+ @echo " + $(MAKE) uninstall-all +"
+else
+ @echo " + $(MAKE) uninstall-all +"
+endif
+ @echo " +-------------------------------------------+"
+
+uninstall-all: _uninstall
+ rm -rf $(DESTDIR)$(ASTLIBDIR)
+ rm -rf $(DESTDIR)$(ASTVARLIBDIR)
+ rm -rf $(DESTDIR)$(ASTDATADIR)
+ rm -rf $(DESTDIR)$(ASTSPOOLDIR)
+ rm -rf $(DESTDIR)$(ASTETCDIR)
+ rm -rf $(DESTDIR)$(ASTLOGDIR)
+
+menuconfig: menuselect
+
+gmenuconfig: gmenuselect
+
+menuselect: menuselect/menuselect menuselect-tree
+ -@menuselect/menuselect menuselect.makeopts $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) && (echo "menuselect changes saved!"; rm -f channels/h323/Makefile.ast main/asterisk) || echo "menuselect changes NOT saved!"
+
+gmenuselect: menuselect/gmenuselect menuselect-tree
+ -@menuselect/gmenuselect menuselect.makeopts $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) && (echo "menuselect changes saved!"; rm -f channels/h323/Makefile.ast main/asterisk) || echo "menuselect changes NOT saved!"
+
+menuselect/menuselect: makeopts menuselect/menuselect.c menuselect/menuselect_curses.c menuselect/menuselect_stub.c menuselect/menuselect.h menuselect/linkedlists.h makeopts
+ @CC="$(HOST_CC)" LD="" AR="" RANLIB="" CFLAGS="" $(MAKE) -C menuselect CONFIGURE_SILENT="--silent"
+
+menuselect/gmenuselect: makeopts menuselect/menuselect.c menuselect/menuselect_gtk.c menuselect/menuselect_stub.c menuselect/menuselect.h menuselect/linkedlists.h makeopts
+ @CC="$(HOST_CC)" CXX="$(CXX)" LD="" AR="" RANLIB="" CFLAGS="" $(MAKE) -C menuselect _gmenuselect CONFIGURE_SILENT="--silent"
+
+menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(dir)/*.c) $(wildcard $(dir)/*.cc)) build_tools/cflags.xml build_tools/cflags-devmode.xml sounds/sounds.xml build_tools/embed_modules.xml configure
+ @echo "Generating input for menuselect ..."
+ @echo "<?xml version=\"1.0\"?>" > $@
+ @echo >> $@
+ @echo "<menu name=\"Asterisk Module and Build Option Selection\">" >> $@
+ @for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SUBMAKE) -C $${dir} SUBDIR=$${dir} moduleinfo >> $@; done
+ @for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SUBMAKE) -C $${dir} SUBDIR=$${dir} makeopts >> $@; done
+ @cat build_tools/cflags.xml >> $@
+ @if [ "${AST_DEVMODE}" = "yes" ]; then \
+ cat build_tools/cflags-devmode.xml >> $@; \
+ fi
+ @cat build_tools/embed_modules.xml >> $@
+ @cat sounds/sounds.xml >> $@
+ @echo "</menu>" >> $@
+
+.PHONY: menuselect main sounds clean dist-clean distclean all prereqs cleantest uninstall _uninstall uninstall-all dont-optimize $(SUBDIRS_INSTALL) $(SUBDIRS_DIST_CLEAN) $(SUBDIRS_CLEAN) $(SUBDIRS_UNINSTALL) $(SUBDIRS) $(MOD_SUBDIRS_EMBED_LDSCRIPT) $(MOD_SUBDIRS_EMBED_LDFLAGS) $(MOD_SUBDIRS_EMBED_LIBS) badshell menuselect.makeopts installdirs _clean
+
+FORCE:
diff --git a/Makefile.moddir_rules b/Makefile.moddir_rules
new file mode 100644
index 000000000..a0738fecb
--- /dev/null
+++ b/Makefile.moddir_rules
@@ -0,0 +1,112 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile rules for subdirectories containing modules
+#
+# Copyright (C) 2006, Digium, Inc.
+#
+# Kevin P. Fleming <kpfleming@digium.com>
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+ifeq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
+ ASTCFLAGS+=${GC_CFLAGS}
+endif
+
+ifneq ($(findstring STATIC_BUILD,$(MENUSELECT_CFLAGS)),)
+ STATIC_BUILD=-static
+endif
+
+include $(ASTTOPDIR)/Makefile.rules
+
+comma:=,
+
+$(addsuffix .o,$(C_MODS)): ASTCFLAGS+=-DAST_MODULE=\"$*\" $(MENUSELECT_OPTS_$*:%=-D%) $(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_INCLUDE))
+$(addsuffix .oo,$(CC_MODS)): ASTCFLAGS+=-DAST_MODULE=\"$*\" $(MENUSELECT_OPTS_$*:%=-D%) $(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_INCLUDE))
+
+$(LOADABLE_MODS:%=%.so): ASTCFLAGS+=-fPIC
+$(LOADABLE_MODS:%=%.so): LIBS+=$(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_LIB))
+$(LOADABLE_MODS:%=%.so): ASTLDFLAGS+=$(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_LDFLAGS))
+
+$(addsuffix .so,$(filter $(LOADABLE_MODS),$(C_MODS))): %.so: %.o
+$(addsuffix .so,$(filter $(LOADABLE_MODS),$(CC_MODS))): %.so: %.oo
+
+modules.link: $(addsuffix .o,$(filter $(EMBEDDED_MODS),$(C_MODS)))
+modules.link: $(addsuffix .oo,$(filter $(EMBEDDED_MODS),$(CC_MODS)))
+
+.PHONY: clean uninstall _all moduleinfo makeopts
+
+ifneq ($(LOADABLE_MODS),)
+_all: $(LOADABLE_MODS:%=%.so)
+endif
+
+ifneq ($(EMBEDDED_MODS),)
+_all: modules.link
+__embed_ldscript:
+ @echo "../$(SUBDIR)/modules.link"
+__embed_ldflags:
+ @echo "$(foreach mod,$(filter $(EMBEDDED_MODS),$(C_MODS)),$(foreach dep,$(MENUSELECT_DEPENDS_$(mod)),$(dep)_LDFLAGS))"
+ @echo "$(foreach mod,$(filter $(EMBEDDED_MODS),$(CC_MODS)),$(foreach dep,$(MENUSELECT_DEPENDS_$(mod)),$(dep)_LDFLAGS))"
+__embed_libs:
+ @echo "$(foreach mod,$(filter $(EMBEDDED_MODS),$(C_MODS)),$(foreach dep,$(MENUSELECT_DEPENDS_$(mod)),$(dep)_LIB))"
+ @echo "$(foreach mod,$(filter $(EMBEDDED_MODS),$(CC_MODS)),$(foreach dep,$(MENUSELECT_DEPENDS_$(mod)),$(dep)_LIB))"
+else
+__embed_ldscript:
+__embed_ldflags:
+__embed_libs:
+endif
+
+modules.link:
+ @rm -f $@
+ @for file in $(patsubst %,$(SUBDIR)/%,$(filter %.o,$^)); do echo "INPUT (../$${file})" >> $@; done
+ @for file in $(patsubst %,$(SUBDIR)/%,$(filter-out %.o,$^)); do echo "INPUT (../$${file})" >> $@; done
+
+clean::
+ rm -f *.so *.o *.oo *.s *.i *.ii
+ rm -f .*.d
+ rm -f modules.link
+
+install:: all
+ for x in $(LOADABLE_MODS:%=%.so); do $(INSTALL) -m 755 $$x $(DESTDIR)$(MODULES_DIR) ; done
+
+uninstall::
+
+dist-clean::
+ rm -f .*.moduleinfo .moduleinfo
+ rm -f .*.makeopts .makeopts
+
+.%.moduleinfo: %.c
+ @echo "<member name=\"$*\" displayname=\"$(shell $(GREP) -e AST_MODULE_INFO $< | head -n 1 | cut -d '"' -f 2)\" remove_on_change=\"$(SUBDIR)/$*.o $(SUBDIR)/$*.so\">" > $@
+ $(AWK) -f $(ASTTOPDIR)/build_tools/get_moduleinfo $< >> $@
+ echo "</member>" >> $@
+
+.%.moduleinfo: %.cc
+ @echo "<member name=\"$*\" displayname=\"$(shell $(GREP) -e AST_MODULE_INFO $< | head -n 1 | cut -d '"' -f 2)\" remove_on_change=\"$(SUBDIR)/$*.oo $(SUBDIR)/$*.so\">" > $@
+ $(AWK) -f $(ASTTOPDIR)/build_tools/get_moduleinfo $< >> $@
+ echo "</member>" >> $@
+
+.moduleinfo:: $(addsuffix .moduleinfo,$(addprefix .,$(ALL_C_MODS) $(ALL_CC_MODS)))
+ @echo "<category name=\"MENUSELECT_$(MENUSELECT_CATEGORY)\" displayname=\"$(MENUSELECT_DESCRIPTION)\" remove_on_change=\"$(SUBDIR)/modules.link\">" > $@
+ @cat $^ >> $@
+ @echo "</category>" >> $@
+
+moduleinfo: .moduleinfo
+ @cat $<
+
+.%.makeopts: %.c
+ @$(AWK) -f $(ASTTOPDIR)/build_tools/get_makeopts $< > $@
+
+.%.makeopts: %.cc
+ @$(AWK) -f $(ASTTOPDIR)/build_tools/get_makeopts $< > $@
+
+.makeopts:: $(addsuffix .makeopts,$(addprefix .,$(ALL_C_MODS) $(ALL_CC_MODS)))
+ @cat $^ > $@
+
+makeopts: .makeopts
+ @cat $<
+
+ifneq ($(wildcard .*.d),)
+ include .*.d
+endif
diff --git a/Makefile.rules b/Makefile.rules
new file mode 100644
index 000000000..e4b174577
--- /dev/null
+++ b/Makefile.rules
@@ -0,0 +1,129 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile rules
+#
+# Copyright (C) 2006-2008, Digium, Inc.
+#
+# Kevin P. Fleming <kpfleming@digium.com>
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+# Each command is preceded by a short comment on what to do.
+# Prefixing one or the other with @\# or @ or nothing makes the desired
+# behaviour. ECHO_PREFIX prefixes the comment, CMD_PREFIX prefixes the command.
+
+-include $(ASTTOPDIR)/makeopts
+
+.PHONY: dist-clean
+
+# If 'make' decides to create intermediate files to satisfy a build requirement
+# (like producing a .i from a .c), we want to keep them, so tell make to keep
+# all intermediate files
+.SECONDARY:
+
+# extra cflags to build dependencies. Recursively expanded.
+MAKE_DEPS=-MD -MT $@ -MF .$(subst /,_,$@).d -MP
+
+ifeq ($(NOISY_BUILD),)
+ ECHO_PREFIX=@
+ CMD_PREFIX=@
+else
+ ECHO_PREFIX=@\#
+ CMD_PREFIX=
+endif
+
+OPTIMIZE?=-O6
+
+ifeq ($(findstring DONT_OPTIMIZE,$(MENUSELECT_CFLAGS)),)
+ # More GSM codec optimization
+ # Uncomment to enable MMXTM optimizations for x86 architecture CPU's
+ # which support MMX instructions. This should be newer pentiums,
+ # ppro's, etc, as well as the AMD K6 and K7.
+ #K6OPT=-DK6OPT
+
+ ASTCFLAGS+=$(OPTIMIZE)
+endif
+
+# shortcuts for common combinations of flags; these must be recursively expanded so that
+# per-target settings will be applied
+CC_CFLAGS=$(PTHREAD_CFLAGS) $(ASTCFLAGS)
+CXX_CFLAGS=$(PTHREAD_CFLAGS) $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(AST_DECLARATION_AFTER_STATEMENT),$(ASTCFLAGS))
+CC_LDFLAGS_SO=$(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK)
+CXX_LDFLAGS_SO=$(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK)
+CC_LIBS=$(PTHREAD_LIBS) $(LIBS)
+CXX_LIBS=$(PTHREAD_LIBS) $(LIBS)
+
+# determine whether to double-compile so that the optimizer can report code path problems
+# this is only done when developer mode and DONT_OPTIMIZE are both enabled
+# in that case, we run the preprocessor to produce a .i or .ii file from the source
+# code, then compile once with optimizer enabled (and the output to /dev/null),
+# and if that doesn't fail then compile again with optimizer disabled
+ifeq ($(findstring DONT_OPTIMIZE,$(MENUSELECT_CFLAGS))$(AST_DEVMODE),DONT_OPTIMIZEyes)
+COMPILE_DOUBLE=yes
+endif
+
+%.o: %.s
+ $(ECHO_PREFIX) echo " [AS] $< -> $@"
+ifeq ($(COMPILE_DOUBLE),yes)
+ $(CMD_PREFIX) $(CC) -o /dev/null -c $< $(CC_CFLAGS) $(OPTIMIZE)
+endif
+ $(CMD_PREFIX) $(CC) -o $@ -c $< $(CC_CFLAGS)
+
+%.o: %.i
+ $(ECHO_PREFIX) echo " [CCi] $< -> $@"
+ifeq ($(COMPILE_DOUBLE),yes)
+ $(CMD_PREFIX) $(CC) -o /dev/null -c $< $(CC_CFLAGS) $(OPTIMIZE)
+endif
+ $(CMD_PREFIX) $(CC) -o $@ -c $< $(CC_CFLAGS)
+
+ifneq ($(COMPILE_DOUBLE),yes)
+%.o: %.c
+ $(ECHO_PREFIX) echo " [CC] $< -> $@"
+ $(CMD_PREFIX) $(CC) -o $@ -c $< $(CC_CFLAGS) $(MAKE_DEPS)
+endif
+
+%.i: %.c
+ $(ECHO_PREFIX) echo " [CPP] $< -> $@"
+ $(CMD_PREFIX) $(CC) -o $@ -E $< $(CC_CFLAGS) $(MAKE_DEPS)
+
+%.oo: %.ii
+ $(ECHO_PREFIX) echo " [CXXi] $< -> $@"
+ifeq ($(COMPILE_DOUBLE),yes)
+ $(CMD_PREFIX) $(CXX) -o /dev/null -c $< $(CXX_CFLAGS) $(OPTIMIZE)
+endif
+ $(CMD_PREFIX) $(CXX) -o $@ -c $< $(CXX_CFLAGS)
+
+ifneq ($(COMPILE_DOUBLE),yes)
+%.oo: %.cc
+ $(ECHO_PREFIX) echo " [CXX] $< -> $@"
+ $(CMD_PREFIX) $(CXX) -o $@ -c $< $(CXX_CFLAGS) $(MAKE_DEPS)
+endif
+
+%.ii: %.cc
+ $(ECHO_PREFIX) echo " [CPP] $< -> $@"
+ $(CMD_PREFIX) $(CXX) -o $@ -E $< $(CXX_CFLAGS) $(MAKE_DEPS)
+
+%.c: %.y
+ $(ECHO_PREFIX) echo " [BISON] $< -> $@"
+ $(CMD_PREFIX) bison -o $@ -d --name-prefix=ast_yy $<
+
+%.c: %.fl
+ $(ECHO_PREFIX) echo " [FLEX] $< -> $@"
+ $(CMD_PREFIX) flex -o $@ --full $<
+
+%.so: %.o
+ $(ECHO_PREFIX) echo " [LD] $^ -> $@"
+ $(CMD_PREFIX) $(CC) $(STATIC_BUILD) -o $@ $(CC_LDFLAGS_SO) $^ $(CC_LIBS)
+
+%.so: %.oo
+ $(ECHO_PREFIX) echo " [LDXX] $^ -> $@"
+ $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(CXX_LDFLAGS_SO) $^ $(CXX_LIBS)
+
+%: %.o
+ $(ECHO_PREFIX) echo " [LD] $^ -> $@"
+ $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $^ $(CXX_LIBS)
+
+dist-clean:: clean
diff --git a/README b/README
new file mode 100644
index 000000000..3530007a6
--- /dev/null
+++ b/README
@@ -0,0 +1,262 @@
+The Asterisk(R) Open Source PBX
+by Mark Spencer <markster@digium.com>
+and the Asterisk.org developer community
+
+Copyright (C) 2001-2009 Digium, Inc.
+and other copyright holders.
+================================================================
+
+* SECURITY
+ It is imperative that you read and fully understand the contents of
+the security information file (doc/security.txt) before you attempt
+to configure and run an Asterisk server.
+
+* WHAT IS ASTERISK?
+ Asterisk is an Open Source PBX and telephony toolkit. It is, in a
+sense, middleware between Internet and telephony channels on the bottom,
+and Internet and telephony applications at the top. For more information
+on the project itself, please visit the Asterisk home page at:
+
+ http://www.asterisk.org
+
+In addition you'll find lots of information compiled by the Asterisk
+community on this Wiki:
+
+ http://www.voip-info.org/wiki-Asterisk
+
+There is a book on Asterisk published by O'Reilly under the
+Creative Commons License. It is available in book stores as well
+as in a downloadable version on the http://www.asteriskdocs.org
+web site.
+
+* SUPPORTED OPERATING SYSTEMS
+
+== Linux ==
+ The Asterisk Open Source PBX is developed and tested primarily on the
+GNU/Linux operating system, and is supported on every major GNU/Linux
+distribution.
+
+== Others ==
+ Asterisk has also been 'ported' and reportedly runs properly on other
+operating systems as well, including Sun Solaris, Apple's Mac OS X, and
+the BSD variants.
+
+* GETTING STARTED
+
+ First, be sure you've got supported hardware (but note that you don't need
+ANY special hardware, not even a soundcard) to install and run Asterisk.
+
+ Supported telephony hardware includes:
+
+ * All Wildcard (tm) products from Digium (www.digium.com)
+ * QuickNet Internet PhoneJack and LineJack (http://www.quicknet.net)
+ * any full duplex sound card supported by ALSA or OSS
+ * any ISDN card supported by mISDN on Linux (BRI)
+ * The Xorcom AstriBank channel bank
+ * VoiceTronix OpenLine products
+
+The are several drivers for ISDN BRI cards available from third party sources.
+Check the voip-info.org wiki for more information on chan_capi and
+zaphfc.
+
+* UPGRADING FROM AN EARLIER VERSION
+
+ If you are updating from a previous version of Asterisk, make sure you
+read the UPGRADE.txt file in the source directory. There are some files
+and configuration options that you will have to change, even though we
+made every effort possible to maintain backwards compatibility.
+
+ In order to discover new features to use, please check the configuration
+examples in the /configs directory of the source code distribution.
+To discover the major new features of Asterisk 1.2, please visit
+http://edvina.net/asterisk1-2/
+
+* NEW INSTALLATIONS
+
+ Ensure that your system contains a compatible compiler and development
+libraries. Asterisk requires either the GNU Compiler Collection (GCC) version
+3.0 or higher, or a compiler that supports the C99 specification and some of
+the gcc language extensions. In addition, your system needs to have the C
+library headers available, and the headers and libraries for OpenSSL,
+ncurses and zlib.
+On many distributions, these files are installed by packages with names like
+'glibc-devel', 'ncurses-devel', 'openssl-devel' and 'zlib-devel' or similar.
+
+ So let's proceed:
+
+1) Read this README file.
+
+ There are more documents than this one in the doc/ directory.
+You may also want to check the configuration files that contain
+examples and reference guides. They are all in the configs/
+directory.
+
+2) Run "./configure"
+
+ Execute the configure script to guess values for system-dependent
+variables used during compilation.
+
+3) Run "make menuselect" [optional]
+
+ This is needed if you want to select the modules that will be
+compiled and to check modules dependencies.
+
+4) Run "make"
+
+ Assuming the build completes successfully:
+
+5) Run "make install"
+
+ Each time you update or checkout from the repository, you are strongly
+encouraged to ensure all previous object files are removed to avoid internal
+inconsistency in Asterisk. Normally, this is automatically done with
+the presence of the file .cleancount, which increments each time a 'make clean'
+is required, and the file .lastclean, which contains the last .cleancount used.
+
+ If this is your first time working with Asterisk, you may wish to install
+the sample PBX, with demonstration extensions, etc. If so, run:
+
+6) "make samples"
+
+ Doing so will overwrite any existing config files you have.
+
+ Finally, you can launch Asterisk in the foreground mode (not a daemon)
+with:
+
+# asterisk -vvvc
+
+ You'll see a bunch of verbose messages fly by your screen as Asterisk
+initializes (that's the "very very verbose" mode). When it's ready, if
+you specified the "c" then you'll get a command line console, that looks
+like this:
+
+*CLI>
+
+ You can type "help" at any time to get help with the system. For help
+with a specific command, type "help <command>". To start the PBX using
+your sound card, you can type "dial" to dial the PBX. Then you can use
+"answer", "hangup", and "dial" to simulate the actions of a telephone.
+Remember that if you don't have a full duplex sound card (and Asterisk
+will tell you somewhere in its verbose messages if you do/don't) then it
+won't work right (not yet).
+
+ "man asterisk" at the Unix/Linux command prompt will give you detailed
+information on how to start and stop Asterisk, as well as all the command
+line options for starting Asterisk.
+
+ Feel free to look over the configuration files in /etc/asterisk, where
+you'll find a lot of information about what you can do with Asterisk.
+
+* ABOUT CONFIGURATION FILES
+
+ All Asterisk configuration files share a common format. Comments are
+delimited by ';' (since '#' of course, being a DTMF digit, may occur in
+many places). A configuration file is divided into sections whose names
+appear in []'s. Each section typically contains two types of statements,
+those of the form 'variable = value', and those of the form 'object =>
+parameters'. Internally the use of '=' and '=>' is exactly the same, so
+they're used only to help make the configuration file easier to
+understand, and do not affect how it is actually parsed.
+
+ Entries of the form 'variable=value' set the value of some parameter in
+asterisk. For example, in chan_dahdi.conf, one might specify:
+
+ switchtype=national
+
+in order to indicate to Asterisk that the switch they are connecting to is
+of the type "national". In general, the parameter will apply to
+instantiations which occur below its specification. For example, if the
+configuration file read:
+
+ switchtype = national
+ channel => 1-4
+ channel => 10-12
+ switchtype = dms100
+ channel => 25-47
+
+the "national" switchtype would be applied to channels one through
+four and channels 10 through 12, whereas the "dms100" switchtype would
+apply to channels 25 through 47.
+
+ The "object => parameters" instantiates an object with the given
+parameters. For example, the line "channel => 25-47" creates objects for
+the channels 25 through 47 of the card, obtaining the settings
+from the variables specified above.
+
+* SPECIAL NOTE ON TIME
+
+ Those using SIP phones should be aware that Asterisk is sensitive to
+large jumps in time. Manually changing the system time using date(1)
+(or other similar commands) may cause SIP registrations and other
+internal processes to fail. If your system cannot keep accurate time
+by itself use NTP (http://www.ntp.org/) to keep the system clock
+synchronized to "real time". NTP is designed to keep the system clock
+synchronized by speeding up or slowing down the system clock until it
+is synchronized to "real time" rather than by jumping the time and
+causing discontinuities. Most Linux distributions include precompiled
+versions of NTP. Beware of some time synchronization methods that get
+the correct real time periodically and then manually set the system
+clock.
+
+ Apparent time changes due to daylight savings time are just that,
+apparent. The use of daylight savings time in a Linux system is
+purely a user interface issue and does not affect the operation of the
+Linux kernel or Asterisk. The system clock on Linux kernels operates
+on UTC. UTC does not use daylight savings time.
+
+ Also note that this issue is separate from the clocking of TDM
+channels, and is known to at least affect SIP registrations.
+
+* FILE DESCRIPTORS
+
+ Depending on the size of your system and your configuration,
+Asterisk can consume a large number of file descriptors. In UNIX,
+file descriptors are used for more than just files on disk. File
+descriptors are also used for handling network communication
+(e.g. SIP, IAX2, or H.323 calls) and hardware access (e.g. analog and
+digital trunk hardware). Asterisk accesses many on-disk files for
+everything from configuration information to voicemail storage.
+
+ Most systems limit the number of file descriptors that Asterisk can
+have open at one time. This can limit the number of simultaneous
+calls that your system can handle. For example, if the limit is set
+at 1024 (a common default value) Asterisk can handle approxiately 150
+SIP calls simultaneously. To change the number of file descriptors
+follow the instructions for your system below:
+
+== PAM-based Linux System ==
+
+ If your system uses PAM (Pluggable Authentication Modules) edit
+/etc/security/limits.conf. Add these lines to the bottom of the file:
+
+root soft nofile 4096
+root hard nofile 8196
+asterisk soft nofile 4096
+asterisk hard nofile 8196
+
+(adjust the numbers to taste). You may need to reboot the system for
+these changes to take effect.
+
+== Generic UNIX System ==
+
+ If there are no instructions specifically adapted to your system
+above you can try adding the command "ulimit -n 8192" to the script
+that starts Asterisk.
+
+* MORE INFORMATION
+
+ See the doc directory for more documentation on various features. Again,
+please read all the configuration samples that include documentation on
+the configuration options.
+
+ Finally, you may wish to visit the web site and join the mailing list if
+you're interested in getting more information.
+
+ http://www.asterisk.org/support
+
+ Welcome to the growing worldwide community of Asterisk users!
+
+Mark Spencer
+
+----
+Asterisk is a trademark belonging to Digium, inc
diff --git a/UPGRADE-1.2.txt b/UPGRADE-1.2.txt
new file mode 100644
index 000000000..7070bec11
--- /dev/null
+++ b/UPGRADE-1.2.txt
@@ -0,0 +1,210 @@
+=========================================================
+=== Information for upgrading from Asterisk 1.0 to 1.2
+===
+===
+=== UPGRADE-1.2.txt -- Upgrade info for 1.0 to 1.2
+=== UPGRADE.txt -- Upgrade info for 1.2 to 1.4
+=========================================================
+
+Compiling:
+
+* The Asterisk 1.2 source code now uses C language features
+ supported only by 'modern' C compilers. Generally, this means GCC
+ version 3.0 or higher, although some GCC 2.96 releases will also
+ work. Some non-GCC compilers that support C99 and the common GCC
+ extensions (including anonymous structures and unions) will also
+ work. All releases of GCC 2.95 do _not_ have the requisite feature
+ support; systems using that compiler will need to be upgraded to
+ a more recent compiler release.
+
+Dialplan Expressions:
+
+* The dialplan expression parser (which handles $[ ... ] constructs)
+ has gone through a major upgrade, but has one incompatible change:
+ spaces are no longer required around expression operators, including
+ string comparisons. However, you can now use quoting to keep strings
+ together for comparison. For more details, please read the
+ doc/README.variables file, and check over your dialplan for possible
+ problems.
+
+Agents:
+
+* The default for ackcall has been changed to "no" instead of "yes"
+ because of a bug which caused the "yes" behavior to generally act like
+ "no". You may need to adjust the value if your agents behave
+ differently than you expect with respect to acknowledgement.
+
+* The AgentCallBackLogin application now requires a second '|' before
+ specifying an extension@context. This is to distinguish the options
+ string from the extension, so that they do not conflict. See
+ 'show application AgentCallbackLogin' for more details.
+
+Parking:
+
+* Parking behavior has changed slightly; when a parked call times out,
+ Asterisk will attempt to deliver the call back to the extension that
+ parked it, rather than the 's' extension. If that extension is busy
+ or unavailable, the parked call will be lost.
+
+Dialing:
+
+* The Caller*ID of the outbound leg is now the extension that was
+ called, rather than the Caller*ID of the inbound leg of the call. The
+ "o" flag for Dial can be used to restore the original behavior if
+ desired. Note that if you are looking for the originating callerid
+ from the manager event, there is a new manager event "Dial" which
+ provides the source and destination channels and callerid.
+
+IAX:
+
+* The naming convention for IAX channels has changed in two ways:
+ 1. The call number follows a "-" rather than a "/" character.
+ 2. The name of the channel has been simplified to IAX2/peer-callno,
+ rather than IAX2/peer@peer-callno or even IAX2/peer@peer/callno.
+
+SIP:
+
+* The global option "port" in 1.0.X that is used to set which port to
+ bind to has been changed to "bindport" to be more consistent with
+ the other channel drivers and to avoid confusion with the "port"
+ option for users/peers.
+
+* The "Registry" event now uses "Username" rather than "User" for
+ consistency with IAX.
+
+Applications:
+
+* With the addition of dialplan functions (which operate similarly
+ to variables), the SetVar application has been renamed to Set.
+
+* The CallerPres application has been removed. Use SetCallerPres
+ instead. It accepts both numeric and symbolic names.
+
+* The applications GetGroupCount, GetGroupMatchCount, SetGroup, and
+ CheckGroup have been deprecated in favor of functions. Here is a
+ table of their replacements:
+
+ GetGroupCount([groupname][@category] GROUP_COUNT([groupname][@category]) Set(GROUPCOUNT=${GROUP_COUNT()})
+ GroupMatchCount(groupmatch[@category]) GROUP_MATCH_COUNT(groupmatch[@category]) Set(GROUPCOUNT=${GROUP_MATCH_COUNT(SIP/.*)})
+ SetGroup(groupname[@category]) GROUP([category])=groupname Set(GROUP()=test)
+ CheckGroup(max[@category]) N/A GotoIf($[ ${GROUP_COUNT()} > 5 ]?103)
+
+ Note that CheckGroup does not have a direct replacement. There is
+ also a new function called GROUP_LIST() which will return a space
+ separated list of all of the groups set on a channel. The GROUP()
+ function can also return the name of the group set on a channel when
+ used in a read environment.
+
+* The applications DBGet and DBPut have been deprecated in favor of
+ functions. Here is a table of their replacements:
+
+ DBGet(foo=family/key) Set(foo=${DB(family/key)})
+ DBPut(family/key=${foo}) Set(DB(family/key)=${foo})
+
+* The application SetLanguage has been deprecated in favor of the
+ function LANGUAGE().
+
+ SetLanguage(fr) Set(LANGUAGE()=fr)
+
+ The LANGUAGE function can also return the currently set language:
+
+ Set(MYLANG=${LANGUAGE()})
+
+* The applications AbsoluteTimeout, DigitTimeout, and ResponseTimeout
+ have been deprecated in favor of the function TIMEOUT(timeouttype):
+
+ AbsoluteTimeout(300) Set(TIMEOUT(absolute)=300)
+ DigitTimeout(15) Set(TIMEOUT(digit)=15)
+ ResponseTimeout(15) Set(TIMEOUT(response)=15)
+
+ The TIMEOUT() function can also return the currently set timeouts:
+
+ Set(DTIMEOUT=${TIMEOUT(digit)})
+
+* The applications SetCIDName, SetCIDNum, and SetRDNIS have been
+ deprecated in favor of the CALLERID(datatype) function:
+
+ SetCIDName(Joe Cool) Set(CALLERID(name)=Joe Cool)
+ SetCIDNum(2025551212) Set(CALLERID(number)=2025551212)
+ SetRDNIS(2024561414) Set(CALLERID(RDNIS)=2024561414)
+
+* The application Record now uses the period to separate the filename
+ from the format, rather than the colon.
+
+* The application VoiceMail now supports a 'temporary' greeting for each
+ mailbox. This greeting can be recorded by using option 4 in the
+ 'mailbox options' menu, and 'change your password' option has been
+ moved to option 5.
+
+* The application VoiceMailMain now only matches the 'default' context if
+ none is specified in the arguments. (This was the previously
+ documented behavior, however, we didn't follow that behavior.) The old
+ behavior can be restored by setting searchcontexts=yes in voicemail.conf.
+
+Queues:
+
+* A queue is now considered empty not only if there are no members but if
+ none of the members are available (e.g. agents not logged on). To
+ restore the original behavior, use "leavewhenempty=strict" or
+ "joinwhenempty=strict" instead of "=yes" for those options.
+
+* It is now possible to use multi-digit extensions in the exit context
+ for a queue (although you should not have overlapping extensions,
+ as there is no digit timeout). This means that the EXITWITHKEY event
+ in queue_log can now contain a key field with more than a single
+ character in it.
+
+Extensions:
+
+* By default, there is a new option called "autofallthrough" in
+ extensions.conf that is set to yes. Asterisk 1.0 (and earlier)
+ behavior was to wait for an extension to be dialed after there were no
+ more extensions to execute. "autofallthrough" changes this behavior
+ so that the call will immediately be terminated with BUSY,
+ CONGESTION, or HANGUP based on Asterisk's best guess. If you are
+ writing an extension for IVR, you must use the WaitExten application
+ if "autofallthrough" is set to yes.
+
+AGI:
+
+* AGI scripts did not always get SIGHUP at the end, previously. That
+ behavior has been fixed. If you do not want your script to terminate
+ at the end of AGI being called (e.g. on a hangup) then set SIGHUP to
+ be ignored within your application.
+
+* CallerID is reported with agi_callerid and agi_calleridname instead
+ of a single parameter holding both.
+
+Music On Hold:
+
+* The preferred format for musiconhold.conf has changed; please see the
+ sample configuration file for the new format. The existing format
+ is still supported but will generate warnings when the module is loaded.
+
+chan_modem:
+
+* All the chan_modem channel drivers (aopen, bestdata and i4l) are deprecated
+ in this release, and will be removed in the next major Asterisk release.
+ Please migrate to chan_misdn for ISDN interfaces; there is no upgrade
+ path for aopen and bestdata modem users.
+
+MeetMe:
+
+* The conference application now allows users to increase/decrease their
+ speaking volume and listening volume (independently of each other and
+ other users); the 'admin' and 'user' menus have changed, and new sound
+ files are included with this release. However, if a user calling in
+ over a Zaptel channel that does NOT have hardware DTMF detection
+ increases their speaking volume, it is likely they will no longer be
+ able to enter/exit the menu or make any further adjustments, as the
+ software DTMF detector will not be able to recognize the DTMF coming
+ from their device.
+
+GetVar Manager Action:
+
+* Previously, the behavior of the GetVar manager action reported the value
+ of a variable in the following manner:
+ > name: value
+ This has been changed to a manner similar to the SetVar action and is now
+ > Variable: name
+ > Value: value
diff --git a/UPGRADE.txt b/UPGRADE.txt
new file mode 100644
index 000000000..6dad890ea
--- /dev/null
+++ b/UPGRADE.txt
@@ -0,0 +1,500 @@
+=========================================================
+=== Information for upgrading from Asterisk 1.2 to 1.4
+===
+===
+=== UPGRADE-1.2.txt -- Upgrade info for 1.0 to 1.2
+=== UPGRADE.txt -- Upgrade info for 1.2 to 1.4
+=========================================================
+
+Build Process (configure script):
+
+Asterisk now uses an autoconf-generated configuration script to learn how it
+should build itself for your system. As it is a standard script, running:
+
+$ ./configure --help
+
+will show you all the options available. This script can be used to tell the
+build process what libraries you have on your system (if it cannot find them
+automatically), which libraries you wish to have ignored even though they may
+be present, etc.
+
+You must run the configure script before Asterisk will build, although it will
+attempt to automatically run it for you with no options specified; for most
+users, that will result in a similar build to what they would have had before
+the configure script was added to the build process (except for having to run
+'make' again after the configure script is run). Note that the configure script
+does NOT need to be re-run just to rebuild Asterisk; you only need to re-run it
+when your system configuration changes or you wish to build Asterisk with
+different options.
+
+Build Process (module selection):
+
+The Asterisk source tree now includes a basic module selection and build option
+selection tool called 'menuselect'. Run 'make menuselect' to make your choices.
+In this tool, you can disable building of modules that you don't care about,
+turn on/off global options for the build and see which modules will not
+(and cannot) be built because your system does not have the required external
+dependencies installed.
+
+The resulting file from menuselect is called 'menuselect.makeopts'. Note that
+the resulting menuselect.makeopts file generally contains which modules *not*
+to build. The modules listed in this file indicate which modules have unmet
+dependencies, a present conflict, or have been disabled by the user in the
+menuselect interface. Compiler Flags can also be set in the menuselect
+interface. In this case, the resulting file contains which CFLAGS are in use,
+not which ones are not in use.
+
+If you would like to save your choices and have them applied against all
+builds, the file can be copied to '~/.asterisk.makeopts' or
+'/etc/asterisk.makeopts'.
+
+Build Process (Makefile targets):
+
+The 'valgrind' and 'dont-optimize' targets have been removed; their functionality
+is available by enabling the DONT_OPTIMIZE setting in the 'Compiler Flags' menu
+in the menuselect tool.
+
+It is now possible to run most make targets against a single subdirectory; from
+the top level directory, for example, 'make channels' will run 'make all' in the
+'channels' subdirectory. This also is true for 'clean', 'distclean' and 'depend'.
+
+Sound (prompt) and Music On Hold files:
+
+Beginning with Asterisk 1.4, the sound files and music on hold files supplied for
+use with Asterisk have been replaced with new versions produced from high quality
+master recordings, and are available in three languages (English, French and
+Spanish) and in five formats (WAV (uncompressed), mu-Law, a-Law, GSM and G.729).
+In addition, the music on hold files provided by FreePlay Music are now available
+in the same five formats, but no longer available in MP3 format.
+
+The Asterisk 1.4 tarball packages will only include English prompts in GSM format,
+(as were supplied with previous releases) and the FreePlay MOH files in WAV format.
+All of the other variations can be installed by running 'make menuselect' and
+selecting the packages you wish to install; when you run 'make install', those
+packages will be downloaded and installed along with the standard files included
+in the tarball.
+
+If for some reason you expect to not have Internet access at the time you will be
+running 'make install', you can make your package selections using menuselect and
+then run 'make sounds' to download (only) the sound packages; this will leave the
+sound packages in the 'sounds' subdirectory to be used later during installation.
+
+WARNING: Asterisk 1.4 supports a new layout for sound files in multiple languages;
+instead of the alternate-language files being stored in subdirectories underneath
+the existing files (for French, that would be digits/fr, letters/fr, phonetic/fr,
+etc.) the new layout creates one directory under /var/lib/asterisk/sounds for the
+language itself, then places all the sound files for that language under that
+directory and its subdirectories. This is the layout that will be created if you
+select non-English languages to be installed via menuselect, HOWEVER Asterisk does
+not default to this layout and will not find the files in the places it expects them
+to be. If you wish to use this layout, make sure you put 'languageprefix=yes' in your
+/etc/asterisk/asterisk.conf file, so that Asterisk will know how the files were
+installed.
+
+PBX Core:
+
+* The (very old and undocumented) ability to use BYEXTENSION for dialing
+ instead of ${EXTEN} has been removed.
+
+* Builtin (res_features) transfer functionality attempts to use the context
+ defined in TRANSFER_CONTEXT variable of the transferer channel first. If
+ not set, it uses the transferee variable. If not set in any channel, it will
+ attempt to use the last non macro context. If not possible, it will default
+ to the current context.
+
+* The autofallthrough setting introduced in Asterisk 1.2 now defaults to 'yes';
+ if your dialplan relies on the ability to 'run off the end' of an extension
+ and wait for a new extension without using WaitExten() to accomplish that,
+ you will need set autofallthrough to 'no' in your extensions.conf file.
+
+Command Line Interface:
+
+* 'show channels concise', designed to be used by applications that will parse
+ its output, previously used ':' characters to separate fields. However, some
+ of those fields can easily contain that character, making the output not
+ parseable. The delimiter has been changed to '!'.
+
+Applications:
+
+* In previous Asterisk releases, many applications would jump to priority n+101
+ to indicate some kind of status or error condition. This functionality was
+ marked deprecated in Asterisk 1.2. An option to disable it was provided with
+ the default value set to 'on'. The default value for the global priority
+ jumping option is now 'off'.
+
+* The applications Cut, Sort, DBGet, DBPut, SetCIDNum, SetCIDName, SetRDNIS,
+ AbsoluteTimeout, DigitTimeout, ResponseTimeout, SetLanguage, GetGroupCount,
+ and GetGroupMatchCount were all deprecated in version 1.2, and therefore have
+ been removed in this version. You should use the equivalent dialplan
+ function in places where you have previously used one of these applications.
+
+* The application SetGlobalVar has been deprecated. You should replace uses
+ of this application with the following combination of Set and GLOBAL():
+ Set(GLOBAL(name)=value). You may also access global variables exclusively by
+ using the GLOBAL() dialplan function, instead of relying on variable
+ interpolation falling back to globals when no channel variable is set.
+
+* The application SetVar has been renamed to Set. The syntax SetVar was marked
+ deprecated in version 1.2 and is no longer recognized in this version. The
+ use of Set with multiple argument pairs has also been deprecated. Please
+ separate each name/value pair into its own dialplan line.
+
+* app_read has been updated to use the newer options codes, using "skip" or
+ "noanswer" will not work. Use s or n. Also there is a new feature i, for
+ using indication tones, so typing in skip would give you unexpected results.
+
+* OSPAuth is added to authenticate OSP tokens in in_bound call setup messages.
+
+* The CONNECT event in the queue_log from app_queue now has a second field
+ in addition to the holdtime field. It contains the unique ID of the
+ queue member channel that is taking the call. This is useful when trying
+ to link recording filenames back to a particular call from the queue.
+
+* The old/current behavior of app_queue has a serial type behavior
+ in that the queue will make all waiting callers wait in the queue
+ even if there is more than one available member ready to take
+ calls until the head caller is connected with the member they
+ were trying to get to. The next waiting caller in line then
+ becomes the head caller, and they are then connected with the
+ next available member and all available members and waiting callers
+ waits while this happens. This cycle continues until there are
+ no more available members or waiting callers, whichever comes first.
+ The new behavior, enabled by setting autofill=yes in queues.conf
+ either at the [general] level to default for all queues or
+ to set on a per-queue level, makes sure that when the waiting
+ callers are connecting with available members in a parallel fashion
+ until there are no more available members or no more waiting callers,
+ whichever comes first. This is probably more along the lines of how
+ one would expect a queue should work and in most cases, you will want
+ to enable this new behavior. If you do not specify or comment out this
+ option, it will default to "no" to keep backward compatability with the old
+ behavior.
+
+* Queues depend on the channel driver reporting the proper state
+ for each member of the queue. To get proper signalling on
+ queue members that use the SIP channel driver, you need to
+ enable a call limit (could be set to a high value so it
+ is not put into action) and also make sure that both inbound
+ and outbound calls are accounted for.
+
+ Example:
+
+ [general]
+ limitonpeer = yes
+
+ [peername]
+ type=friend
+ call-limit=10
+
+
+* The app_queue application now has the ability to use MixMonitor to
+ record conversations queue members are having with queue callers. Please
+ see configs/queues.conf.sample for more information on this option.
+
+* The app_queue application strategy called 'roundrobin' has been deprecated
+ for this release. Users are encouraged to use 'rrmemory' instead, since it
+ provides more 'true' round-robin call delivery. For the Asterisk 1.6 release,
+ 'rrmemory' will be renamed 'roundrobin'.
+
+* The app_queue application option called 'monitor-join' has been deprecated
+ for this release. Users are encouraged to use 'monitor-type=mixmonitor' instead,
+ since it provides the same functionality but is not dependent on soxmix or some
+ other external program in order to mix the audio.
+
+* app_meetme: The 'm' option (monitor) is renamed to 'l' (listen only), and
+ the 'm' option now provides the functionality of "initially muted".
+ In practice, most existing dialplans using the 'm' flag should not notice
+ any difference, unless the keypad menu is enabled, allowing the user
+ to unmute themsleves.
+
+* ast_play_and_record would attempt to cancel the recording if a DTMF
+ '0' was received. This behavior was not documented in most of the
+ applications that used ast_play_and_record and the return codes from
+ ast_play_and_record weren't checked for properly.
+ ast_play_and_record has been changed so that '0' no longer cancels a
+ recording. If you want to allow DTMF digits to cancel an
+ in-progress recording use ast_play_and_record_full which allows you
+ to specify which DTMF digits can be used to accept a recording and
+ which digits can be used to cancel a recording.
+
+* ast_app_messagecount has been renamed to ast_app_inboxcount. There is now a
+ new ast_app_messagecount function which takes a single context/mailbox/folder
+ mailbox specification and returns the message count for that folder only.
+ This addresses the deficiency of not being able to count the number of
+ messages in folders other than INBOX and Old.
+
+* The exit behavior of the AGI applications has changed. Previously, when
+ a connection to an AGI server failed, the application would cause the channel
+ to immediately stop dialplan execution and hangup. Now, the only time that
+ the AGI applications will cause the channel to stop dialplan execution is
+ when the channel itself requests hangup. The AGI applications now set an
+ AGISTATUS variable which will allow you to find out whether running the AGI
+ was successful or not.
+
+ Previously, there was no way to handle the case where Asterisk was unable to
+ locally execute an AGI script for some reason. In this case, dialplan
+ execution will continue as it did before, but the AGISTATUS variable will be
+ set to "FAILURE".
+
+ A locally executed AGI script can now exit with a non-zero exit code and this
+ failure will be detected by Asterisk. If an AGI script exits with a non-zero
+ exit code, the AGISTATUS variable will be set to "FAILURE" as opposed to
+ "SUCCESS".
+
+* app_voicemail: The ODBC_STORAGE capability now requires the extended table format
+ previously used only by EXTENDED_ODBC_STORAGE. This means that you will need to update
+ your table format using the schema provided in doc/odbcstorage.txt
+
+* app_waitforsilence: Fixes have been made to this application which changes the
+ default behavior with how quickly it returns. You can maintain "old-style" behavior
+ with the addition/use of a third "timeout" parameter.
+ Please consult the application documentation and make changes to your dialplan
+ if appropriate.
+
+Manager:
+
+* After executing the 'status' manager action, the "Status" manager events
+ included the header "CallerID:" which was actually only the CallerID number,
+ and not the full CallerID string. This header has been renamed to
+ "CallerIDNum". For compatibility purposes, the CallerID parameter will remain
+ until after the release of 1.4, when it will be removed. Please use the time
+ during the 1.4 release to make this transition.
+
+* The AgentConnect event now has an additional field called "BridgedChannel"
+ which contains the unique ID of the queue member channel that is taking the
+ call. This is useful when trying to link recording filenames back to
+ a particular call from the queue.
+
+* app_userevent has been modified to always send Event: UserEvent with the
+ additional header UserEvent: <userspec>. Also, the Channel and UniqueID
+ headers are not automatically sent, unless you specify them as separate
+ arguments. Please see the application help for the new syntax.
+
+* app_meetme: Mute and Unmute events are now reported via the Manager API.
+ Native Manager API commands MeetMeMute and MeetMeUnmute are provided, which
+ are easier to use than "Action Command:". The MeetMeStopTalking event has
+ also been deprecated in favor of the already existing MeetmeTalking event
+ with a "Status" of "on" or "off" added.
+
+* OriginateFailure and OriginateSuccess events were replaced by event
+ OriginateResponse with a header named "Response" to indicate success or
+ failure
+
+Variables:
+
+* The builtin variables ${CALLERID}, ${CALLERIDNAME}, ${CALLERIDNUM},
+ ${CALLERANI}, ${DNID}, ${RDNIS}, ${DATETIME}, ${TIMESTAMP}, ${ACCOUNTCODE},
+ and ${LANGUAGE} have all been deprecated in favor of their related dialplan
+ functions. You are encouraged to move towards the associated dialplan
+ function, as these variables will be removed in a future release.
+
+* The CDR-CSV variables uniqueid, userfield, and basing time on GMT are now
+ adjustable from cdr.conf, instead of recompiling.
+
+* OSP applications exports several new variables, ${OSPINHANDLE},
+ ${OSPOUTHANDLE}, ${OSPINTOKEN}, ${OSPOUTTOKEN}, ${OSPCALLING},
+ ${OSPINTIMELIMIT}, and ${OSPOUTTIMELIMIT}
+
+* Builtin transfer functionality sets the variable ${TRANSFERERNAME} in the new
+ created channel. This variables holds the channel name of the transferer.
+
+* The dial plan variable PRI_CAUSE will be removed from future versions
+ of Asterisk.
+ It is replaced by adding a cause value to the hangup() application.
+
+Functions:
+
+* The function ${CHECK_MD5()} has been deprecated in favor of using an
+ expression: $[${MD5(<string>)} = ${saved_md5}].
+
+* The 'builtin' functions that used to be combined in pbx_functions.so are
+ now built as separate modules. If you are not using 'autoload=yes' in your
+ modules.conf file then you will need to explicitly load the modules that
+ contain the functions you want to use.
+
+* The ENUMLOOKUP() function with the 'c' option (for counting the number of
+ records), but the lookup fails to match any records, the returned value will
+ now be "0" instead of blank.
+
+* The REALTIME() function is now available in version 1.4 and app_realtime has
+ been deprecated in favor of the new function. app_realtime will be removed
+ completely with the version 1.6 release so please take the time between
+ releases to make any necessary changes
+
+* The QUEUEAGENTCOUNT() function has been deprecated in favor of
+ QUEUE_MEMBER_COUNT().
+
+The IAX2 channel:
+
+* It is possible that previous configurations depended on the order in which
+ peers and users were specified in iax.conf for forcing the order in which
+ chan_iax2 matched against them. This behavior is going away and is considered
+ deprecated in this version. Avoid having ambiguous peer and user entries and
+ to make things easy on yourself, always set the "username" option for users
+ so that the remote end can match on that exactly instead of trying to infer
+ which user you want based on host.
+
+ If you would like to go ahead and use the new behavior which doesn't use the
+ order in the config file to influence matching order, then change the
+ MAX_PEER_BUCKETS define in chan_iax2.c to a value greater than one. An
+ example is provided there. By changing this, you will get *much* better
+ performance on systems that do a lot of peer and user lookups as they will be
+ stored in memory in a much more efficient manner.
+
+* The "mailboxdetail" option has been deprecated. Previously, if this option
+ was not enabled, the 2 byte MSGCOUNT information element would be set to all
+ 1's to indicate there there is some number of messages waiting. With this
+ option enabled, the number of new messages were placed in one byte and the
+ number of old messages are placed in the other. This is now the default
+ (and the only) behavior.
+
+The SIP channel:
+
+* The "incominglimit" setting is replaced by the "call-limit" setting in
+ sip.conf.
+
+* OSP support code is removed from SIP channel to OSP applications. ospauth
+ option in sip.conf is removed to osp.conf as authpolicy. allowguest option
+ in sip.conf cannot be set as osp anymore.
+
+* The Asterisk RTP stack has been changed in regards to RFC2833 reception
+ and transmission. Packets will now be sent with proper duration instead of all
+ at once. If you are receiving calls from a pre-1.4 Asterisk installation you
+ will want to turn on the rfc2833compensate option. Without this option your
+ DTMF reception may act poorly.
+
+* The $SIPUSERAGENT dialplan variable is deprecated and will be removed
+ in coming versions of Asterisk. Please use the dialplan function
+ SIPCHANINFO(useragent) instead.
+
+* The ALERT_INFO dialplan variable is deprecated and will be removed
+ in coming versions of Asterisk. Please use the dialplan application
+ sipaddheader() to add the "Alert-Info" header to the outbound invite.
+
+* The "canreinvite" option has changed. canreinvite=yes used to disable
+ re-invites if you had NAT=yes. In 1.4, you need to set canreinvite=nonat
+ to disable re-invites when NAT=yes. This is propably what you want.
+ The settings are now: "yes", "no", "nonat", "update". Please consult
+ sip.conf.sample for detailed information.
+
+The Zap channel:
+
+* Support for MFC/R2 has been removed, as it has not been functional for some
+ time and it has no maintainer.
+
+The Agent channel:
+
+* Callback mode (AgentCallbackLogin) is now deprecated, since the entire function
+ it provided can be done using dialplan logic, without requiring additional
+ channel and module locks (which frequently caused deadlocks). An example of
+ how to do this using AEL dialplan is in doc/queues-with-callback-members.txt.
+
+The G726-32 codec:
+
+* It has been determined that previous versions of Asterisk used the wrong codeword
+ packing order for G726-32 data. This version supports both available packing orders,
+ and can transcode between them. It also now selects the proper order when
+ negotiating with a SIP peer based on the codec name supplied in the SDP. However,
+ there are existing devices that improperly request one order and then use another;
+ Sipura and Grandstream ATAs are known to do this, and there may be others. To
+ be able to continue to use these devices with this version of Asterisk and the
+ G726-32 codec, a configuration parameter called 'g726nonstandard' has been added
+ to sip.conf, so that Asterisk can use the packing order expected by the device (even
+ though it requested a different order). In addition, the internal format number for
+ G726-32 has been changed, and the old number is now assigned to AAL2-G726-32. The
+ result of this is that this version of Asterisk will be able to interoperate over
+ IAX2 with older versions of Asterisk, as long as this version is told to allow
+ 'g726aal2' instead of 'g726' as the codec for the call.
+
+Installation:
+
+* On BSD systems, the installation directories have changed to more "FreeBSDish"
+ directories. On startup, Asterisk will look for the main configuration in
+ /usr/local/etc/asterisk/asterisk.conf
+ If you have an old installation, you might want to remove the binaries and
+ move the configuration files to the new locations. The following directories
+ are now default:
+ ASTLIBDIR /usr/local/lib/asterisk
+ ASTVARLIBDIR /usr/local/share/asterisk
+ ASTETCDIR /usr/local/etc/asterisk
+ ASTBINDIR /usr/local/bin/asterisk
+ ASTSBINDIR /usr/local/sbin/asterisk
+
+Music on Hold:
+
+* The music on hold handling has been changed in some significant ways in hopes
+ to make it work in a way that is much less confusing to users. Behavior will
+ not change if the same configuration is used from older versions of Asterisk.
+ However, there are some new configuration options that will make things work
+ in a way that makes more sense.
+
+ Previously, many of the channel drivers had an option called "musicclass" or
+ something similar. This option set what music on hold class this channel
+ would *hear* when put on hold. Some people expected (with good reason) that
+ this option was to configure what music on hold class to play when putting
+ the bridged channel on hold. This option has now been deprecated.
+
+ Two new music on hold related configuration options for channel drivers have
+ been introduced. Some channel drivers support both options, some just one,
+ and some support neither of them. Check the sample configuration files to see
+ which options apply to which channel driver.
+
+ The "mohsuggest" option specifies which music on hold class to suggest to the
+ bridged channel when putting them on hold. The only way that this class can
+ be overridden is if the bridged channel has a specific music class set that
+ was done in the dialplan using Set(CHANNEL(musicclass)=something).
+
+ The "mohinterpret" option is similar to the old "musicclass" option. It
+ specifies which music on hold class this channel would like to listen to when
+ put on hold. This music class is only effective if this channel has no music
+ class set on it from the dialplan and the bridged channel putting this one on
+ hold had no "mohsuggest" setting.
+
+ The IAX2 and Zap channel drivers have an additional feature for the
+ "mohinterpret" option. If this option is set to "passthrough", then these
+ channel drivers will pass through the HOLD message in signalling instead of
+ starting music on hold on the channel. An example for how this would be
+ useful is in an enterprise network of Asterisk servers. When one phone on one
+ server puts a phone on a different server on hold, the remote server will be
+ responsible for playing the hold music to its local phone that was put on
+ hold instead of the far end server across the network playing the music.
+
+CDR Records:
+
+* The behavior of the "clid" field of the CDR has always been that it will
+ contain the callerid ANI if it is set, or the callerid number if ANI was not
+ set. When using the "callerid" option for various channel drivers, some
+ would set ANI and some would not. This has been cleared up so that all
+ channel drivers set ANI. If you would like to change the callerid number
+ on the channel from the dialplan and have that change also show up in the
+ CDR, then you *must* set CALLERID(ANI) as well as CALLERID(num).
+
+API:
+
+* There are some API functions that were not previously prefixed with the 'ast_'
+ prefix but now are; these include the ADSI, ODBC and AGI interfaces. If you
+ have a module that uses the services provided by res_adsi, res_odbc, or
+ res_agi, you will need to add ast_ prefixes to the functions that you call
+ from those modules.
+
+Formats:
+
+* format_wav: The GAIN preprocessor definition has been changed from 2 to 0
+ in Asterisk 1.4. This change was made in response to user complaints of
+ choppiness or the clipping of loud signal peaks. The GAIN preprocessor
+ definition will be retained in Asterisk 1.4, but will be removed in a
+ future release. The use of GAIN for the increasing of voicemail message
+ volume should use the 'volgain' option in voicemail.conf
+
+iLBC Codec:
+
+* Previously, the Asterisk source code distribution included the iLBC
+ encoder/decoder source code, from Global IP Solutions
+ (http://www.gipscorp.com). This code is not licensed for
+ distribution, and thus has been removed from the Asterisk source
+ code distribution. If you wish to use codec_ilbc to support iLBC
+ channels in Asterisk, you can run the contrib/scripts/get_ilbc_source.sh
+ script to download the source and put it in the proper place in
+ the Asterisk build tree. Once that is done you can follow your normal
+ steps of building Asterisk. You will need to run 'menuselect' and enable
+ the iLBC codec in the 'Codec Translators' category.
diff --git a/Zaptel-to-DAHDI.txt b/Zaptel-to-DAHDI.txt
new file mode 100644
index 000000000..a08fdea69
--- /dev/null
+++ b/Zaptel-to-DAHDI.txt
@@ -0,0 +1,105 @@
+=========================================================
+=== Information for upgrading from Zaptel to DAHDI ===
+=========================================================
+
+As announced in early 2008, Digium is renaming the Zaptel telephony
+interface project to DAHDI (Digium Asterisk Hardware Device Interface)
+to accommodate the desires of the owner of the Zaptel trademark for
+telephony purposes.
+
+This version of Asterisk can be built using either Zaptel or DAHDI,
+and has many changes to make the use of DAHDI as easy as possible for
+existing users with dialplans, CDR parsers, AMI applications, and
+others that expect Zaptel to be in use.
+
+First, the modules that directly use services from Zaptel/DAHDI have been
+renamed; the new names are:
+
+ chan_zap.so -> chan_dahdi.so
+ app_zapbarge.so -> app_dahdibarge.so
+ app_zapras.so -> app_dahdiras.so
+ app_zapscan.so -> app_dahdiscan.so
+ codec_zap.so -> codec_dahdi.so
+
+However, in spite of the file name changes, the channels and
+applications provided by these modules can still be used with 'Zap'
+style names; see below for more information.
+
+Second, there are have been a number of efforts made to ensure that
+existing systems will not have to have any major configuration changes
+made solely because Asterisk was built against DAHDI instead of
+Zaptel. This includes:
+
+chan_dahdi.so:
+
+ This module will determine which channel name ('Zap' or 'DAHDI')
+ should be used for incoming and outgoing channels based on the
+ build-time choice of telephony drivers. However, if you wish to
+ continue using the 'Zap' channel name even though you built Asterisk
+ against the DAHDI drivers, you can add the following line to the
+ [options] section of your /etc/asterisk/asterisk.conf file:
+
+ dahdichanname = no
+
+ All CLI commands that begin with 'zap' are now available as 'dahdi'
+ commands as well; the 'zap' variants will report that they are
+ deprecated the first time you use each one in an Asterisk instance,
+ but they will otherwise operate just as they did in previous versions.
+
+ All Asterisk Manager Interface (AMI) actions that begin with 'Zap'
+ are also available with 'DAHDI' prefixes.
+
+ The ZapSendKeypadFacility dialplan application is now available as
+ DAHDISendKeypadFacility as well; the Zap variant will report a deprecation
+ warning but will otherwise operate as it did it in previous
+ versions.
+
+ The configuration for the channel driver will be read from
+ /etc/asterisk/chan_dahdi.conf unless 'dahdichanname' has been set to
+ 'no' in asterisk.conf; if that is done, then the configuration will
+ be read from /etc/asterisk/zapata.conf, just as it was in previous
+ versions.
+
+app_dahdibarge.so:
+
+ The ZapBarge application is now available as DAHDIBarge as well; the
+ ZapBarge variant will report a deprecation warning when used, but
+ will otherwise operate as it did in previous versions. Regardless of
+ which application name is used, the application will restrict itself
+ to channels of the proper type, based on the 'dahdichanname' setting
+ in asterisk.conf.
+
+app_dahdiras.so:
+
+ The ZapRAS application is now available as DAHDIRAS as well; the
+ ZapRAS variant will report a deprecation warning when used, but will
+ otherwise operate as it did in previous versions. Regardless of
+ which application name is used, the application will restrict itself
+ to channels of the proper type, based on the 'dahdichanname' setting
+ in asterisk.conf.
+
+app_dahdiscan.so:
+
+ The ZapScan application is now available as DAHDIScan as well; the
+ ZapScan variant will report a deprecation warning when used, but will
+ otherwise operate as it did in previous versions. Regardless of
+ which application name is used, the application will restrict itself
+ to channels of the proper type, based on the 'dahdichanname' setting
+ in asterisk.conf.
+
+app_flash.so:
+
+ This application has not had any name changes, but will report its
+ usage (via 'show application flash') as being for either DAHDI or
+ Zaptel channels based on the 'dahdichanname' setting in
+ asterisk.conf.
+
+app_chanspy.so:
+
+ This application will transparently create 'DAHDI' or 'Zap' channels
+ as needed, based on the 'dahdichanname' setting in asterisk.conf.
+
+app_meetme.so:
+
+ This application will transparently create 'DAHDI' or 'Zap' channels
+ as needed, based on the 'dahdichanname' setting in asterisk.conf.
diff --git a/agi/DialAnMp3.agi b/agi/DialAnMp3.agi
new file mode 100644
index 000000000..59a54265e
--- /dev/null
+++ b/agi/DialAnMp3.agi
@@ -0,0 +1,82 @@
+#!/usr/bin/perl
+#
+# Simple AGI application to play mp3's selected by a user both using
+# xmms and over the phone itself.
+#
+$|=1;
+while(<STDIN>) {
+ chomp;
+ last unless length($_);
+ if (/^agi_(\w+)\:\s+(.*)$/) {
+ $AGI{$1} = $2;
+ }
+}
+
+print STDERR "AGI Environment Dump:\n";
+foreach $i (sort keys %AGI) {
+ print STDERR " -- $i = $AGI{$i}\n";
+}
+
+dbmopen(%DIGITS, "/var/lib/asterisk/mp3list", 0644) || die("Unable to open mp3list");;
+
+sub checkresult {
+ my ($res) = @_;
+ my $retval;
+ $tests++;
+ chomp $res;
+ if ($res =~ /^200/) {
+ $res =~ /result=(-?[\w\*\#]+)/;
+ return $1;
+ } else {
+ return -1;
+ }
+}
+
+#print STDERR "1. Playing beep...\n";
+#print "STREAM FILE beep \"\"\n";
+#$result = <STDIN>;
+#checkresult($result);
+
+print STDERR "2. Getting song name...\n";
+print "GET DATA demo-enterkeywords\n";
+$result = <STDIN>;
+$digitstr = checkresult($result);
+if ($digitstr < 0) {
+ exit(1);
+}
+$digitstr =~ s/\*/ /g;
+
+print STDERR "Resulting songname is $digitstr\n";
+@searchwords = split (/\s+/, $digitstr);
+print STDERR "Searchwords: " . join(':', @searchwords) . "\n";
+
+foreach $key (sort keys %DIGITS) {
+ @words = split(/\s+/, $DIGITS{$key});
+ $match = 1;
+ foreach $search (@searchwords) {
+ $match = 0 unless grep(/$search/, @words);
+ }
+ if ($match > 0) {
+ print STDERR "File $key matches\n";
+ # Play a beep
+ print "STREAM FILE beep \"\"\n";
+ system("xmms", $key);
+ $result = <STDIN>;
+ if (&checkresult($result) < 0) {
+ exit 0;
+ }
+ print "EXEC MP3Player \"$key\"\n";
+# print "WAIT FOR DIGIT 60000\n";
+ $result = <STDIN>;
+ if (&checkresult($result) < 0) {
+ exit 0;
+ }
+ print STDERR "Got here...\n";
+ }
+}
+
+print STDERR "4. Testing 'saynumber' of $digitstr...\n";
+print "STREAM FILE demo-nomatch\"\"\n";
+$result = <STDIN>;
+checkresult($result);
+
diff --git a/agi/Makefile b/agi/Makefile
new file mode 100644
index 000000000..7a13d2241
--- /dev/null
+++ b/agi/Makefile
@@ -0,0 +1,47 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for AGI-related stuff
+#
+# Copyright (C) 1999-2006, Digium
+#
+# Mark Spencer <markster@digium.com>
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+.PHONY: clean all uninstall
+
+AGIS=agi-test.agi eagi-test eagi-sphinx-test jukebox.agi
+
+ifeq ($(OSARCH),SunOS)
+ LIBS+=-lsocket -lnsl
+endif
+
+include $(ASTTOPDIR)/Makefile.rules
+
+all: $(AGIS)
+
+strcompat.c: ../main/strcompat.c
+ @cp $< $@
+
+eagi-test: eagi-test.o strcompat.o
+
+eagi-sphinx-test: eagi-sphinx-test.o
+
+install: all
+ mkdir -p $(DESTDIR)$(AGI_DIR)
+ for x in $(AGIS); do $(INSTALL) -m 755 $$x $(DESTDIR)$(AGI_DIR) ; done
+
+uninstall:
+ for x in $(AGIS); do rm -f $(DESTDIR)$(AGI_DIR)/$$x ; done
+
+clean:
+ rm -f *.so *.o look eagi-test eagi-sphinx-test
+ rm -f .*.d *.s *.i
+ rm -f strcompat.c
+
+ifneq ($(wildcard .*.d),)
+ include .*.d
+endif
diff --git a/agi/agi-test.agi b/agi/agi-test.agi
new file mode 100644
index 000000000..4fc36eda8
--- /dev/null
+++ b/agi/agi-test.agi
@@ -0,0 +1,79 @@
+#!/usr/bin/perl
+use strict;
+
+$|=1;
+
+# Setup some variables
+my %AGI; my $tests = 0; my $fail = 0; my $pass = 0;
+
+while(<STDIN>) {
+ chomp;
+ last unless length($_);
+ if (/^agi_(\w+)\:\s+(.*)$/) {
+ $AGI{$1} = $2;
+ }
+}
+
+print STDERR "AGI Environment Dump:\n";
+foreach my $i (sort keys %AGI) {
+ print STDERR " -- $i = $AGI{$i}\n";
+}
+
+sub checkresult {
+ my ($res) = @_;
+ my $retval;
+ $tests++;
+ chomp $res;
+ if ($res =~ /^200/) {
+ $res =~ /result=(-?\d+)/;
+ if (!length($1)) {
+ print STDERR "FAIL ($res)\n";
+ $fail++;
+ } else {
+ print STDERR "PASS ($1)\n";
+ $pass++;
+ }
+ } else {
+ print STDERR "FAIL (unexpected result '$res')\n";
+ $fail++;
+ }
+}
+
+print STDERR "1. Testing 'sendfile'...";
+print "STREAM FILE beep \"\"\n";
+my $result = <STDIN>;
+&checkresult($result);
+
+print STDERR "2. Testing 'sendtext'...";
+print "SEND TEXT \"hello world\"\n";
+my $result = <STDIN>;
+&checkresult($result);
+
+print STDERR "3. Testing 'sendimage'...";
+print "SEND IMAGE asterisk-image\n";
+my $result = <STDIN>;
+&checkresult($result);
+
+print STDERR "4. Testing 'saynumber'...";
+print "SAY NUMBER 192837465 \"\"\n";
+my $result = <STDIN>;
+&checkresult($result);
+
+print STDERR "5. Testing 'waitdtmf'...";
+print "WAIT FOR DIGIT 1000\n";
+my $result = <STDIN>;
+&checkresult($result);
+
+print STDERR "6. Testing 'record'...";
+print "RECORD FILE testagi gsm 1234 3000\n";
+my $result = <STDIN>;
+&checkresult($result);
+
+print STDERR "6a. Testing 'record' playback...";
+print "STREAM FILE testagi \"\"\n";
+my $result = <STDIN>;
+&checkresult($result);
+
+print STDERR "================== Complete ======================\n";
+print STDERR "$tests tests completed, $pass passed, $fail failed\n";
+print STDERR "==================================================\n";
diff --git a/agi/eagi-sphinx-test.c b/agi/eagi-sphinx-test.c
new file mode 100644
index 000000000..968e3cfc3
--- /dev/null
+++ b/agi/eagi-sphinx-test.c
@@ -0,0 +1,227 @@
+/*
+ * Extended AGI test application
+ *
+ * This code is released into public domain
+ * without any warranty of any kind.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "asterisk.h"
+
+#include "asterisk/compat.h"
+
+#define AUDIO_FILENO (STDERR_FILENO + 1)
+
+#define SPHINX_HOST "192.168.1.108"
+#define SPHINX_PORT 3460
+
+static int sphinx_sock = -1;
+
+static int connect_sphinx(void)
+{
+ struct hostent *hp;
+ struct sockaddr_in sin;
+ int res;
+ hp = gethostbyname(SPHINX_HOST);
+ if (!hp) {
+ fprintf(stderr, "Unable to resolve '%s'\n", SPHINX_HOST);
+ return -1;
+ }
+ sphinx_sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sphinx_sock < 0) {
+ fprintf(stderr, "Unable to allocate socket: %s\n", strerror(errno));
+ return -1;
+ }
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(SPHINX_PORT);
+ memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
+ if (connect(sphinx_sock, (struct sockaddr *)&sin, sizeof(sin))) {
+ fprintf(stderr, "Unable to connect on socket: %s\n", strerror(errno));
+ close(sphinx_sock);
+ sphinx_sock = -1;
+ return -1;
+ }
+ res = fcntl(sphinx_sock, F_GETFL);
+ if ((res < 0) || (fcntl(sphinx_sock, F_SETFL, res | O_NONBLOCK) < 0)) {
+ fprintf(stderr, "Unable to set flags on socket: %s\n", strerror(errno));
+ close(sphinx_sock);
+ sphinx_sock = -1;
+ return -1;
+ }
+ return 0;
+}
+
+static int read_environment(void)
+{
+ char buf[256];
+ char *val;
+ /* Read environment */
+ for(;;) {
+ if (!fgets(buf, sizeof(buf), stdin)) {
+ return -1;
+ }
+ if (feof(stdin))
+ return -1;
+ buf[strlen(buf) - 1] = '\0';
+ /* Check for end of environment */
+ if (!strlen(buf))
+ return 0;
+ val = strchr(buf, ':');
+ if (!val) {
+ fprintf(stderr, "Invalid environment: '%s'\n", buf);
+ return -1;
+ }
+ *val = '\0';
+ val++;
+ val++;
+ /* Skip space */
+ fprintf(stderr, "Environment: '%s' is '%s'\n", buf, val);
+
+ /* Load into normal environment */
+ setenv(buf, val, 1);
+
+ }
+ /* Never reached */
+ return 0;
+}
+
+static char *wait_result(void)
+{
+ fd_set fds;
+ int res;
+ int max;
+ static char astresp[256];
+ static char sphinxresp[256];
+ char audiobuf[4096];
+ for (;;) {
+ FD_ZERO(&fds);
+ FD_SET(STDIN_FILENO, &fds);
+ FD_SET(AUDIO_FILENO, &fds);
+ max = AUDIO_FILENO;
+ if (sphinx_sock > -1) {
+ FD_SET(sphinx_sock, &fds);
+ if (sphinx_sock > max)
+ max = sphinx_sock;
+ }
+ /* Wait for *some* sort of I/O */
+ res = select(max + 1, &fds, NULL, NULL, NULL);
+ if (res < 0) {
+ fprintf(stderr, "Error in select: %s\n", strerror(errno));
+ return NULL;
+ }
+ if (FD_ISSET(STDIN_FILENO, &fds)) {
+ if (!fgets(astresp, sizeof(astresp), stdin)) {
+ return NULL;
+ }
+ if (feof(stdin)) {
+ fprintf(stderr, "Got hungup on apparently\n");
+ return NULL;
+ }
+ astresp[strlen(astresp) - 1] = '\0';
+ fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp);
+ return astresp;
+ }
+ if (FD_ISSET(AUDIO_FILENO, &fds)) {
+ res = read(AUDIO_FILENO, audiobuf, sizeof(audiobuf));
+ if ((res > 0) && (sphinx_sock > -1)) {
+ if (write(sphinx_sock, audiobuf, res) < 0) {
+ fprintf(stderr, "write() failed: %s\n", strerror(errno));
+ }
+ }
+ }
+ if ((sphinx_sock > -1) && FD_ISSET(sphinx_sock, &fds)) {
+ res = read(sphinx_sock, sphinxresp, sizeof(sphinxresp));
+ if (res > 0) {
+ fprintf(stderr, "Oooh, Sphinx found a token: '%s'\n", sphinxresp);
+ return sphinxresp;
+ } else if (res == 0) {
+ fprintf(stderr, "Hrm, lost sphinx, guess we're on our own\n");
+ close(sphinx_sock);
+ sphinx_sock = -1;
+ }
+ }
+ }
+
+}
+
+static char *run_command(char *command)
+{
+ fprintf(stdout, "%s\n", command);
+ return wait_result();
+}
+
+static int run_script(void)
+{
+ char *res;
+ res = run_command("STREAM FILE demo-enterkeywords 0123456789*#");
+ if (!res) {
+ fprintf(stderr, "Failed to execute command\n");
+ return -1;
+ }
+ fprintf(stderr, "1. Result is '%s'\n", res);
+ res = run_command("STREAM FILE demo-nomatch 0123456789*#");
+ if (!res) {
+ fprintf(stderr, "Failed to execute command\n");
+ return -1;
+ }
+ fprintf(stderr, "2. Result is '%s'\n", res);
+ res = run_command("SAY NUMBER 23452345 0123456789*#");
+ if (!res) {
+ fprintf(stderr, "Failed to execute command\n");
+ return -1;
+ }
+ fprintf(stderr, "3. Result is '%s'\n", res);
+ res = run_command("GET DATA demo-enterkeywords");
+ if (!res) {
+ fprintf(stderr, "Failed to execute command\n");
+ return -1;
+ }
+ fprintf(stderr, "4. Result is '%s'\n", res);
+ res = run_command("STREAM FILE auth-thankyou \"\"");
+ if (!res) {
+ fprintf(stderr, "Failed to execute command\n");
+ return -1;
+ }
+ fprintf(stderr, "5. Result is '%s'\n", res);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char *tmp;
+ int ver = 0;
+ int subver = 0;
+ /* Setup stdin/stdout for line buffering */
+ setlinebuf(stdin);
+ setlinebuf(stdout);
+ if (read_environment()) {
+ fprintf(stderr, "Failed to read environment: %s\n", strerror(errno));
+ exit(1);
+ }
+ connect_sphinx();
+ tmp = getenv("agi_enhanced");
+ if (tmp) {
+ if (sscanf(tmp, "%d.%d", &ver, &subver) != 2)
+ ver = 0;
+ }
+ if (ver < 1) {
+ fprintf(stderr, "No enhanced AGI services available. Use EAGI, not AGI\n");
+ exit(1);
+ }
+ if (run_script())
+ return -1;
+ exit(0);
+}
diff --git a/agi/eagi-test.c b/agi/eagi-test.c
new file mode 100644
index 000000000..9bfbee79e
--- /dev/null
+++ b/agi/eagi-test.c
@@ -0,0 +1,169 @@
+/*
+ * Extended AGI test application
+ *
+ * This code is released into the public domain
+ * with no warranty of any kind
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/select.h>
+
+#include "asterisk.h"
+
+#include "asterisk/compat.h"
+
+#define AUDIO_FILENO (STDERR_FILENO + 1)
+
+static int read_environment(void)
+{
+ char buf[256];
+ char *val;
+ /* Read environment */
+ for(;;) {
+ if (!fgets(buf, sizeof(buf), stdin)) {
+ return -1;
+ }
+ if (feof(stdin))
+ return -1;
+ buf[strlen(buf) - 1] = '\0';
+ /* Check for end of environment */
+ if (!strlen(buf))
+ return 0;
+ val = strchr(buf, ':');
+ if (!val) {
+ fprintf(stderr, "Invalid environment: '%s'\n", buf);
+ return -1;
+ }
+ *val = '\0';
+ val++;
+ val++;
+ /* Skip space */
+ fprintf(stderr, "Environment: '%s' is '%s'\n", buf, val);
+
+ /* Load into normal environment */
+ setenv(buf, val, 1);
+
+ }
+ /* Never reached */
+ return 0;
+}
+
+static char *wait_result(void)
+{
+ fd_set fds;
+ int res;
+ int bytes = 0;
+ static char astresp[256];
+ char audiobuf[4096];
+ for (;;) {
+ FD_ZERO(&fds);
+ FD_SET(STDIN_FILENO, &fds);
+ FD_SET(AUDIO_FILENO, &fds);
+ /* Wait for *some* sort of I/O */
+ res = select(AUDIO_FILENO + 1, &fds, NULL, NULL, NULL);
+ if (res < 0) {
+ fprintf(stderr, "Error in select: %s\n", strerror(errno));
+ return NULL;
+ }
+ if (FD_ISSET(STDIN_FILENO, &fds)) {
+ if (!fgets(astresp, sizeof(astresp), stdin)) {
+ return NULL;
+ }
+ if (feof(stdin)) {
+ fprintf(stderr, "Got hungup on apparently\n");
+ return NULL;
+ }
+ astresp[strlen(astresp) - 1] = '\0';
+ fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp);
+ return astresp;
+ }
+ if (FD_ISSET(AUDIO_FILENO, &fds)) {
+ res = read(AUDIO_FILENO, audiobuf, sizeof(audiobuf));
+ if (res > 0) {
+ /* XXX Process the audio with sphinx here XXX */
+#if 0
+ fprintf(stderr, "Got %d/%d bytes of audio\n", res, bytes);
+#endif
+ bytes += res;
+ /* Prentend we detected some audio after 3 seconds */
+ if (bytes > 16000 * 3) {
+ return "Sample Message";
+ bytes = 0;
+ }
+ }
+ }
+ }
+
+}
+
+static char *run_command(char *command)
+{
+ fprintf(stdout, "%s\n", command);
+ return wait_result();
+}
+
+static int run_script(void)
+{
+ char *res;
+ res = run_command("STREAM FILE demo-enterkeywords 0123456789*#");
+ if (!res) {
+ fprintf(stderr, "Failed to execute command\n");
+ return -1;
+ }
+ fprintf(stderr, "1. Result is '%s'\n", res);
+ res = run_command("STREAM FILE demo-nomatch 0123456789*#");
+ if (!res) {
+ fprintf(stderr, "Failed to execute command\n");
+ return -1;
+ }
+ fprintf(stderr, "2. Result is '%s'\n", res);
+ res = run_command("SAY NUMBER 23452345 0123456789*#");
+ if (!res) {
+ fprintf(stderr, "Failed to execute command\n");
+ return -1;
+ }
+ fprintf(stderr, "3. Result is '%s'\n", res);
+ res = run_command("GET DATA demo-enterkeywords");
+ if (!res) {
+ fprintf(stderr, "Failed to execute command\n");
+ return -1;
+ }
+ fprintf(stderr, "4. Result is '%s'\n", res);
+ res = run_command("STREAM FILE auth-thankyou \"\"");
+ if (!res) {
+ fprintf(stderr, "Failed to execute command\n");
+ return -1;
+ }
+ fprintf(stderr, "5. Result is '%s'\n", res);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char *tmp;
+ int ver = 0;
+ int subver = 0;
+ /* Setup stdin/stdout for line buffering */
+ setlinebuf(stdin);
+ setlinebuf(stdout);
+ if (read_environment()) {
+ fprintf(stderr, "Failed to read environment: %s\n", strerror(errno));
+ exit(1);
+ }
+ tmp = getenv("agi_enhanced");
+ if (tmp) {
+ if (sscanf(tmp, "%d.%d", &ver, &subver) != 2)
+ ver = 0;
+ }
+ if (ver < 1) {
+ fprintf(stderr, "No enhanced AGI services available. Use EAGI, not AGI\n");
+ exit(1);
+ }
+ if (run_script())
+ return -1;
+ exit(0);
+}
diff --git a/agi/fastagi-test b/agi/fastagi-test
new file mode 100644
index 000000000..d3f13cf6b
--- /dev/null
+++ b/agi/fastagi-test
@@ -0,0 +1,94 @@
+#!/usr/bin/perl
+use strict;
+use Socket;
+use Carp;
+use IO::Handle;
+
+my $port = 4573;
+
+$|=1;
+
+# Setup some variables
+my %AGI; my $tests = 0; my $fail = 0; my $pass = 0;
+
+sub checkresult {
+ my ($res) = @_;
+ my $retval;
+ $tests++;
+ chomp $res;
+ if ($res =~ /^200/) {
+ $res =~ /result=(-?\d+)/;
+ if (!length($1)) {
+ print STDERR "FAIL ($res)\n";
+ $fail++;
+ } else {
+ print STDERR "PASS ($1)\n";
+ $pass++;
+ }
+ } else {
+ print STDERR "FAIL (unexpected result '$res')\n";
+ $fail++;
+ }
+}
+
+socket(SERVER, PF_INET, SOCK_STREAM, 0);
+setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, pack("l", 1));
+bind(SERVER, sockaddr_in($port, INADDR_ANY)) || die("can't bind\n");
+listen(SERVER, SOMAXCONN);
+
+for(;;) {
+ my $raddr = accept(CLIENT, SERVER);
+ my ($s, $p) = sockaddr_in($raddr);
+ CLIENT->autoflush(1);
+ while(<CLIENT>) {
+ chomp;
+ last unless length($_);
+ if (/^agi_(\w+)\:\s+(.*)$/) {
+ $AGI{$1} = $2;
+ }
+ }
+ print STDERR "AGI Environment Dump from $s:$p --\n";
+ foreach my $i (sort keys %AGI) {
+ print STDERR " -- $i = $AGI{$i}\n";
+ }
+
+ print STDERR "1. Testing 'sendfile'...";
+ print CLIENT "STREAM FILE beep \"\"\n";
+ my $result = <CLIENT>;
+ &checkresult($result);
+
+ print STDERR "2. Testing 'sendtext'...";
+ print CLIENT "SEND TEXT \"hello world\"\n";
+ my $result = <CLIENT>;
+ &checkresult($result);
+
+ print STDERR "3. Testing 'sendimage'...";
+ print CLIENT "SEND IMAGE asterisk-image\n";
+ my $result = <CLIENT>;
+ &checkresult($result);
+
+ print STDERR "4. Testing 'saynumber'...";
+ print CLIENT "SAY NUMBER 192837465 \"\"\n";
+ my $result = <CLIENT>;
+ &checkresult($result);
+
+ print STDERR "5. Testing 'waitdtmf'...";
+ print CLIENT "WAIT FOR DIGIT 1000\n";
+ my $result = <CLIENT>;
+ &checkresult($result);
+
+ print STDERR "6. Testing 'record'...";
+ print CLIENT "RECORD FILE testagi gsm 1234 3000\n";
+ my $result = <CLIENT>;
+ &checkresult($result);
+
+ print STDERR "6a. Testing 'record' playback...";
+ print CLIENT "STREAM FILE testagi \"\"\n";
+ my $result = <CLIENT>;
+ &checkresult($result);
+ close(CLIENT);
+ print STDERR "================== Complete ======================\n";
+ print STDERR "$tests tests completed, $pass passed, $fail failed\n";
+ print STDERR "==================================================\n";
+}
+
diff --git a/agi/jukebox.agi b/agi/jukebox.agi
new file mode 100755
index 000000000..7bd9c10f9
--- /dev/null
+++ b/agi/jukebox.agi
@@ -0,0 +1,488 @@
+#!/usr/bin/perl
+#
+# Jukebox 0.2
+#
+# A music manager for Asterisk.
+#
+# Copyright (C) 2005-2006, Justin Tunney
+#
+# Justin Tunney <jesuscyborg@gmail.com>
+#
+# This program is free software, distributed under the terms of the
+# GNU General Public License v2.
+#
+# Keep it open source pigs
+#
+# --------------------------------------------------------------------
+#
+# Uses festival to list off all your MP3 music files over a channel in
+# a hierarchical fashion. Put this file in your agi-bin folder which
+# is located at: /var/lib/asterisk/agi-bin Be sure to chmod +x it!
+#
+# Invocation Example:
+# exten => 68742,1,Answer()
+# exten => 68742,2,agi,jukebox.agi|/home/justin/Music
+# exten => 68742,3,Hangup()
+#
+# exten => 68742,1,Answer()
+# exten => 68742,2,agi,jukebox.agi|/home/justin/Music|pm
+# exten => 68742,3,Hangup()
+#
+# Options:
+# p - Precache text2wave outputs for every possible filename.
+# It is much better to set this option because if a caller
+# presses a key during a cache operation, it will be ignored.
+# m - Go back to menu after playing song
+# g - Do not play the greeting message
+#
+# Usage Instructions:
+# - Press '*' to go up a directory. If you are in the root music
+# folder you will be exitted from the script.
+# - If you have a really long list of files, you can filter the list
+# at any time by pressing '#' and spelling out a few letters you
+# expect the files to start with. For example, if you wanted to
+# know what extension 'Requiem For A Dream' was, you'd type:
+# '#737'. Note, phone keypads don't include Q and Z. Q is 7 and
+# Z is 9.
+#
+# Notes:
+# - This AGI script uses the MP3Player command which uses the
+# mpg123 Program. Grab yourself a copy of this program by
+# going to http://www.mpg123.de/cgi-bin/sitexplorer.cgi?/mpg123/
+# Be sure to download mpg123-0.59r.tar.gz because it is known to
+# work with Asterisk and hopefully isn't the release with that
+# awful security problem. If you're using Fedora Core 3 with
+# Alsa like me, make linux-alsa isn't going to work. Do make
+# linux-devel and you're peachy keen.
+#
+# - You won't get nifty STDERR debug messages if you're using a
+# remote asterisk shell.
+#
+# - For some reason, caching certain files will generate the
+# error: 'using default diphone ax-ax for y-pau'. Example:
+# # echo "Depeche Mode - CUW - 05 - The Meaning of Love" | text2wave -o /var/jukeboxcache/jukeboxcache/Depeche_Mode/Depeche_Mode_-_CUW_-_05_-_The_Meaning_of_Love.mp3.ul -otype ulaw -
+# The temporary work around is to just touch these files.
+#
+# - The background app doesn't like to get more than 2031 chars
+# of input.
+#
+
+use strict;
+
+$|=1;
+
+# Setup some variables
+my %AGI; my $tests = 0; my $fail = 0; my $pass = 0;
+my @masterCacheList = ();
+my $maxNumber = 10;
+
+while (<STDIN>) {
+ chomp;
+ last unless length($_);
+ if (/^agi_(\w+)\:\s+(.*)$/) {
+ $AGI{$1} = $2;
+ }
+}
+
+# setup options
+my $SHOWGREET = 1;
+my $PRECACHE = 0;
+my $MENUAFTERSONG = 0;
+
+$PRECACHE = 1 if $ARGV[1] =~ /p/;
+$MENUAFTERSONG = 1 if $ARGV[1] =~ /m/;
+$SHOWGREET = 0 if $ARGV[1] =~ /g/;
+
+# setup folders
+my $MUSIC = $ARGV[0];
+$MUSIC = &rmts($MUSIC);
+my $FESTIVALCACHE = "/var/jukeboxcache";
+if (! -e $FESTIVALCACHE) {
+ `mkdir -p -m0776 $FESTIVALCACHE`;
+}
+
+# make sure we have some essential files
+if (! -e "$FESTIVALCACHE/jukebox_greet.ul") {
+ `echo "Welcome to the Asterisk Jukebox" | text2wave -o $FESTIVALCACHE/jukebox_greet.ul -otype ulaw -`;
+}
+if (! -e "$FESTIVALCACHE/jukebox_press.ul") {
+ `echo "Press" | text2wave -o $FESTIVALCACHE/jukebox_press.ul -otype ulaw -`;
+}
+if (! -e "$FESTIVALCACHE/jukebox_for.ul") {
+ `echo "For" | text2wave -o $FESTIVALCACHE/jukebox_for.ul -otype ulaw -`;
+}
+if (! -e "$FESTIVALCACHE/jukebox_toplay.ul") {
+ `echo "To play" | text2wave -o $FESTIVALCACHE/jukebox_toplay.ul -otype ulaw -`;
+}
+if (! -e "$FESTIVALCACHE/jukebox_nonefound.ul") {
+ `echo "There were no music files found in this folder" | text2wave -o $FESTIVALCACHE/jukebox_nonefound.ul -otype ulaw -`;
+}
+if (! -e "$FESTIVALCACHE/jukebox_percent.ul") {
+ `echo "Percent" | text2wave -o $FESTIVALCACHE/jukebox_percent.ul -otype ulaw -`;
+}
+if (! -e "$FESTIVALCACHE/jukebox_generate.ul") {
+ `echo "Please wait while Astrisk Jukebox cashes the files of your music collection" | text2wave -o $FESTIVALCACHE/jukebox_generate.ul -otype ulaw -`;
+}
+if (! -e "$FESTIVALCACHE/jukebox_invalid.ul") {
+ `echo "You have entered an invalid selection" | text2wave -o $FESTIVALCACHE/jukebox_invalid.ul -otype ulaw -`;
+}
+if (! -e "$FESTIVALCACHE/jukebox_thankyou.ul") {
+ `echo "Thank you for using Astrisk Jukebox, Goodbye" | text2wave -o $FESTIVALCACHE/jukebox_thankyou.ul -otype ulaw -`;
+}
+
+# greet the user
+if ($SHOWGREET) {
+ print "EXEC Playback \"$FESTIVALCACHE/jukebox_greet\"\n";
+ my $result = <STDIN>; &check_result($result);
+}
+
+# go through the directories
+music_dir_cache() if $PRECACHE;
+music_dir_menu('/');
+
+exit 0;
+
+##########################################################################
+
+sub music_dir_menu {
+ my $dir = shift;
+
+# generate a list of mp3's and directories and assign each one it's
+# own selection number. Then make sure that we've got a sound clip
+# for the file name
+ if (!opendir(THEDIR, rmts($MUSIC.$dir))) {
+ print STDERR "Failed to open music directory: $dir\n";
+ exit 1;
+ }
+ my @files = sort readdir THEDIR;
+ my $cnt = 1;
+ my @masterBgList = ();
+
+ foreach my $file (@files) {
+ chomp($file);
+ if ($file ne '.' && $file ne '..' && $file ne 'festivalcache') { # ignore special files
+ my $real_version = &rmts($MUSIC.$dir).'/'.$file;
+ my $cache_version = &rmts($FESTIVALCACHE.$dir).'/'.$file.'.ul';
+ my $cache_version2 = &rmts($FESTIVALCACHE.$dir).'/'.$file;
+ my $cache_version_esc = &clean_file($cache_version);
+ my $cache_version2_esc = &clean_file($cache_version2);
+
+ if (-d $real_version) {
+# 0:id 1:type 2:text2wav-file 3:for-filtering 4:the-directory 5:text2wav echo
+ push(@masterBgList, [$cnt++, 1, $cache_version2_esc, &remove_special_chars($file), $file, "for the $file folder"]);
+ } elsif ($real_version =~ /\.mp3$/) {
+# 0:id 1:type 2:text2wav-file 3:for-filtering 4:the-mp3
+ push(@masterBgList, [$cnt++, 2, $cache_version2_esc, &remove_special_chars($file), $real_version, "to play $file"]);
+ }
+ }
+ }
+ close(THEDIR);
+
+ my @filterList = @masterBgList;
+
+ if (@filterList == 0) {
+ print "EXEC Playback \"$FESTIVALCACHE/jukebox_nonefound\"\n";
+ my $result = <STDIN>; &check_result($result);
+ return 0;
+ }
+
+ for (;;) {
+MYCONTINUE:
+
+# play bg selections and figure out their selection
+ my $digit = '';
+ my $digitstr = '';
+ for (my $n=0; $n<@filterList; $n++) {
+ &cache_speech(&remove_file_extension($filterList[$n][5]), "$filterList[$n][2].ul") if ! -e "$filterList[$n][2].ul";
+ &cache_speech("Press $filterList[$n][0]", "$FESTIVALCACHE/jukebox_$filterList[$n][0].ul") if ! -e "$FESTIVALCACHE/jukebox_$filterList[$n][0].ul";
+ print "EXEC Background \"$filterList[$n][2]&$FESTIVALCACHE/jukebox_$filterList[$n][0]\"\n";
+ my $result = <STDIN>;
+ $digit = &check_result($result);
+ if ($digit > 0) {
+ $digitstr .= chr($digit);
+ last;
+ }
+ }
+ for (;;) {
+ print "WAIT FOR DIGIT 3000\n";
+ my $result = <STDIN>;
+ $digit = &check_result($result);
+ last if $digit <= 0;
+ $digitstr .= chr($digit);
+ }
+
+# see if it's a valid selection
+ print STDERR "Digits Entered: '$digitstr'\n";
+ exit 0 if $digitstr eq '';
+ my $found = 0;
+ goto EXITSUB if $digitstr =~ /\*/;
+
+# filter the list
+ if ($digitstr =~ /^\#\d+/) {
+ my $regexp = '';
+ for (my $n=1; $n<length($digitstr); $n++) {
+ my $d = substr($digitstr, $n, 1);
+ if ($d == 2) {
+ $regexp .= '[abc]';
+ } elsif ($d == 3) {
+ $regexp .= '[def]';
+ } elsif ($d == 4) {
+ $regexp .= '[ghi]';
+ } elsif ($d == 5) {
+ $regexp .= '[jkl]';
+ } elsif ($d == 6) {
+ $regexp .= '[mno]';
+ } elsif ($d == 7) {
+ $regexp .= '[pqrs]';
+ } elsif ($d == 8) {
+ $regexp .= '[tuv]';
+ } elsif ($d == 9) {
+ $regexp .= '[wxyz]';
+ }
+ }
+ @filterList = ();
+ for (my $n=1; $n<@masterBgList; $n++) {
+ push(@filterList, $masterBgList[$n]) if $masterBgList[$n][3] =~ /^$regexp/i;
+ }
+ goto MYCONTINUE;
+ }
+
+ for (my $n=0; $n<@masterBgList; $n++) {
+ if ($digitstr == $masterBgList[$n][0]) {
+ if ($masterBgList[$n][1] == 1) { # a folder
+ &music_dir_menu(rmts($dir).'/'.$masterBgList[$n][4]);
+ @filterList = @masterBgList;
+ goto MYCONTINUE;
+ } elsif ($masterBgList[$n][1] == 2) { # a file
+# because *'s scripting language is crunk and won't allow us to escape
+# funny filenames, we need to create a temporary symlink to the mp3
+# file
+ my $mp3 = &escape_file($masterBgList[$n][4]);
+ my $link = `mktemp`;
+ chomp($link);
+ $link .= '.mp3';
+ print STDERR "ln -s $mp3 $link\n";
+ my $cmdr = `ln -s $mp3 $link`;
+ chomp($cmdr);
+ print "Failed to create symlink to mp3: $cmdr\n" if $cmdr ne '';
+
+ print "EXEC MP3Player \"$link\"\n";
+ my $result = <STDIN>; &check_result($result);
+
+ `rm $link`;
+
+ if (!$MENUAFTERSONG) {
+ print "EXEC Playback \"$FESTIVALCACHE/jukebox_thankyou\"\n";
+ my $result = <STDIN>; &check_result($result);
+ exit 0;
+ } else {
+ goto MYCONTINUE;
+ }
+ }
+ }
+ }
+ print "EXEC Playback \"$FESTIVALCACHE/jukebox_invalid\"\n";
+ my $result = <STDIN>; &check_result($result);
+ }
+ EXITSUB:
+}
+
+sub cache_speech {
+ my $speech = shift;
+ my $file = shift;
+
+ my $theDir = extract_file_dir($file);
+ `mkdir -p -m0776 $theDir`;
+
+ print STDERR "echo \"$speech\" | text2wave -o $file -otype ulaw -\n";
+ my $cmdr = `echo "$speech" | text2wave -o $file -otype ulaw -`;
+ chomp($cmdr);
+ if ($cmdr =~ /using default diphone/) {
+# temporary bug work around....
+ `touch $file`;
+ } elsif ($cmdr ne '') {
+ print STDERR "Command Failed\n";
+ exit 1;
+ }
+}
+
+sub music_dir_cache {
+# generate list of text2speech files to generate
+ if (!music_dir_cache_genlist('/')) {
+ print STDERR "Horrible Dreadful Error: No Music Found in $MUSIC!";
+ exit 1;
+ }
+
+# add to list how many 'number' files we have to generate. We can't
+# use the SayNumber app in Asterisk because we want to chain all
+# talking in one Background command. We also want a consistent
+# voice...
+ for (my $n=1; $n<=$maxNumber; $n++) {
+ push(@masterCacheList, [3, "Press $n", "$FESTIVALCACHE/jukebox_$n.ul"]) if ! -e "$FESTIVALCACHE/jukebox_$n.ul";
+ }
+
+# now generate all these darn text2speech files
+ if (@masterCacheList > 5) {
+ print "EXEC Playback \"$FESTIVALCACHE/jukebox_generate\"\n";
+ my $result = <STDIN>; &check_result($result);
+ }
+ my $theTime = time();
+ for (my $n=0; $n < @masterCacheList; $n++) {
+ my $cmdr = '';
+ if ($masterCacheList[$n][0] == 1) { # directory
+ &cache_speech("for folder $masterCacheList[$n][1]", $masterCacheList[$n][2]);
+ } elsif ($masterCacheList[$n][0] == 2) { # file
+ &cache_speech("to play $masterCacheList[$n][1]", $masterCacheList[$n][2]);
+ } elsif ($masterCacheList[$n][0] == 3) { # number
+ &cache_speech($masterCacheList[$n][1], $masterCacheList[$n][2]);
+ }
+ if (time() >= $theTime + 30) {
+ my $percent = int($n / @masterCacheList * 100);
+ print "SAY NUMBER $percent \"\"\n";
+ my $result = <STDIN>; &check_result($result);
+ print "EXEC Playback \"$FESTIVALCACHE/jukebox_percent\"\n";
+ my $result = <STDIN>; &check_result($result);
+ $theTime = time();
+ }
+ }
+}
+
+# this function will fill the @masterCacheList of all the files that
+# need to have text2speeced ulaw files of their names generated
+sub music_dir_cache_genlist {
+ my $dir = shift;
+ if (!opendir(THEDIR, rmts($MUSIC.$dir))) {
+ print STDERR "Failed to open music directory: $dir\n";
+ exit 1;
+ }
+ my @files = sort readdir THEDIR;
+ my $foundFiles = 0;
+ my $tmpMaxNum = 0;
+ foreach my $file (@files) {
+ chomp;
+ if ($file ne '.' && $file ne '..' && $file ne 'festivalcache') { # ignore special files
+ my $real_version = &rmts($MUSIC.$dir).'/'.$file;
+ my $cache_version = &rmts($FESTIVALCACHE.$dir).'/'.$file.'.ul';
+ my $cache_version2 = &rmts($FESTIVALCACHE.$dir).'/'.$file;
+ my $cache_version_esc = &clean_file($cache_version);
+ my $cache_version2_esc = &clean_file($cache_version2);
+
+ if (-d $real_version) {
+ if (music_dir_cache_genlist(rmts($dir).'/'.$file)) {
+ $tmpMaxNum++;
+ $maxNumber = $tmpMaxNum if $tmpMaxNum > $maxNumber;
+ push(@masterCacheList, [1, $file, $cache_version_esc]) if ! -e $cache_version_esc;
+ $foundFiles = 1;
+ }
+ } elsif ($real_version =~ /\.mp3$/) {
+ $tmpMaxNum++;
+ $maxNumber = $tmpMaxNum if $tmpMaxNum > $maxNumber;
+ push(@masterCacheList, [2, &remove_file_extension($file), $cache_version_esc]) if ! -e $cache_version_esc;
+ $foundFiles = 1;
+ }
+ }
+ }
+ close(THEDIR);
+ return $foundFiles;
+}
+
+sub rmts { # remove trailing slash
+ my $hog = shift;
+ $hog =~ s/\/$//;
+ return $hog;
+}
+
+sub extract_file_name {
+ my $hog = shift;
+ $hog =~ /\/?([^\/]+)$/;
+ return $1;
+}
+
+sub extract_file_dir {
+ my $hog = shift;
+ return $hog if ! ($hog =~ /\//);
+ $hog =~ /(.*)\/[^\/]*$/;
+ return $1;
+}
+
+sub remove_file_extension {
+ my $hog = shift;
+ return $hog if ! ($hog =~ /\./);
+ $hog =~ /(.*)\.[^.]*$/;
+ return $1;
+}
+
+sub clean_file {
+ my $hog = shift;
+ $hog =~ s/\\/_/g;
+ $hog =~ s/ /_/g;
+ $hog =~ s/\t/_/g;
+ $hog =~ s/\'/_/g;
+ $hog =~ s/\"/_/g;
+ $hog =~ s/\(/_/g;
+ $hog =~ s/\)/_/g;
+ $hog =~ s/&/_/g;
+ $hog =~ s/\[/_/g;
+ $hog =~ s/\]/_/g;
+ $hog =~ s/\$/_/g;
+ $hog =~ s/\|/_/g;
+ $hog =~ s/\^/_/g;
+ return $hog;
+}
+
+sub remove_special_chars {
+ my $hog = shift;
+ $hog =~ s/\\//g;
+ $hog =~ s/ //g;
+ $hog =~ s/\t//g;
+ $hog =~ s/\'//g;
+ $hog =~ s/\"//g;
+ $hog =~ s/\(//g;
+ $hog =~ s/\)//g;
+ $hog =~ s/&//g;
+ $hog =~ s/\[//g;
+ $hog =~ s/\]//g;
+ $hog =~ s/\$//g;
+ $hog =~ s/\|//g;
+ $hog =~ s/\^//g;
+ return $hog;
+}
+
+sub escape_file {
+ my $hog = shift;
+ $hog =~ s/\\/\\\\/g;
+ $hog =~ s/ /\\ /g;
+ $hog =~ s/\t/\\\t/g;
+ $hog =~ s/\'/\\\'/g;
+ $hog =~ s/\"/\\\"/g;
+ $hog =~ s/\(/\\\(/g;
+ $hog =~ s/\)/\\\)/g;
+ $hog =~ s/&/\\&/g;
+ $hog =~ s/\[/\\\[/g;
+ $hog =~ s/\]/\\\]/g;
+ $hog =~ s/\$/\\\$/g;
+ $hog =~ s/\|/\\\|/g;
+ $hog =~ s/\^/\\\^/g;
+ return $hog;
+}
+
+sub check_result {
+ my ($res) = @_;
+ my $retval;
+ $tests++;
+ chomp $res;
+ if ($res =~ /^200/) {
+ $res =~ /result=(-?\d+)/;
+ if (!length($1)) {
+ print STDERR "FAIL ($res)\n";
+ $fail++;
+ exit 1;
+ } else {
+ print STDERR "PASS ($1)\n";
+ return $1;
+ }
+ } else {
+ print STDERR "FAIL (unexpected result '$res')\n";
+ exit 1;
+ }
+}
diff --git a/agi/numeralize b/agi/numeralize
new file mode 100644
index 000000000..5ca51913d
--- /dev/null
+++ b/agi/numeralize
@@ -0,0 +1,44 @@
+#!/usr/bin/perl
+#
+# Build a database linking filenames to their numerical representations
+# using a keypad for the DialAnMp3 application
+#
+
+$mp3dir="/usr/media/mpeg3";
+
+dbmopen(%DIGITS, "/var/lib/asterisk/mp3list", 0644) || die("Unable to open mp3list");;
+sub process_dir {
+ my ($dir) = @_;
+ my $file;
+ my $digits;
+ my @entries;
+ opendir(DIR, $dir);
+ @entries = readdir(DIR);
+ closedir(DIR);
+ foreach $_ (@entries) {
+ if (!/^\./) {
+ $file = "$dir/$_";
+ if (-d "$file") {
+ process_dir("$file");
+ } else {
+ $digits = $_;
+ $digits =~ s/[^ \w]+//g;
+ $digits =~ s/\_/ /g;
+ $digits =~ tr/[a-z]/[A-Z]/;
+ $digits =~ tr/[A-C]/2/;
+ $digits =~ tr/[D-F]/3/;
+ $digits =~ tr/[G-I]/4/;
+ $digits =~ tr/[J-L]/5/;
+ $digits =~ tr/[M-O]/6/;
+ $digits =~ tr/[P-S]/7/;
+ $digits =~ tr/[T-V]/8/;
+ $digits =~ tr/[W-Z]/9/;
+ $digits =~ s/\s+/ /;
+ print "File: $file, digits: $digits\n";
+ $DIGITS{$file} = $digits;
+ }
+ }
+ }
+}
+
+process_dir($mp3dir);
diff --git a/apps/Makefile b/apps/Makefile
new file mode 100644
index 000000000..c57926a6a
--- /dev/null
+++ b/apps/Makefile
@@ -0,0 +1,42 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for PBX applications
+#
+# Copyright (C) 1999-2006, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include ../menuselect.makeopts ../menuselect.makedeps
+
+MENUSELECT_CATEGORY=APPS
+MENUSELECT_DESCRIPTION=Applications
+
+ALL_C_MODS:=$(patsubst %.c,%,$(wildcard app_*.c))
+ALL_CC_MODS:=$(patsubst %.cc,%,$(wildcard app_*.cc))
+
+C_MODS:=$(filter-out $(MENUSELECT_APPS),$(ALL_C_MODS))
+CC_MODS:=$(filter-out $(MENUSELECT_APPS),$(ALL_CC_MODS))
+
+LOADABLE_MODS:=$(C_MODS) $(CC_MODS)
+
+ifneq ($(findstring apps,$(MENUSELECT_EMBED)),)
+ EMBEDDED_MODS:=$(LOADABLE_MODS)
+ LOADABLE_MODS:=
+endif
+
+MENUSELECT_OPTS_app_directory:=$(MENUSELECT_OPTS_app_voicemail)
+ifneq ($(findstring ODBC_STORAGE,$(MENUSELECT_OPTS_app_voicemail)),)
+MENUSELECT_DEPENDS_app_voicemail+=$(MENUSELECT_DEPENDS_ODBC_STORAGE)
+MENUSELECT_DEPENDS_app_directory+=$(MENUSELECT_DEPENDS_ODBC_STORAGE)
+endif
+ifneq ($(findstring IMAP_STORAGE,$(MENUSELECT_OPTS_app_voicemail)),)
+MENUSELECT_DEPENDS_app_voicemail+=$(MENUSELECT_DEPENDS_IMAP_STORAGE)
+MENUSELECT_DEPENDS_app_directory+=$(MENUSELECT_DEPENDS_IMAP_STORAGE)
+endif
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
diff --git a/apps/app_adsiprog.c b/apps/app_adsiprog.c
new file mode 100644
index 000000000..750cc6fc7
--- /dev/null
+++ b/apps/app_adsiprog.c
@@ -0,0 +1,1592 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Program Asterisk ADSI Scripts into phone
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>res_adsi</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/adsi.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+
+static char *app = "ADSIProg";
+
+static char *synopsis = "Load Asterisk ADSI Scripts into phone";
+
+/* #define DUMP_MESSAGES */
+
+static char *descrip =
+" ADSIProg(script): This application programs an ADSI Phone with the given\n"
+"script. If nothing is specified, the default script (asterisk.adsi) is used.\n";
+
+struct adsi_event {
+ int id;
+ char *name;
+};
+
+static struct adsi_event events[] = {
+ { 1, "CALLERID" },
+ { 2, "VMWI" },
+ { 3, "NEARANSWER" },
+ { 4, "FARANSWER" },
+ { 5, "ENDOFRING" },
+ { 6, "IDLE" },
+ { 7, "OFFHOOK" },
+ { 8, "CIDCW" },
+ { 9, "BUSY" },
+ { 10, "FARRING" },
+ { 11, "DIALTONE" },
+ { 12, "RECALL" },
+ { 13, "MESSAGE" },
+ { 14, "REORDER" },
+ { 15, "DISTINCTIVERING" },
+ { 16, "RING" },
+ { 17, "REMINDERRING" },
+ { 18, "SPECIALRING" },
+ { 19, "CODEDRING" },
+ { 20, "TIMER" },
+ { 21, "INUSE" },
+ { 22, "EVENT22" },
+ { 23, "EVENT23" },
+ { 24, "CPEID" },
+};
+
+static struct adsi_event justify[] = {
+ { 0, "CENTER" },
+ { 1, "RIGHT" },
+ { 2, "LEFT" },
+ { 3, "INDENT" },
+};
+
+#define STATE_NORMAL 0
+#define STATE_INKEY 1
+#define STATE_INSUB 2
+#define STATE_INIF 3
+
+#define MAX_RET_CODE 20
+#define MAX_SUB_LEN 255
+#define MAX_MAIN_LEN 1600
+
+#define ARG_STRING (1 << 0)
+#define ARG_NUMBER (1 << 1)
+
+struct adsi_soft_key {
+ char vname[40]; /* Which "variable" is associated with it */
+ int retstrlen; /* Length of return string */
+ int initlen; /* initial length */
+ int id;
+ int defined;
+ char retstr[80]; /* Return string data */
+};
+
+struct adsi_subscript {
+ char vname[40];
+ int id;
+ int defined;
+ int datalen;
+ int inscount;
+ int ifinscount;
+ char *ifdata;
+ char data[2048];
+};
+
+struct adsi_state {
+ char vname[40];
+ int id;
+};
+
+struct adsi_flag {
+ char vname[40];
+ int id;
+};
+
+struct adsi_display {
+ char vname[40];
+ int id;
+ char data[70];
+ int datalen;
+};
+
+struct adsi_script {
+ int state;
+ int numkeys;
+ int numsubs;
+ int numstates;
+ int numdisplays;
+ int numflags;
+ struct adsi_soft_key *key;
+ struct adsi_subscript *sub;
+ /* Pre-defined displays */
+ struct adsi_display displays[63];
+ /* ADSI States 1 (initial) - 254 */
+ struct adsi_state states[256];
+ /* Keys 2-63 */
+ struct adsi_soft_key keys[62];
+ /* Subscripts 0 (main) to 127 */
+ struct adsi_subscript subs[128];
+ /* Flags 1-7 */
+ struct adsi_flag flags[7];
+
+ /* Stuff from adsi script */
+ unsigned char sec[5];
+ char desc[19];
+ unsigned char fdn[5];
+ int ver;
+};
+
+
+static int process_token(void *out, char *src, int maxlen, int argtype)
+{
+ if ((strlen(src) > 1) && src[0] == '\"') {
+ /* This is a quoted string */
+ if (!(argtype & ARG_STRING))
+ return -1;
+ src++;
+ /* Don't take more than what's there */
+ if (maxlen > strlen(src) - 1)
+ maxlen = strlen(src) - 1;
+ memcpy(out, src, maxlen);
+ ((char *)out)[maxlen] = '\0';
+ } else if (!ast_strlen_zero(src) && (src[0] == '\\')) {
+ if (!(argtype & ARG_NUMBER))
+ return -1;
+ /* Octal value */
+ if (sscanf(src, "%o", (int *)out) != 1)
+ return -1;
+ if (argtype & ARG_STRING) {
+ /* Convert */
+ *((unsigned int *)out) = htonl(*((unsigned int *)out));
+ }
+ } else if ((strlen(src) > 2) && (src[0] == '0') && (tolower(src[1]) == 'x')) {
+ if (!(argtype & ARG_NUMBER))
+ return -1;
+ /* Hex value */
+ if (sscanf(src + 2, "%x", (unsigned int *)out) != 1)
+ return -1;
+ if (argtype & ARG_STRING) {
+ /* Convert */
+ *((unsigned int *)out) = htonl(*((unsigned int *)out));
+ }
+ } else if ((!ast_strlen_zero(src) && isdigit(src[0]))) {
+ if (!(argtype & ARG_NUMBER))
+ return -1;
+ /* Hex value */
+ if (sscanf(src, "%d", (int *)out) != 1)
+ return -1;
+ if (argtype & ARG_STRING) {
+ /* Convert */
+ *((unsigned int *)out) = htonl(*((unsigned int *)out));
+ }
+ } else
+ return -1;
+ return 0;
+}
+
+static char *get_token(char **buf, char *script, int lineno)
+{
+ char *tmp = *buf;
+ char *keyword;
+ int quoted = 0;
+ /* Advance past any white space */
+ while(*tmp && (*tmp < 33))
+ tmp++;
+ if (!*tmp)
+ return NULL;
+ keyword = tmp;
+ while(*tmp && ((*tmp > 32) || quoted)) {
+ if (*tmp == '\"') {
+ quoted = !quoted;
+ }
+ tmp++;
+ }
+ if (quoted) {
+ ast_log(LOG_WARNING, "Mismatched quotes at line %d of %s\n", lineno, script);
+ return NULL;
+ }
+ *tmp = '\0';
+ tmp++;
+ while(*tmp && (*tmp < 33))
+ tmp++;
+ /* Note where we left off */
+ *buf = tmp;
+ return keyword;
+}
+
+static char *validdtmf = "123456789*0#ABCD";
+
+static int send_dtmf(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char dtmfstr[80];
+ char *a;
+ int bytes=0;
+ a = get_token(&args, script, lineno);
+ if (!a) {
+ ast_log(LOG_WARNING, "Expecting something to send for SENDDTMF at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(dtmfstr, a, sizeof(dtmfstr) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid token for SENDDTMF at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ a = dtmfstr;
+ while(*a) {
+ if (strchr(validdtmf, *a)) {
+ *buf = *a;
+ buf++;
+ bytes++;
+ } else
+ ast_log(LOG_WARNING, "'%c' is not a valid DTMF tone at line %d of %s\n", *a, lineno, script);
+ a++;
+ }
+ return bytes;
+}
+
+static int goto_line(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *page;
+ char *gline;
+ int line;
+ unsigned char cmd;
+ page = get_token(&args, script, lineno);
+ gline = get_token(&args, script, lineno);
+ if (!page || !gline) {
+ ast_log(LOG_WARNING, "Expecting page and line number for GOTOLINE at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (!strcasecmp(page, "INFO")) {
+ cmd = 0;
+ } else if (!strcasecmp(page, "COMM")) {
+ cmd = 0x80;
+ } else {
+ ast_log(LOG_WARNING, "Expecting either 'INFO' or 'COMM' page, got got '%s' at line %d of %s\n", page, lineno, script);
+ return 0;
+ }
+ if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
+ return 0;
+ }
+ cmd |= line;
+ buf[0] = 0x8b;
+ buf[1] = cmd;
+ return 2;
+}
+
+static int goto_line_rel(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *dir;
+ char *gline;
+ int line;
+ unsigned char cmd;
+ dir = get_token(&args, script, lineno);
+ gline = get_token(&args, script, lineno);
+ if (!dir || !gline) {
+ ast_log(LOG_WARNING, "Expecting direction and number of lines for GOTOLINEREL at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (!strcasecmp(dir, "UP")) {
+ cmd = 0;
+ } else if (!strcasecmp(dir, "DOWN")) {
+ cmd = 0x20;
+ } else {
+ ast_log(LOG_WARNING, "Expecting either 'UP' or 'DOWN' direction, got '%s' at line %d of %s\n", dir, lineno, script);
+ return 0;
+ }
+ if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script);
+ return 0;
+ }
+ cmd |= line;
+ buf[0] = 0x8c;
+ buf[1] = cmd;
+ return 2;
+}
+
+static int send_delay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *gtime;
+ int ms;
+ gtime = get_token(&args, script, lineno);
+ if (!gtime) {
+ ast_log(LOG_WARNING, "Expecting number of milliseconds to wait at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(&ms, gtime, sizeof(ms), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid delay milliseconds '%s' at line %d of %s\n", gtime, lineno, script);
+ return 0;
+ }
+ buf[0] = 0x90;
+ if (id == 11)
+ buf[1] = ms / 100;
+ else
+ buf[1] = ms / 10;
+ return 2;
+}
+
+static int set_state(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *gstate;
+ int state;
+ gstate = get_token(&args, script, lineno);
+ if (!gstate) {
+ ast_log(LOG_WARNING, "Expecting state number at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(&state, gstate, sizeof(state), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid state number '%s' at line %d of %s\n", gstate, lineno, script);
+ return 0;
+ }
+ buf[0] = id;
+ buf[1] = state;
+ return 2;
+}
+
+static int cleartimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *tok;
+ tok = get_token(&args, script, lineno);
+ if (tok)
+ ast_log(LOG_WARNING, "Clearing timer requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
+
+ buf[0] = id;
+ /* For some reason the clear code is different slightly */
+ if (id == 7)
+ buf[1] = 0x10;
+ else
+ buf[1] = 0x00;
+ return 2;
+}
+
+static struct adsi_flag *getflagbyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
+{
+ int x;
+ for (x=0;x<state->numflags;x++)
+ if (!strcasecmp(state->flags[x].vname, name))
+ return &state->flags[x];
+ /* Return now if we're not allowed to create */
+ if (!create)
+ return NULL;
+ if (state->numflags > 6) {
+ ast_log(LOG_WARNING, "No more flag space at line %d of %s\n", lineno, script);
+ return NULL;
+ }
+ ast_copy_string(state->flags[state->numflags].vname, name, sizeof(state->flags[state->numflags].vname));
+ state->flags[state->numflags].id = state->numflags + 1;
+ state->numflags++;
+ return &state->flags[state->numflags-1];
+}
+
+static int setflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *tok;
+ char sname[80];
+ struct adsi_flag *flag;
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Setting flag requires a flag number at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
+ return 0;
+ }
+ flag = getflagbyname(state, sname, script, lineno, 0);
+ if (!flag) {
+ ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
+ return 0;
+ }
+ buf[0] = id;
+ buf[1] = ((flag->id & 0x7) << 4) | 1;
+ return 2;
+}
+
+static int clearflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *tok;
+ struct adsi_flag *flag;
+ char sname[80];
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Clearing flag requires a flag number at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script);
+ return 0;
+ }
+ flag = getflagbyname(state, sname, script, lineno, 0);
+ if (!flag) {
+ ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script);
+ return 0;
+ }
+ buf[0] = id;
+ buf[1] = ((flag->id & 0x7) << 4);
+ return 2;
+}
+
+static int starttimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *tok;
+ int secs;
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing number of seconds at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(&secs, tok, sizeof(secs), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
+ return 0;
+ }
+ buf[0] = id;
+ buf[1] = 0x1;
+ buf[2] = secs;
+ return 3;
+}
+
+static int geteventbyname(char *name)
+{
+ int x;
+ for (x=0;x<sizeof(events) / sizeof(events[0]); x++) {
+ if (!strcasecmp(events[x].name, name))
+ return events[x].id;
+ }
+ return 0;
+}
+
+static int getjustifybyname(char *name)
+{
+ int x;
+ for (x=0;x<sizeof(justify) / sizeof(justify[0]); x++) {
+ if (!strcasecmp(justify[x].name, name))
+ return justify[x].id;
+ }
+ return -1;
+}
+
+static struct adsi_soft_key *getkeybyname(struct adsi_script *state, char *name, char *script, int lineno)
+{
+ int x;
+ for (x=0;x<state->numkeys;x++)
+ if (!strcasecmp(state->keys[x].vname, name))
+ return &state->keys[x];
+ if (state->numkeys > 61) {
+ ast_log(LOG_WARNING, "No more key space at line %d of %s\n", lineno, script);
+ return NULL;
+ }
+ ast_copy_string(state->keys[state->numkeys].vname, name, sizeof(state->keys[state->numkeys].vname));
+ state->keys[state->numkeys].id = state->numkeys + 2;
+ state->numkeys++;
+ return &state->keys[state->numkeys-1];
+}
+
+static struct adsi_subscript *getsubbyname(struct adsi_script *state, char *name, char *script, int lineno)
+{
+ int x;
+ for (x=0;x<state->numsubs;x++)
+ if (!strcasecmp(state->subs[x].vname, name))
+ return &state->subs[x];
+ if (state->numsubs > 127) {
+ ast_log(LOG_WARNING, "No more subscript space at line %d of %s\n", lineno, script);
+ return NULL;
+ }
+ ast_copy_string(state->subs[state->numsubs].vname, name, sizeof(state->subs[state->numsubs].vname));
+ state->subs[state->numsubs].id = state->numsubs;
+ state->numsubs++;
+ return &state->subs[state->numsubs-1];
+}
+
+static struct adsi_state *getstatebyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
+{
+ int x;
+ for (x=0;x<state->numstates;x++)
+ if (!strcasecmp(state->states[x].vname, name))
+ return &state->states[x];
+ /* Return now if we're not allowed to create */
+ if (!create)
+ return NULL;
+ if (state->numstates > 253) {
+ ast_log(LOG_WARNING, "No more state space at line %d of %s\n", lineno, script);
+ return NULL;
+ }
+ ast_copy_string(state->states[state->numstates].vname, name, sizeof(state->states[state->numstates].vname));
+ state->states[state->numstates].id = state->numstates + 1;
+ state->numstates++;
+ return &state->states[state->numstates-1];
+}
+
+static struct adsi_display *getdisplaybyname(struct adsi_script *state, char *name, char *script, int lineno, int create)
+{
+ int x;
+ for (x=0;x<state->numdisplays;x++)
+ if (!strcasecmp(state->displays[x].vname, name))
+ return &state->displays[x];
+ /* Return now if we're not allowed to create */
+ if (!create)
+ return NULL;
+ if (state->numdisplays > 61) {
+ ast_log(LOG_WARNING, "No more display space at line %d of %s\n", lineno, script);
+ return NULL;
+ }
+ ast_copy_string(state->displays[state->numdisplays].vname, name, sizeof(state->displays[state->numdisplays].vname));
+ state->displays[state->numdisplays].id = state->numdisplays + 1;
+ state->numdisplays++;
+ return &state->displays[state->numdisplays-1];
+}
+
+static int showkeys(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *tok;
+ char newkey[80];
+ int bytes;
+ unsigned char keyid[6];
+ int x;
+ int flagid=0;
+ struct adsi_soft_key *key;
+ struct adsi_flag *flag;
+
+ for (x=0;x<7;x++) {
+ /* Up to 6 key arguments */
+ tok = get_token(&args, script, lineno);
+ if (!tok)
+ break;
+ if (!strcasecmp(tok, "UNLESS")) {
+ /* Check for trailing UNLESS flag */
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
+ } else if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid flag name '%s' at line %d of %s\n", tok, lineno, script);
+ } else if (!(flag = getflagbyname(state, newkey, script, lineno, 0))) {
+ ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", newkey, lineno, script);
+ } else
+ flagid = flag->id;
+ if ((tok = get_token(&args, script, lineno)))
+ ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
+ break;
+ }
+ if (x > 5) {
+ ast_log(LOG_WARNING, "Only 6 keys can be defined, ignoring '%s' at line %d of %s\n", tok, lineno, script);
+ break;
+ }
+ if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid token for key name: %s\n", tok);
+ continue;
+ }
+
+ key = getkeybyname(state, newkey, script, lineno);
+ if (!key)
+ break;
+ keyid[x] = key->id;
+ }
+ buf[0] = id;
+ buf[1] = (flagid & 0x7) << 3 | (x & 0x7);
+ for (bytes=0;bytes<x;bytes++) {
+ buf[bytes + 2] = keyid[bytes];
+ }
+ return 2 + x;
+}
+
+static int showdisplay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *tok;
+ char dispname[80];
+ int line=0;
+ int flag=0;
+ int cmd = 3;
+ struct adsi_display *disp;
+
+ /* Get display */
+ tok = get_token(&args, script, lineno);
+ if (!tok || process_token(dispname, tok, sizeof(dispname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid display name: %s at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
+ return 0;
+ }
+ disp = getdisplaybyname(state, dispname, script, lineno, 0);
+ if (!disp) {
+ ast_log(LOG_WARNING, "Display '%s' is undefined at line %d of %s\n", dispname, lineno, script);
+ return 0;
+ }
+
+ tok = get_token(&args, script, lineno);
+ if (!tok || strcasecmp(tok, "AT")) {
+ ast_log(LOG_WARNING, "Missing token 'AT' at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ /* Get line number */
+ tok = get_token(&args, script, lineno);
+ if (!tok || process_token(&line, tok, sizeof(line), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid line: '%s' at line %d of %s\n", tok ? tok : "<nothing>", lineno, script);
+ return 0;
+ }
+ tok = get_token(&args, script, lineno);
+ if (tok && !strcasecmp(tok, "NOUPDATE")) {
+ cmd = 1;
+ tok = get_token(&args, script, lineno);
+ }
+ if (tok && !strcasecmp(tok, "UNLESS")) {
+ /* Check for trailing UNLESS flag */
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script);
+ } else if (process_token(&flag, tok, sizeof(flag), ARG_NUMBER)) {
+ ast_log(LOG_WARNING, "Invalid flag number '%s' at line %d of %s\n", tok, lineno, script);
+ }
+ if ((tok = get_token(&args, script, lineno)))
+ ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script);
+ }
+
+ buf[0] = id;
+ buf[1] = (cmd << 6) | (disp->id & 0x3f);
+ buf[2] = ((line & 0x1f) << 3) | (flag & 0x7);
+ return 3;
+}
+
+static int cleardisplay(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *tok;
+ tok = get_token(&args, script, lineno);
+ if (tok)
+ ast_log(LOG_WARNING, "Clearing display requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
+
+ buf[0] = id;
+ buf[1] = 0x00;
+ return 2;
+}
+
+static int digitdirect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *tok;
+ tok = get_token(&args, script, lineno);
+ if (tok)
+ ast_log(LOG_WARNING, "Digitdirect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
+
+ buf[0] = id;
+ buf[1] = 0x7;
+ return 2;
+}
+
+static int clearcbone(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *tok;
+ tok = get_token(&args, script, lineno);
+ if (tok)
+ ast_log(LOG_WARNING, "CLEARCB1 requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
+
+ buf[0] = id;
+ buf[1] = 0;
+ return 2;
+}
+
+static int digitcollect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno)
+{
+ char *tok;
+ tok = get_token(&args, script, lineno);
+ if (tok)
+ ast_log(LOG_WARNING, "Digitcollect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script);
+
+ buf[0] = id;
+ buf[1] = 0xf;
+ return 2;
+}
+
+static int subscript(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *tok;
+ char subscript[80];
+ struct adsi_subscript *sub;
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script);
+ return 0;
+ }
+ sub = getsubbyname(state, subscript, script, lineno);
+ if (!sub)
+ return 0;
+ buf[0] = 0x9d;
+ buf[1] = sub->id;
+ return 2;
+}
+
+static int onevent(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ char *tok;
+ char subscript[80];
+ char sname[80];
+ int sawin=0;
+ int event;
+ int snums[8];
+ int scnt = 0;
+ int x;
+ struct adsi_subscript *sub;
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing event for 'ONEVENT' at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ event = geteventbyname(tok);
+ if (event < 1) {
+ ast_log(LOG_WARNING, "'%s' is not a valid event name, at line %d of %s\n", args, lineno, script);
+ return 0;
+ }
+ tok = get_token(&args, script, lineno);
+ while ((!sawin && !strcasecmp(tok, "IN")) ||
+ (sawin && !strcasecmp(tok, "OR"))) {
+ sawin = 1;
+ if (scnt > 7) {
+ ast_log(LOG_WARNING, "No more than 8 states may be specified for inclusion at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ /* Process 'in' things */
+ tok = get_token(&args, script, lineno);
+ if (process_token(sname, tok, sizeof(sname), ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid state name at line %d of %s\n", tok, lineno, script);
+ return 0;
+ }
+ if ((snums[scnt] = getstatebyname(state, sname, script, lineno, 0) < 0)) {
+ ast_log(LOG_WARNING, "State '%s' not declared at line %d of %s\n", sname, lineno, script);
+ return 0;
+ }
+ scnt++;
+ tok = get_token(&args, script, lineno);
+ if (!tok)
+ break;
+ }
+ if (!tok || strcasecmp(tok, "GOTO")) {
+ if (!tok)
+ tok = "<nothing>";
+ if (sawin)
+ ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'OR' at line %d of %s\n", tok, lineno, script);
+ else
+ ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'IN' at line %d of %s\n", tok, lineno, script);
+ }
+ tok = get_token(&args, script, lineno);
+ if (!tok) {
+ ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script);
+ return 0;
+ }
+ if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Invalid subscript '%s' at line %d of %s\n", tok, lineno, script);
+ return 0;
+ }
+ sub = getsubbyname(state, subscript, script, lineno);
+ if (!sub)
+ return 0;
+ buf[0] = 8;
+ buf[1] = event;
+ buf[2] = sub->id | 0x80;
+ for (x=0;x<scnt;x++)
+ buf[3 + x] = snums[x];
+ return 3 + scnt;
+}
+
+struct adsi_key_cmd {
+ char *name;
+ int id;
+ int (*add_args)(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno);
+};
+
+static struct adsi_key_cmd kcmds[] = {
+ { "SENDDTMF", 0, send_dtmf },
+ /* Encoded DTMF would go here */
+ { "ONHOOK", 0x81 },
+ { "OFFHOOK", 0x82 },
+ { "FLASH", 0x83 },
+ { "WAITDIALTONE", 0x84 },
+ /* Send line number */
+ { "BLANK", 0x86 },
+ { "SENDCHARS", 0x87 },
+ { "CLEARCHARS", 0x88 },
+ { "BACKSPACE", 0x89 },
+ /* Tab column */
+ { "GOTOLINE", 0x8b, goto_line },
+ { "GOTOLINEREL", 0x8c, goto_line_rel },
+ { "PAGEUP", 0x8d },
+ { "PAGEDOWN", 0x8e },
+ /* Extended DTMF */
+ { "DELAY", 0x90, send_delay },
+ { "DIALPULSEONE", 0x91 },
+ { "DATAMODE", 0x92 },
+ { "VOICEMODE", 0x93 },
+ /* Display call buffer 'n' */
+ /* Clear call buffer 'n' */
+ { "CLEARCB1", 0x95, clearcbone },
+ { "DIGITCOLLECT", 0x96, digitcollect },
+ { "DIGITDIRECT", 0x96, digitdirect },
+ { "CLEAR", 0x97 },
+ { "SHOWDISPLAY", 0x98, showdisplay },
+ { "CLEARDISPLAY", 0x98, cleardisplay },
+ { "SHOWKEYS", 0x99, showkeys },
+ { "SETSTATE", 0x9a, set_state },
+ { "TIMERSTART", 0x9b, starttimer },
+ { "TIMERCLEAR", 0x9b, cleartimer },
+ { "SETFLAG", 0x9c, setflag },
+ { "CLEARFLAG", 0x9c, clearflag },
+ { "GOTO", 0x9d, subscript },
+ { "EVENT22", 0x9e },
+ { "EVENT23", 0x9f },
+ { "EXIT", 0xa0 },
+};
+
+static struct adsi_key_cmd opcmds[] = {
+
+ /* 1 - Branch on event -- handled specially */
+ { "SHOWKEYS", 2, showkeys },
+ /* Display Control */
+ { "SHOWDISPLAY", 3, showdisplay },
+ { "CLEARDISPLAY", 3, cleardisplay },
+ { "CLEAR", 5 },
+ { "SETSTATE", 6, set_state },
+ { "TIMERSTART", 7, starttimer },
+ { "TIMERCLEAR", 7, cleartimer },
+ { "ONEVENT", 8, onevent },
+ /* 9 - Subroutine label, treated specially */
+ { "SETFLAG", 10, setflag },
+ { "CLEARFLAG", 10, clearflag },
+ { "DELAY", 11, send_delay },
+ { "EXIT", 12 },
+};
+
+
+static int process_returncode(struct adsi_soft_key *key, char *code, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ int x;
+ char *unused;
+ int res;
+ for (x=0;x<sizeof(kcmds) / sizeof(kcmds[0]);x++) {
+ if ((kcmds[x].id > -1) && !strcasecmp(kcmds[x].name, code)) {
+ if (kcmds[x].add_args) {
+ res = kcmds[x].add_args(key->retstr + key->retstrlen,
+ code, kcmds[x].id, args, state, script, lineno);
+ if ((key->retstrlen + res - key->initlen) <= MAX_RET_CODE)
+ key->retstrlen += res;
+ else
+ ast_log(LOG_WARNING, "No space for '%s' code in key '%s' at line %d of %s\n", kcmds[x].name, key->vname, lineno, script);
+ } else {
+ if ((unused = get_token(&args, script, lineno)))
+ ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", kcmds[x].name, lineno, script, unused);
+ if ((key->retstrlen + 1 - key->initlen) <= MAX_RET_CODE) {
+ key->retstr[key->retstrlen] = kcmds[x].id;
+ key->retstrlen++;
+ } else
+ ast_log(LOG_WARNING, "No space for '%s' code in key '%s' at line %d of %s\n", kcmds[x].name, key->vname, lineno, script);
+ }
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int process_opcode(struct adsi_subscript *sub, char *code, char *args, struct adsi_script *state, char *script, int lineno)
+{
+ int x;
+ char *unused;
+ int res;
+ int max = sub->id ? MAX_SUB_LEN : MAX_MAIN_LEN;
+ for (x=0;x<sizeof(opcmds) / sizeof(opcmds[0]);x++) {
+ if ((opcmds[x].id > -1) && !strcasecmp(opcmds[x].name, code)) {
+ if (opcmds[x].add_args) {
+ res = opcmds[x].add_args(sub->data + sub->datalen,
+ code, opcmds[x].id, args, state, script, lineno);
+ if ((sub->datalen + res + 1) <= max)
+ sub->datalen += res;
+ else {
+ ast_log(LOG_WARNING, "No space for '%s' code in subscript '%s' at line %d of %s\n", opcmds[x].name, sub->vname, lineno, script);
+ return -1;
+ }
+ } else {
+ if ((unused = get_token(&args, script, lineno)))
+ ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", opcmds[x].name, lineno, script, unused);
+ if ((sub->datalen + 2) <= max) {
+ sub->data[sub->datalen] = opcmds[x].id;
+ sub->datalen++;
+ } else {
+ ast_log(LOG_WARNING, "No space for '%s' code in key '%s' at line %d of %s\n", opcmds[x].name, sub->vname, lineno, script);
+ return -1;
+ }
+ }
+ /* Separate commands with 0xff */
+ sub->data[sub->datalen] = 0xff;
+ sub->datalen++;
+ sub->inscount++;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int adsi_process(struct adsi_script *state, char *buf, char *script, int lineno)
+{
+ char *keyword;
+ char *args;
+ char vname[256];
+ char tmp[80];
+ char tmp2[80];
+ int lrci;
+ int wi;
+ int event;
+ struct adsi_display *disp;
+ struct adsi_subscript *newsub;
+ /* Find the first keyword */
+ keyword = get_token(&buf, script, lineno);
+ if (!keyword)
+ return 0;
+ switch(state->state) {
+ case STATE_NORMAL:
+ if (!strcasecmp(keyword, "DESCRIPTION")) {
+ args = get_token(&buf, script, lineno);
+ if (args) {
+ if (process_token(state->desc, args, sizeof(state->desc) - 1, ARG_STRING))
+ ast_log(LOG_WARNING, "'%s' is not a valid token for DESCRIPTION at line %d of %s\n", args, lineno, script);
+ } else
+ ast_log(LOG_WARNING, "Missing argument for DESCRIPTION at line %d of %s\n", lineno, script);
+ } else if (!strcasecmp(keyword, "VERSION")) {
+ args = get_token(&buf, script, lineno);
+ if (args) {
+ if (process_token(&state->ver, args, sizeof(state->ver) - 1, ARG_NUMBER))
+ ast_log(LOG_WARNING, "'%s' is not a valid token for VERSION at line %d of %s\n", args, lineno, script);
+ } else
+ ast_log(LOG_WARNING, "Missing argument for VERSION at line %d of %s\n", lineno, script);
+ } else if (!strcasecmp(keyword, "SECURITY")) {
+ args = get_token(&buf, script, lineno);
+ if (args) {
+ if (process_token(state->sec, args, sizeof(state->sec) - 1, ARG_STRING | ARG_NUMBER))
+ ast_log(LOG_WARNING, "'%s' is not a valid token for SECURITY at line %d of %s\n", args, lineno, script);
+ } else
+ ast_log(LOG_WARNING, "Missing argument for SECURITY at line %d of %s\n", lineno, script);
+ } else if (!strcasecmp(keyword, "FDN")) {
+ args = get_token(&buf, script, lineno);
+ if (args) {
+ if (process_token(state->fdn, args, sizeof(state->fdn) - 1, ARG_STRING | ARG_NUMBER))
+ ast_log(LOG_WARNING, "'%s' is not a valid token for FDN at line %d of %s\n", args, lineno, script);
+ } else
+ ast_log(LOG_WARNING, "Missing argument for FDN at line %d of %s\n", lineno, script);
+ } else if (!strcasecmp(keyword, "KEY")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "KEY definition missing name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ state->key = getkeybyname(state, vname, script, lineno);
+ if (!state->key) {
+ ast_log(LOG_WARNING, "Out of key space at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (state->key->defined) {
+ ast_log(LOG_WARNING, "Cannot redefine key '%s' at line %d of %s\n", vname, lineno, script);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ if (!args || strcasecmp(args, "IS")) {
+ ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "KEY definition missing short name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY short name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ if (args) {
+ if (strcasecmp(args, "OR")) {
+ ast_log(LOG_WARNING, "Expecting 'OR' but got '%s' instead at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "KEY definition missing optional long name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(tmp2, args, sizeof(tmp2) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY long name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ } else {
+ ast_copy_string(tmp2, tmp, sizeof(tmp2));
+ }
+ if (strlen(tmp2) > 18) {
+ ast_log(LOG_WARNING, "Truncating full name to 18 characters at line %d of %s\n", lineno, script);
+ tmp2[18] = '\0';
+ }
+ if (strlen(tmp) > 7) {
+ ast_log(LOG_WARNING, "Truncating short name to 7 bytes at line %d of %s\n", lineno, script);
+ tmp[7] = '\0';
+ }
+ /* Setup initial stuff */
+ state->key->retstr[0] = 128;
+ /* 1 has the length */
+ state->key->retstr[2] = state->key->id;
+ /* Put the Full name in */
+ memcpy(state->key->retstr + 3, tmp2, strlen(tmp2));
+ /* Update length */
+ state->key->retstrlen = strlen(tmp2) + 3;
+ /* Put trailing 0xff */
+ state->key->retstr[state->key->retstrlen++] = 0xff;
+ /* Put the short name */
+ memcpy(state->key->retstr + state->key->retstrlen, tmp, strlen(tmp));
+ /* Update length */
+ state->key->retstrlen += strlen(tmp);
+ /* Put trailing 0xff */
+ state->key->retstr[state->key->retstrlen++] = 0xff;
+ /* Record initial length */
+ state->key->initlen = state->key->retstrlen;
+ state->state = STATE_INKEY;
+ } else if (!strcasecmp(keyword, "SUB")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ state->sub = getsubbyname(state, vname, script, lineno);
+ if (!state->sub) {
+ ast_log(LOG_WARNING, "Out of subroutine space at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (state->sub->defined) {
+ ast_log(LOG_WARNING, "Cannot redefine subroutine '%s' at line %d of %s\n", vname, lineno, script);
+ break;
+ }
+ /* Setup sub */
+ state->sub->data[0] = 130;
+ /* 1 is the length */
+ state->sub->data[2] = 0x0; /* Clear extensibility bit */
+ state->sub->datalen = 3;
+ if (state->sub->id) {
+ /* If this isn't the main subroutine, make a subroutine label for it */
+ state->sub->data[3] = 9;
+ state->sub->data[4] = state->sub->id;
+ /* 5 is length */
+ state->sub->data[6] = 0xff;
+ state->sub->datalen = 7;
+ }
+ args = get_token(&buf, script, lineno);
+ if (!args || strcasecmp(args, "IS")) {
+ ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "<nothing>", lineno, script);
+ break;
+ }
+ state->state = STATE_INSUB;
+ } else if (!strcasecmp(keyword, "STATE")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "STATE definition missing name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a STATE name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ if (getstatebyname(state, vname, script, lineno, 0)) {
+ ast_log(LOG_WARNING, "State '%s' is already defined at line %d of %s\n", vname, lineno, script);
+ break;
+ }
+ getstatebyname(state, vname, script, lineno, 1);
+ } else if (!strcasecmp(keyword, "FLAG")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "FLAG definition missing name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a FLAG name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ if (getflagbyname(state, vname, script, lineno, 0)) {
+ ast_log(LOG_WARNING, "Flag '%s' is already defined\n", vname);
+ break;
+ }
+ getflagbyname(state, vname, script, lineno, 1);
+ } else if (!strcasecmp(keyword, "DISPLAY")) {
+ lrci = 0;
+ wi = 0;
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ if (getdisplaybyname(state, vname, script, lineno, 0)) {
+ ast_log(LOG_WARNING, "State '%s' is already defined\n", vname);
+ break;
+ }
+ disp = getdisplaybyname(state, vname, script, lineno, 1);
+ if (!disp)
+ break;
+ args = get_token(&buf, script, lineno);
+ if (!args || strcasecmp(args, "IS")) {
+ ast_log(LOG_WARNING, "Missing 'IS' at line %d of %s\n", lineno, script);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "Missing Column 1 text at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "Token '%s' is not valid column 1 text at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ if (strlen(tmp) > 20) {
+ ast_log(LOG_WARNING, "Truncating column one to 20 characters at line %d of %s\n", lineno, script);
+ tmp[20] = '\0';
+ }
+ memcpy(disp->data + 5, tmp, strlen(tmp));
+ disp->datalen = strlen(tmp) + 5;
+ disp->data[disp->datalen++] = 0xff;
+
+ args = get_token(&buf, script, lineno);
+ if (args && !process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
+ /* Got a column two */
+ if (strlen(tmp) > 20) {
+ ast_log(LOG_WARNING, "Truncating column two to 20 characters at line %d of %s\n", lineno, script);
+ tmp[20] = '\0';
+ }
+ memcpy(disp->data + disp->datalen, tmp, strlen(tmp));
+ disp->datalen += strlen(tmp);
+ args = get_token(&buf, script, lineno);
+ }
+ while(args) {
+ if (!strcasecmp(args, "JUSTIFY")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "Qualifier 'JUSTIFY' requires an argument at line %d of %s\n", lineno, script);
+ break;
+ }
+ lrci = getjustifybyname(args);
+ if (lrci < 0) {
+ ast_log(LOG_WARNING, "'%s' is not a valid justification at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ } else if (!strcasecmp(args, "WRAP")) {
+ wi = 0x80;
+ } else {
+ ast_log(LOG_WARNING, "'%s' is not a known qualifier at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ }
+ if (args) {
+ /* Something bad happened */
+ break;
+ }
+ disp->data[0] = 129;
+ disp->data[1] = disp->datalen - 2;
+ disp->data[2] = ((lrci & 0x3) << 6) | disp->id;
+ disp->data[3] = wi;
+ disp->data[4] = 0xff;
+ } else {
+ ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in PROGRAM\n", keyword);
+ }
+ break;
+ case STATE_INKEY:
+ if (process_returncode(state->key, keyword, buf, state, script, lineno)) {
+ if (!strcasecmp(keyword, "ENDKEY")) {
+ /* Return to normal operation and increment current key */
+ state->state = STATE_NORMAL;
+ state->key->defined = 1;
+ state->key->retstr[1] = state->key->retstrlen - 2;
+ state->key = NULL;
+ } else {
+ ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SOFTKEY definition at line %d of %s\n", keyword, lineno, script);
+ }
+ }
+ break;
+ case STATE_INIF:
+ if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
+ if (!strcasecmp(keyword, "ENDIF")) {
+ /* Return to normal SUB operation and increment current key */
+ state->state = STATE_INSUB;
+ state->sub->defined = 1;
+ /* Store the proper number of instructions */
+ state->sub->ifdata[2] = state->sub->ifinscount;
+ } else if (!strcasecmp(keyword, "GOTO")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "GOTO clause missing Subscript name at line %d of %s\n", lineno, script);
+ break;
+ }
+ if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) {
+ ast_log(LOG_WARNING, "'%s' is not a valid subscript name token at line %d of %s\n", args, lineno, script);
+ break;
+ }
+ newsub = getsubbyname(state, tmp, script, lineno);
+ if (!newsub)
+ break;
+ /* Somehow you use GOTO to go to another place */
+ state->sub->data[state->sub->datalen++] = 0x8;
+ state->sub->data[state->sub->datalen++] = state->sub->ifdata[1];
+ state->sub->data[state->sub->datalen++] = newsub->id;
+ /* Terminate */
+ state->sub->data[state->sub->datalen++] = 0xff;
+ /* Increment counters */
+ state->sub->inscount++;
+ state->sub->ifinscount++;
+ } else {
+ ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in IF clause at line %d of %s\n", keyword, lineno, script);
+ }
+ } else
+ state->sub->ifinscount++;
+ break;
+ case STATE_INSUB:
+ if (process_opcode(state->sub, keyword, buf, state, script, lineno)) {
+ if (!strcasecmp(keyword, "ENDSUB")) {
+ /* Return to normal operation and increment current key */
+ state->state = STATE_NORMAL;
+ state->sub->defined = 1;
+ /* Store the proper length */
+ state->sub->data[1] = state->sub->datalen - 2;
+ if (state->sub->id) {
+ /* if this isn't main, store number of instructions, too */
+ state->sub->data[5] = state->sub->inscount;
+ }
+ state->sub = NULL;
+ } else if (!strcasecmp(keyword, "IFEVENT")) {
+ args = get_token(&buf, script, lineno);
+ if (!args) {
+ ast_log(LOG_WARNING, "IFEVENT clause missing Event name at line %d of %s\n", lineno, script);
+ break;
+ }
+ event = geteventbyname(args);
+ if (event < 1) {
+ ast_log(LOG_WARNING, "'%s' is not a valid event\n", args);
+ break;
+ }
+ args = get_token(&buf, script, lineno);
+ if (!args || strcasecmp(args, "THEN")) {
+ ast_log(LOG_WARNING, "IFEVENT clause missing 'THEN' at line %d of %s\n", lineno, script);
+ break;
+ }
+ state->sub->ifinscount = 0;
+ state->sub->ifdata = state->sub->data +
+ state->sub->datalen;
+ /* Reserve header and insert op codes */
+ state->sub->ifdata[0] = 0x1;
+ state->sub->ifdata[1] = event;
+ /* 2 is for the number of instructions */
+ state->sub->ifdata[3] = 0xff;
+ state->sub->datalen += 4;
+ /* Update Subscript instruction count */
+ state->sub->inscount++;
+ state->state = STATE_INIF;
+ } else {
+ ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SUB definition at line %d of %s\n", keyword, lineno, script);
+ }
+ }
+ break;
+ default:
+ ast_log(LOG_WARNING, "Can't process keyword '%s' in weird state %d\n", keyword, state->state);
+ }
+ return 0;
+}
+
+static struct adsi_script *compile_script(char *script)
+{
+ FILE *f;
+ char fn[256];
+ char buf[256];
+ char *c;
+ int lineno=0;
+ int x, err;
+ struct adsi_script *scr;
+ if (script[0] == '/')
+ ast_copy_string(fn, script, sizeof(fn));
+ else
+ snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, script);
+ f = fopen(fn, "r");
+ if (!f) {
+ ast_log(LOG_WARNING, "Can't open file '%s'\n", fn);
+ return NULL;
+ }
+ if (!(scr = ast_calloc(1, sizeof(*scr)))) {
+ fclose(f);
+ return NULL;
+ }
+ /* Create "main" as first subroutine */
+ getsubbyname(scr, "main", NULL, 0);
+ while(!feof(f)) {
+ if (!fgets(buf, sizeof(buf), f)) {
+ continue;
+ }
+ if (!feof(f)) {
+ lineno++;
+ /* Trim off trailing return */
+ buf[strlen(buf) - 1] = '\0';
+ c = strchr(buf, ';');
+ /* Strip comments */
+ if (c)
+ *c = '\0';
+ if (!ast_strlen_zero(buf))
+ adsi_process(scr, buf, script, lineno);
+ }
+ }
+ fclose(f);
+ /* Make sure we're in the main routine again */
+ switch(scr->state) {
+ case STATE_NORMAL:
+ break;
+ case STATE_INSUB:
+ ast_log(LOG_WARNING, "Missing ENDSUB at end of file %s\n", script);
+ free(scr);
+ return NULL;
+ case STATE_INKEY:
+ ast_log(LOG_WARNING, "Missing ENDKEY at end of file %s\n", script);
+ free(scr);
+ return NULL;
+ }
+ err = 0;
+
+ /* Resolve all keys and record their lengths */
+ for (x=0;x<scr->numkeys;x++) {
+ if (!scr->keys[x].defined) {
+ ast_log(LOG_WARNING, "Key '%s' referenced but never defined in file %s\n", scr->keys[x].vname, fn);
+ err++;
+ }
+ }
+
+ /* Resolve all subs */
+ for (x=0;x<scr->numsubs;x++) {
+ if (!scr->subs[x].defined) {
+ ast_log(LOG_WARNING, "Subscript '%s' referenced but never defined in file %s\n", scr->subs[x].vname, fn);
+ err++;
+ }
+ if (x == (scr->numsubs - 1)) {
+ /* Clear out extension bit on last message */
+ scr->subs[x].data[2] = 0x80;
+ }
+ }
+
+ if (err) {
+ free(scr);
+ return NULL;
+ }
+ return scr;
+}
+
+#ifdef DUMP_MESSAGES
+static void dump_message(char *type, char *vname, unsigned char *buf, int buflen)
+{
+ int x;
+ printf("%s %s: [ ", type, vname);
+ for (x=0;x<buflen;x++)
+ printf("%02x ", buf[x]);
+ printf("]\n");
+}
+#endif
+
+static int adsi_prog(struct ast_channel *chan, char *script)
+{
+ struct adsi_script *scr;
+ int x;
+ unsigned char buf[1024];
+ int bytes;
+ scr = compile_script(script);
+ if (!scr)
+ return -1;
+
+ /* Start an empty ADSI Session */
+ if (ast_adsi_load_session(chan, NULL, 0, 1) < 1)
+ return -1;
+
+ /* Now begin the download attempt */
+ if (ast_adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
+ /* User rejected us for some reason */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "User rejected download attempt\n");
+ ast_log(LOG_NOTICE, "User rejected download on channel %s\n", chan->name);
+ free(scr);
+ return -1;
+ }
+
+ bytes = 0;
+ /* Start with key definitions */
+ for (x=0;x<scr->numkeys;x++) {
+ if (bytes + scr->keys[x].retstrlen > 253) {
+ /* Send what we've collected so far */
+ if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
+ ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
+ return -1;
+ }
+ bytes =0;
+ }
+ memcpy(buf + bytes, scr->keys[x].retstr, scr->keys[x].retstrlen);
+ bytes += scr->keys[x].retstrlen;
+#ifdef DUMP_MESSAGES
+ dump_message("Key", scr->keys[x].vname, scr->keys[x].retstr, scr->keys[x].retstrlen);
+#endif
+ }
+ if (bytes) {
+ if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
+ ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
+ return -1;
+ }
+ }
+
+ bytes = 0;
+ /* Continue with the display messages */
+ for (x=0;x<scr->numdisplays;x++) {
+ if (bytes + scr->displays[x].datalen > 253) {
+ /* Send what we've collected so far */
+ if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
+ ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
+ return -1;
+ }
+ bytes =0;
+ }
+ memcpy(buf + bytes, scr->displays[x].data, scr->displays[x].datalen);
+ bytes += scr->displays[x].datalen;
+#ifdef DUMP_MESSAGES
+ dump_message("Display", scr->displays[x].vname, scr->displays[x].data, scr->displays[x].datalen);
+#endif
+ }
+ if (bytes) {
+ if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
+ ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
+ return -1;
+ }
+ }
+
+ bytes = 0;
+ /* Send subroutines */
+ for (x=0;x<scr->numsubs;x++) {
+ if (bytes + scr->subs[x].datalen > 253) {
+ /* Send what we've collected so far */
+ if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
+ ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
+ return -1;
+ }
+ bytes =0;
+ }
+ memcpy(buf + bytes, scr->subs[x].data, scr->subs[x].datalen);
+ bytes += scr->subs[x].datalen;
+#ifdef DUMP_MESSAGES
+ dump_message("Sub", scr->subs[x].vname, scr->subs[x].data, scr->subs[x].datalen);
+#endif
+ }
+ if (bytes) {
+ if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) {
+ ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x);
+ return -1;
+ }
+ }
+
+
+ bytes = 0;
+ bytes += ast_adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", "");
+ bytes += ast_adsi_set_line(buf, ADSI_INFO_PAGE, 1);
+ if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0)
+ return -1;
+ if (ast_adsi_end_download(chan)) {
+ /* Download failed for some reason */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Download attempt failed\n");
+ ast_log(LOG_NOTICE, "Download failed on %s\n", chan->name);
+ free(scr);
+ return -1;
+ }
+ free(scr);
+ ast_adsi_unload_session(chan);
+ return 0;
+}
+
+static int adsi_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ struct ast_module_user *u;
+
+ u = ast_module_user_add(chan);
+
+ if (ast_strlen_zero(data))
+ data = "asterisk.adsi";
+
+ if (!ast_adsi_available(chan)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "ADSI Unavailable on CPE. Not bothering to try.\n");
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "ADSI Available on CPE. Attempting Upload.\n");
+ res = adsi_prog(chan, data);
+ }
+
+ ast_module_user_remove(u);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ ast_module_user_hangup_all();
+
+ res = ast_unregister_application(app);
+
+
+ return res;
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, adsi_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Asterisk ADSI Programming Application");
diff --git a/apps/app_alarmreceiver.c b/apps/app_alarmreceiver.c
new file mode 100644
index 000000000..8afce25d5
--- /dev/null
+++ b/apps/app_alarmreceiver.c
@@ -0,0 +1,841 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004 - 2005 Steve Rodgers
+ *
+ * Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Central Station Alarm receiver for Ademco Contact ID
+ * \author Steve Rodgers <hwstar@rodgers.sdcoxmail.com>
+ *
+ * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
+ *
+ * Use at your own risk. Please consult the GNU GPL license document included with Asterisk. *
+ *
+ * *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING *** WARNING ***
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/options.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/config.h"
+#include "asterisk/localtime.h"
+#include "asterisk/callerid.h"
+#include "asterisk/astdb.h"
+#include "asterisk/utils.h"
+
+#define ALMRCV_CONFIG "alarmreceiver.conf"
+#define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID"
+
+struct event_node{
+ char data[17];
+ struct event_node *next;
+};
+
+typedef struct event_node event_node_t;
+
+static char *app = "AlarmReceiver";
+
+static char *synopsis = "Provide support for receiving alarm reports from a burglar or fire alarm panel";
+static char *descrip =
+" AlarmReceiver(): Only 1 signalling format is supported at this time: Ademco\n"
+"Contact ID. This application should be called whenever there is an alarm\n"
+"panel calling in to dump its events. The application will handshake with the\n"
+"alarm panel, and receive events, validate them, handshake them, and store them\n"
+"until the panel hangs up. Once the panel hangs up, the application will run the\n"
+"system command specified by the eventcmd setting in alarmreceiver.conf and pipe\n"
+"the events to the standard input of the application. The configuration file also\n"
+"contains settings for DTMF timing, and for the loudness of the acknowledgement\n"
+"tones.\n";
+
+/* Config Variables */
+
+static int fdtimeout = 2000;
+static int sdtimeout = 200;
+static int toneloudness = 4096;
+static int log_individual_events = 0;
+static char event_spool_dir[128] = {'\0'};
+static char event_app[128] = {'\0'};
+static char db_family[128] = {'\0'};
+static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"};
+
+/* Misc variables */
+
+static char event_file[14] = "/event-XXXXXX";
+
+/*
+* Attempt to access a database variable and increment it,
+* provided that the user defined db-family in alarmreceiver.conf
+* The alarmreceiver app will write statistics to a few variables
+* in this family if it is defined. If the new key doesn't exist in the
+* family, then create it and set its value to 1.
+*/
+
+static void database_increment( char *key )
+{
+ int res = 0;
+ unsigned v;
+ char value[16];
+
+
+ if (ast_strlen_zero(db_family))
+ return; /* If not defined, don't do anything */
+
+ res = ast_db_get(db_family, key, value, sizeof(value) - 1);
+
+ if(res){
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Creating database entry %s and setting to 1\n", key);
+ /* Guess we have to create it */
+ res = ast_db_put(db_family, key, "1");
+ return;
+ }
+
+ sscanf(value, "%u", &v);
+ v++;
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: New value for %s: %u\n", key, v);
+
+ snprintf(value, sizeof(value), "%u", v);
+
+ res = ast_db_put(db_family, key, value);
+
+ if((res)&&(option_verbose >= 4))
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: database_increment write error\n");
+
+ return;
+}
+
+
+/*
+* Build a MuLaw data block for a single frequency tone
+*/
+
+static void make_tone_burst(unsigned char *data, float freq, float loudness, int len, int *x)
+{
+ int i;
+ float val;
+
+ for(i = 0; i < len; i++){
+ val = loudness * sin((freq * 2.0 * M_PI * (*x)++)/8000.0);
+ data[i] = AST_LIN2MU((int)val);
+ }
+
+ /* wrap back around from 8000 */
+
+ if (*x >= 8000) *x = 0;
+ return;
+}
+
+/*
+* Send a single tone burst for a specifed duration and frequency.
+* Returns 0 if successful
+*/
+
+static int send_tone_burst(struct ast_channel *chan, float freq, int duration, int tldn)
+{
+ int res = 0;
+ int i = 0;
+ int x = 0;
+ struct ast_frame *f, wf;
+
+ struct {
+ unsigned char offset[AST_FRIENDLY_OFFSET];
+ unsigned char buf[640];
+ } tone_block;
+
+ for(;;)
+ {
+
+ if (ast_waitfor(chan, -1) < 0){
+ res = -1;
+ break;
+ }
+
+ f = ast_read(chan);
+ if (!f){
+ res = -1;
+ break;
+ }
+
+ if (f->frametype == AST_FRAME_VOICE) {
+ wf.frametype = AST_FRAME_VOICE;
+ wf.subclass = AST_FORMAT_ULAW;
+ wf.offset = AST_FRIENDLY_OFFSET;
+ wf.mallocd = 0;
+ wf.data = tone_block.buf;
+ wf.datalen = f->datalen;
+ wf.samples = wf.datalen;
+
+ make_tone_burst(tone_block.buf, freq, (float) tldn, wf.datalen, &x);
+
+ i += wf.datalen / 8;
+ if (i > duration) {
+ ast_frfree(f);
+ break;
+ }
+ if (ast_write(chan, &wf)){
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Failed to write frame on %s\n", chan->name);
+ ast_log(LOG_WARNING, "AlarmReceiver Failed to write frame on %s\n",chan->name);
+ res = -1;
+ ast_frfree(f);
+ break;
+ }
+ }
+
+ ast_frfree(f);
+ }
+ return res;
+}
+
+/*
+* Receive a string of DTMF digits where the length of the digit string is known in advance. Do not give preferential
+* treatment to any digit value, and allow separate time out values to be specified for the first digit and all subsequent
+* digits.
+*
+* Returns 0 if all digits successfully received.
+* Returns 1 if a digit time out occurred
+* Returns -1 if the caller hung up or there was a channel error.
+*
+*/
+
+static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int length, int fdto, int sdto)
+{
+ int res = 0;
+ int i = 0;
+ int r;
+ struct ast_frame *f;
+ struct timeval lastdigittime;
+
+ lastdigittime = ast_tvnow();
+ for(;;){
+ /* if outa time, leave */
+ if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) >
+ ((i > 0) ? sdto : fdto)){
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: DTMF Digit Timeout on %s\n", chan->name);
+
+ ast_log(LOG_DEBUG,"AlarmReceiver: DTMF timeout on chan %s\n",chan->name);
+
+ res = 1;
+ break;
+ }
+
+ if ((r = ast_waitfor(chan, -1) < 0)) {
+ ast_log(LOG_DEBUG, "Waitfor returned %d\n", r);
+ continue;
+ }
+
+ f = ast_read(chan);
+
+ if (f == NULL){
+ res = -1;
+ break;
+ }
+
+ /* If they hung up, leave */
+ if ((f->frametype == AST_FRAME_CONTROL) &&
+ (f->subclass == AST_CONTROL_HANGUP)){
+ ast_frfree(f);
+ res = -1;
+ break;
+ }
+
+ /* if not DTMF, just do it again */
+ if (f->frametype != AST_FRAME_DTMF){
+ ast_frfree(f);
+ continue;
+ }
+
+ digit_string[i++] = f->subclass; /* save digit */
+
+ ast_frfree(f);
+
+ /* If we have all the digits we expect, leave */
+ if(i >= length)
+ break;
+
+ lastdigittime = ast_tvnow();
+ }
+
+ digit_string[i] = '\0'; /* Nul terminate the end of the digit string */
+ return res;
+
+}
+
+/*
+* Write the metadata to the log file
+*/
+
+static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan)
+{
+ int res = 0;
+ time_t t;
+ struct tm now;
+ char *cl,*cn;
+ char workstring[80];
+ char timestamp[80];
+
+ /* Extract the caller ID location */
+ if (chan->cid.cid_num)
+ ast_copy_string(workstring, chan->cid.cid_num, sizeof(workstring));
+ workstring[sizeof(workstring) - 1] = '\0';
+
+ ast_callerid_parse(workstring, &cn, &cl);
+ if (cl)
+ ast_shrink_phone_number(cl);
+
+
+ /* Get the current time */
+
+ time(&t);
+ ast_localtime(&t, &now, NULL);
+
+ /* Format the time */
+
+ strftime(timestamp, sizeof(timestamp), time_stamp_format, &now);
+
+
+ res = fprintf(logfile, "\n\n[metadata]\n\n");
+
+ if(res >= 0)
+ res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type);
+
+ if(res >= 0)
+ res = fprintf(logfile, "CALLINGFROM=%s\n", (!cl) ? "<unknown>" : cl);
+
+ if(res >- 0)
+ res = fprintf(logfile, "CALLERNAME=%s\n", (!cn) ? "<unknown>" : cn);
+
+ if(res >= 0)
+ res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp);
+
+ if(res >= 0)
+ res = fprintf(logfile, "[events]\n\n");
+
+ if(res < 0){
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't write metadata\n");
+
+ ast_log(LOG_DEBUG,"AlarmReceiver: can't write metadata\n");
+ }
+ else
+ res = 0;
+
+ return res;
+}
+
+/*
+* Write a single event to the log file
+*/
+
+static int write_event( FILE *logfile, event_node_t *event)
+{
+ int res = 0;
+
+ if( fprintf(logfile, "%s\n", event->data) < 0)
+ res = -1;
+
+ return res;
+}
+
+
+/*
+* If we are configured to log events, do so here.
+*
+*/
+
+static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event)
+{
+
+ int res = 0;
+ char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = "";
+ int fd;
+ FILE *logfile;
+ event_node_t *elp = event;
+
+ if (!ast_strlen_zero(event_spool_dir)) {
+
+ /* Make a template */
+
+ ast_copy_string(workstring, event_spool_dir, sizeof(workstring));
+ strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1);
+
+ /* Make the temporary file */
+
+ fd = mkstemp(workstring);
+
+ if(fd == -1){
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't make temporary file\n");
+ ast_log(LOG_DEBUG,"AlarmReceiver: can't make temporary file\n");
+ res = -1;
+ }
+
+ if(!res){
+ logfile = fdopen(fd, "w");
+ if(logfile){
+ /* Write the file */
+ res = write_metadata(logfile, signalling_type, chan);
+ if(!res)
+ while((!res) && (elp != NULL)){
+ res = write_event(logfile, elp);
+ elp = elp->next;
+ }
+ if(!res){
+ if(fflush(logfile) == EOF)
+ res = -1;
+ if(!res){
+ if(fclose(logfile) == EOF)
+ res = -1;
+ }
+ }
+ }
+ else
+ res = -1;
+ }
+ }
+
+ return res;
+}
+
+/*
+* This function implements the logic to receive the Ademco contact ID format.
+*
+* The function will return 0 when the caller hangs up, else a -1 if there was a problem.
+*/
+
+static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int fdto, int sdto, int tldn, event_node_t **ehead)
+{
+ int i,j;
+ int res = 0;
+ int checksum;
+ char event[17];
+ event_node_t *enew, *elp;
+ int got_some_digits = 0;
+ int events_received = 0;
+ int ack_retries = 0;
+
+ static char digit_map[15] = "0123456789*#ABC";
+ static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15};
+
+ database_increment("calls-received");
+
+ /* Wait for first event */
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Waiting for first event from panel\n");
+
+ while(res >= 0){
+
+ if(got_some_digits == 0){
+
+ /* Send ACK tone sequence */
+
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n");
+
+
+ res = send_tone_burst(chan, 1400.0, 100, tldn);
+
+ if(!res)
+ res = ast_safe_sleep(chan, 100);
+
+ if(!res){
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n");
+
+ res = send_tone_burst(chan, 2300.0, 100, tldn);
+ }
+
+ }
+
+ if( res >= 0)
+ res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto);
+
+ if (res < 0){
+
+ if(events_received == 0)
+ /* Hangup with no events received should be logged in the DB */
+ database_increment("no-events-received");
+ else{
+ if(ack_retries){
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: ACK retries during this call: %d\n", ack_retries);
+
+ database_increment("ack-retries");
+ }
+ }
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: App exiting...\n");
+ res = -1;
+ break;
+ }
+
+ if(res != 0){
+ /* Didn't get all of the digits */
+ if(option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Incomplete string: %s, trying again...\n", event);
+
+ if(!got_some_digits){
+ got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0;
+ ack_retries++;
+ }
+ continue;
+ }
+
+ got_some_digits = 1;
+
+ if(option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Received Event %s\n", event);
+ ast_log(LOG_DEBUG, "AlarmReceiver: Received event: %s\n", event);
+
+ /* Calculate checksum */
+
+ for(j = 0, checksum = 0; j < 16; j++){
+ for(i = 0 ; i < sizeof(digit_map) ; i++){
+ if(digit_map[i] == event[j])
+ break;
+ }
+
+ if(i == 16)
+ break;
+
+ checksum += digit_weights[i];
+ }
+
+ if(i == 16){
+ if(option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]);
+ continue; /* Bad character */
+ }
+
+ /* Checksum is mod(15) of the total */
+
+ checksum = checksum % 15;
+
+ if (checksum) {
+ database_increment("checksum-errors");
+ if (option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Nonzero checksum\n");
+ ast_log(LOG_DEBUG, "AlarmReceiver: Nonzero checksum\n");
+ continue;
+ }
+
+ /* Check the message type for correctness */
+
+ if(strncmp(event + 4, "18", 2)){
+ if(strncmp(event + 4, "98", 2)){
+ database_increment("format-errors");
+ if(option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Wrong message type\n");
+ ast_log(LOG_DEBUG, "AlarmReceiver: Wrong message type\n");
+ continue;
+ }
+ }
+
+ events_received++;
+
+ /* Queue the Event */
+ if (!(enew = ast_calloc(1, sizeof(*enew)))) {
+ res = -1;
+ break;
+ }
+
+ enew->next = NULL;
+ ast_copy_string(enew->data, event, sizeof(enew->data));
+
+ /*
+ * Insert event onto end of list
+ */
+
+ if(*ehead == NULL){
+ *ehead = enew;
+ }
+ else{
+ for(elp = *ehead; elp->next != NULL; elp = elp->next)
+ ;
+
+ elp->next = enew;
+ }
+
+ if(res > 0)
+ res = 0;
+
+ /* Let the user have the option of logging the single event before sending the kissoff tone */
+
+ if((res == 0) && (log_individual_events))
+ res = log_events(chan, ADEMCO_CONTACT_ID, enew);
+
+ /* Wait 200 msec before sending kissoff */
+
+ if(res == 0)
+ res = ast_safe_sleep(chan, 200);
+
+ /* Send the kissoff tone */
+
+ if(res == 0)
+ res = send_tone_burst(chan, 1400.0, 900, tldn);
+ }
+
+
+ return res;
+}
+
+
+/*
+* This is the main function called by Asterisk Core whenever the App is invoked in the extension logic.
+* This function will always return 0.
+*/
+
+static int alarmreceiver_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_module_user *u;
+ event_node_t *elp, *efree;
+ char signalling_type[64] = "";
+
+ event_node_t *event_head = NULL;
+
+ u = ast_module_user_add(chan);
+
+ /* Set write and read formats to ULAW */
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Setting read and write formats to ULAW\n");
+
+ if (ast_set_write_format(chan,AST_FORMAT_ULAW)){
+ ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name);
+ ast_module_user_remove(u);
+ return -1;
+ }
+
+ if (ast_set_read_format(chan,AST_FORMAT_ULAW)){
+ ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name);
+ ast_module_user_remove(u);
+ return -1;
+ }
+
+ /* Set default values for this invokation of the application */
+
+ ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type));
+
+
+ /* Answer the channel if it is not already */
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Answering channel\n");
+
+ if (chan->_state != AST_STATE_UP) {
+
+ res = ast_answer(chan);
+
+ if (res) {
+ ast_module_user_remove(u);
+ return -1;
+ }
+ }
+
+ /* Wait for the connection to settle post-answer */
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Waiting for connection to stabilize\n");
+
+ res = ast_safe_sleep(chan, 1250);
+
+ /* Attempt to receive the events */
+
+ if(!res){
+
+ /* Determine the protocol to receive in advance */
+ /* Note: Ademco contact is the only one supported at this time */
+ /* Others may be added later */
+
+ if(!strcmp(signalling_type, ADEMCO_CONTACT_ID))
+ receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head);
+ else
+ res = -1;
+ }
+
+
+
+ /* Events queued by receiver, write them all out here if so configured */
+
+ if((!res) && (log_individual_events == 0)){
+ res = log_events(chan, signalling_type, event_head);
+
+ }
+
+ /*
+ * Do we exec a command line at the end?
+ */
+
+ if((!res) && (!ast_strlen_zero(event_app)) && (event_head)){
+ ast_log(LOG_DEBUG,"Alarmreceiver: executing: %s\n", event_app);
+ ast_safe_system(event_app);
+ }
+
+ /*
+ * Free up the data allocated in our linked list
+ */
+
+ for(elp = event_head; (elp != NULL);){
+ efree = elp;
+ elp = elp->next;
+ free(efree);
+ }
+
+
+ ast_module_user_remove(u);
+
+ return 0;
+}
+
+/*
+* Load the configuration from the configuration file
+*/
+
+static int load_config(void)
+{
+ struct ast_config *cfg;
+ const char *p;
+
+ /* Read in the config file */
+
+ cfg = ast_config_load(ALMRCV_CONFIG);
+
+ if(!cfg){
+
+ if(option_verbose >= 4)
+ ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: No config file\n");
+ return 0;
+ }
+ else{
+
+
+ p = ast_variable_retrieve(cfg, "general", "eventcmd");
+
+ if(p){
+ ast_copy_string(event_app, p, sizeof(event_app));
+ event_app[sizeof(event_app) - 1] = '\0';
+ }
+
+ p = ast_variable_retrieve(cfg, "general", "loudness");
+ if(p){
+ toneloudness = atoi(p);
+ if(toneloudness < 100)
+ toneloudness = 100;
+ if(toneloudness > 8192)
+ toneloudness = 8192;
+ }
+ p = ast_variable_retrieve(cfg, "general", "fdtimeout");
+ if(p){
+ fdtimeout = atoi(p);
+ if(fdtimeout < 1000)
+ fdtimeout = 1000;
+ if(fdtimeout > 10000)
+ fdtimeout = 10000;
+ }
+
+ p = ast_variable_retrieve(cfg, "general", "sdtimeout");
+ if(p){
+ sdtimeout = atoi(p);
+ if(sdtimeout < 110)
+ sdtimeout = 110;
+ if(sdtimeout > 4000)
+ sdtimeout = 4000;
+
+ }
+
+ p = ast_variable_retrieve(cfg, "general", "logindividualevents");
+ if(p){
+ log_individual_events = ast_true(p);
+
+ }
+
+ p = ast_variable_retrieve(cfg, "general", "eventspooldir");
+
+ if(p){
+ ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir));
+ event_spool_dir[sizeof(event_spool_dir) - 1] = '\0';
+ }
+
+ p = ast_variable_retrieve(cfg, "general", "timestampformat");
+
+ if(p){
+ ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format));
+ time_stamp_format[sizeof(time_stamp_format) - 1] = '\0';
+ }
+
+ p = ast_variable_retrieve(cfg, "general", "db-family");
+
+ if(p){
+ ast_copy_string(db_family, p, sizeof(db_family));
+ db_family[sizeof(db_family) - 1] = '\0';
+ }
+ ast_config_destroy(cfg);
+ }
+ return 1;
+
+}
+
+/*
+* These functions are required to implement an Asterisk App.
+*/
+
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ ast_module_user_hangup_all();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ if(load_config())
+ return ast_register_application(app, alarmreceiver_exec, synopsis, descrip);
+ else
+ return AST_MODULE_LOAD_DECLINE;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk");
diff --git a/apps/app_amd.c b/apps/app_amd.c
new file mode 100644
index 000000000..52f474048
--- /dev/null
+++ b/apps/app_amd.c
@@ -0,0 +1,430 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2003 - 2006, Aheeva Technology.
+ *
+ * Claude Klimos (claude.klimos@aheeva.com)
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/options.h"
+#include "asterisk/channel.h"
+#include "asterisk/dsp.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/app.h"
+
+
+static char *app = "AMD";
+static char *synopsis = "Attempts to detect answering machines";
+static char *descrip =
+" AMD([initialSilence][|greeting][|afterGreetingSilence][|totalAnalysisTime]\n"
+" [|minimumWordLength][|betweenWordsSilence][|maximumNumberOfWords]\n"
+" [|silenceThreshold])\n"
+" This application attempts to detect answering machines at the beginning\n"
+" of outbound calls. Simply call this application after the call\n"
+" has been answered (outbound only, of course).\n"
+" When loaded, AMD reads amd.conf and uses the parameters specified as\n"
+" default values. Those default values get overwritten when calling AMD\n"
+" with parameters.\n"
+"- 'initialSilence' is the maximum silence duration before the greeting. If\n"
+" exceeded then MACHINE.\n"
+"- 'greeting' is the maximum length of a greeting. If exceeded then MACHINE.\n"
+"- 'afterGreetingSilence' is the silence after detecting a greeting.\n"
+" If exceeded then HUMAN.\n"
+"- 'totalAnalysisTime' is the maximum time allowed for the algorithm to decide\n"
+" on a HUMAN or MACHINE.\n"
+"- 'minimumWordLength'is the minimum duration of Voice to considered as a word.\n"
+"- 'betweenWordsSilence' is the minimum duration of silence after a word to \n"
+" consider the audio that follows as a new word.\n"
+"- 'maximumNumberOfWords'is the maximum number of words in the greeting. \n"
+" If exceeded then MACHINE.\n"
+"- 'silenceThreshold' is the silence threshold.\n"
+"This application sets the following channel variable upon completion:\n"
+" AMDSTATUS - This is the status of the answering machine detection.\n"
+" Possible values are:\n"
+" MACHINE | HUMAN | NOTSURE | HANGUP\n"
+" AMDCAUSE - Indicates the cause that led to the conclusion.\n"
+" Possible values are:\n"
+" TOOLONG-<%d total_time>\n"
+" INITIALSILENCE-<%d silenceDuration>-<%d initialSilence>\n"
+" HUMAN-<%d silenceDuration>-<%d afterGreetingSilence>\n"
+" MAXWORDS-<%d wordsCount>-<%d maximumNumberOfWords>\n"
+" LONGGREETING-<%d voiceDuration>-<%d greeting>\n";
+
+#define STATE_IN_WORD 1
+#define STATE_IN_SILENCE 2
+
+/* Some default values for the algorithm parameters. These defaults will be overwritten from amd.conf */
+static int dfltInitialSilence = 2500;
+static int dfltGreeting = 1500;
+static int dfltAfterGreetingSilence = 800;
+static int dfltTotalAnalysisTime = 5000;
+static int dfltMinimumWordLength = 100;
+static int dfltBetweenWordsSilence = 50;
+static int dfltMaximumNumberOfWords = 3;
+static int dfltSilenceThreshold = 256;
+
+/* Set to the lowest ms value provided in amd.conf or application parameters */
+static int dfltMaxWaitTimeForFrame = 50;
+
+static void isAnsweringMachine(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ struct ast_frame *f = NULL;
+ struct ast_dsp *silenceDetector = NULL;
+ int dspsilence = 0, readFormat, framelength = 0;
+ int inInitialSilence = 1;
+ int inGreeting = 0;
+ int voiceDuration = 0;
+ int silenceDuration = 0;
+ int iTotalTime = 0;
+ int iWordsCount = 0;
+ int currentState = STATE_IN_WORD;
+ int previousState = STATE_IN_SILENCE;
+ int consecutiveVoiceDuration = 0;
+ char amdCause[256] = "", amdStatus[256] = "";
+ char *parse = ast_strdupa(data);
+
+ /* Lets set the initial values of the variables that will control the algorithm.
+ The initial values are the default ones. If they are passed as arguments
+ when invoking the application, then the default values will be overwritten
+ by the ones passed as parameters. */
+ int initialSilence = dfltInitialSilence;
+ int greeting = dfltGreeting;
+ int afterGreetingSilence = dfltAfterGreetingSilence;
+ int totalAnalysisTime = dfltTotalAnalysisTime;
+ int minimumWordLength = dfltMinimumWordLength;
+ int betweenWordsSilence = dfltBetweenWordsSilence;
+ int maximumNumberOfWords = dfltMaximumNumberOfWords;
+ int silenceThreshold = dfltSilenceThreshold;
+ int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(argInitialSilence);
+ AST_APP_ARG(argGreeting);
+ AST_APP_ARG(argAfterGreetingSilence);
+ AST_APP_ARG(argTotalAnalysisTime);
+ AST_APP_ARG(argMinimumWordLength);
+ AST_APP_ARG(argBetweenWordsSilence);
+ AST_APP_ARG(argMaximumNumberOfWords);
+ AST_APP_ARG(argSilenceThreshold);
+ );
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "AMD: %s %s %s (Fmt: %d)\n", chan->name ,chan->cid.cid_ani, chan->cid.cid_rdnis, chan->readformat);
+
+ /* Lets parse the arguments. */
+ if (!ast_strlen_zero(parse)) {
+ /* Some arguments have been passed. Lets parse them and overwrite the defaults. */
+ AST_STANDARD_APP_ARGS(args, parse);
+ if (!ast_strlen_zero(args.argInitialSilence))
+ initialSilence = atoi(args.argInitialSilence);
+ if (!ast_strlen_zero(args.argGreeting))
+ greeting = atoi(args.argGreeting);
+ if (!ast_strlen_zero(args.argAfterGreetingSilence))
+ afterGreetingSilence = atoi(args.argAfterGreetingSilence);
+ if (!ast_strlen_zero(args.argTotalAnalysisTime))
+ totalAnalysisTime = atoi(args.argTotalAnalysisTime);
+ if (!ast_strlen_zero(args.argMinimumWordLength))
+ minimumWordLength = atoi(args.argMinimumWordLength);
+ if (!ast_strlen_zero(args.argBetweenWordsSilence))
+ betweenWordsSilence = atoi(args.argBetweenWordsSilence);
+ if (!ast_strlen_zero(args.argMaximumNumberOfWords))
+ maximumNumberOfWords = atoi(args.argMaximumNumberOfWords);
+ if (!ast_strlen_zero(args.argSilenceThreshold))
+ silenceThreshold = atoi(args.argSilenceThreshold);
+ } else if (option_debug)
+ ast_log(LOG_DEBUG, "AMD using the default parameters.\n");
+
+ /* Find lowest ms value, that will be max wait time for a frame */
+ if (maxWaitTimeForFrame > initialSilence)
+ maxWaitTimeForFrame = initialSilence;
+ if (maxWaitTimeForFrame > greeting)
+ maxWaitTimeForFrame = greeting;
+ if (maxWaitTimeForFrame > afterGreetingSilence)
+ maxWaitTimeForFrame = afterGreetingSilence;
+ if (maxWaitTimeForFrame > totalAnalysisTime)
+ maxWaitTimeForFrame = totalAnalysisTime;
+ if (maxWaitTimeForFrame > minimumWordLength)
+ maxWaitTimeForFrame = minimumWordLength;
+ if (maxWaitTimeForFrame > betweenWordsSilence)
+ maxWaitTimeForFrame = betweenWordsSilence;
+
+ /* Now we're ready to roll! */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
+ "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n",
+ initialSilence, greeting, afterGreetingSilence, totalAnalysisTime,
+ minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold );
+
+ /* Set read format to signed linear so we get signed linear frames in */
+ readFormat = chan->readformat;
+ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0 ) {
+ ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", chan->name );
+ pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
+ pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
+ return;
+ }
+
+ /* Create a new DSP that will detect the silence */
+ if (!(silenceDetector = ast_dsp_new())) {
+ ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", chan->name );
+ pbx_builtin_setvar_helper(chan , "AMDSTATUS", "");
+ pbx_builtin_setvar_helper(chan , "AMDCAUSE", "");
+ return;
+ }
+
+ /* Set silence threshold to specified value */
+ ast_dsp_set_threshold(silenceDetector, silenceThreshold);
+
+ /* Now we go into a loop waiting for frames from the channel */
+ while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) {
+
+ /* If we fail to read in a frame, that means they hung up */
+ if (!(f = ast_read(chan))) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "AMD: HANGUP\n");
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Got hangup\n");
+ strcpy(amdStatus, "HANGUP");
+ break;
+ }
+
+ if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) {
+ /* If the total time exceeds the analysis time then give up as we are not too sure */
+ if (f->frametype == AST_FRAME_VOICE)
+ framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS);
+ else
+ framelength += 2 * maxWaitTimeForFrame;
+
+ iTotalTime += framelength;
+ if (iTotalTime >= totalAnalysisTime) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name );
+ ast_frfree(f);
+ strcpy(amdStatus , "NOTSURE");
+ sprintf(amdCause , "TOOLONG-%d", iTotalTime);
+ break;
+ }
+
+ /* Feed the frame of audio into the silence detector and see if we get a result */
+ if (f->frametype != AST_FRAME_VOICE)
+ dspsilence += 2 * maxWaitTimeForFrame;
+ else {
+ dspsilence = 0;
+ ast_dsp_silence(silenceDetector, f, &dspsilence);
+ }
+
+ if (dspsilence > 0) {
+ silenceDuration = dspsilence;
+
+ if (silenceDuration >= betweenWordsSilence) {
+ if (currentState != STATE_IN_SILENCE ) {
+ previousState = currentState;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "AMD: Changed state to STATE_IN_SILENCE\n");
+ }
+ currentState = STATE_IN_SILENCE;
+ consecutiveVoiceDuration = 0;
+ }
+
+ if (inInitialSilence == 1 && silenceDuration >= initialSilence) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n",
+ silenceDuration, initialSilence);
+ ast_frfree(f);
+ strcpy(amdStatus , "MACHINE");
+ sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence);
+ res = 1;
+ break;
+ }
+
+ if (silenceDuration >= afterGreetingSilence && inGreeting == 1) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "AMD: HUMAN: silenceDuration:%d afterGreetingSilence:%d\n",
+ silenceDuration, afterGreetingSilence);
+ ast_frfree(f);
+ strcpy(amdStatus , "HUMAN");
+ sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence);
+ res = 1;
+ break;
+ }
+
+ } else {
+ consecutiveVoiceDuration += framelength;
+ voiceDuration += framelength;
+
+ /* If I have enough consecutive voice to say that I am in a Word, I can only increment the
+ number of words if my previous state was Silence, which means that I moved into a word. */
+ if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) {
+ iWordsCount++;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "AMD: Word detected. iWordsCount:%d\n", iWordsCount);
+ previousState = currentState;
+ currentState = STATE_IN_WORD;
+ }
+
+ if (iWordsCount >= maximumNumberOfWords) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: iWordsCount:%d\n", iWordsCount);
+ ast_frfree(f);
+ strcpy(amdStatus , "MACHINE");
+ sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords);
+ res = 1;
+ break;
+ }
+
+ if (inGreeting == 1 && voiceDuration >= greeting) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", voiceDuration, greeting);
+ ast_frfree(f);
+ strcpy(amdStatus , "MACHINE");
+ sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting);
+ res = 1;
+ break;
+ }
+
+ if (voiceDuration >= minimumWordLength ) {
+ silenceDuration = 0;
+ inInitialSilence = 0;
+ inGreeting = 1;
+ }
+
+ }
+ }
+ ast_frfree(f);
+ }
+
+ if (!res) {
+ /* It took too long to get a frame back. Giving up. */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name);
+ strcpy(amdStatus , "NOTSURE");
+ sprintf(amdCause , "TOOLONG-%d", iTotalTime);
+ }
+
+ /* Set the status and cause on the channel */
+ pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus);
+ pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause);
+
+ /* Restore channel read format */
+ if (readFormat && ast_set_read_format(chan, readFormat))
+ ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name);
+
+ /* Free the DSP used to detect silence */
+ ast_dsp_free(silenceDetector);
+
+ return;
+}
+
+
+static int amd_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_module_user *u = NULL;
+
+ u = ast_module_user_add(chan);
+ isAnsweringMachine(chan, data);
+ ast_module_user_remove(u);
+
+ return 0;
+}
+
+static void load_config(void)
+{
+ struct ast_config *cfg = NULL;
+ char *cat = NULL;
+ struct ast_variable *var = NULL;
+
+ if (!(cfg = ast_config_load("amd.conf"))) {
+ ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n");
+ return;
+ }
+
+ cat = ast_category_browse(cfg, NULL);
+
+ while (cat) {
+ if (!strcasecmp(cat, "general") ) {
+ var = ast_variable_browse(cfg, cat);
+ while (var) {
+ if (!strcasecmp(var->name, "initial_silence")) {
+ dfltInitialSilence = atoi(var->value);
+ } else if (!strcasecmp(var->name, "greeting")) {
+ dfltGreeting = atoi(var->value);
+ } else if (!strcasecmp(var->name, "after_greeting_silence")) {
+ dfltAfterGreetingSilence = atoi(var->value);
+ } else if (!strcasecmp(var->name, "silence_threshold")) {
+ dfltSilenceThreshold = atoi(var->value);
+ } else if (!strcasecmp(var->name, "total_analysis_time")) {
+ dfltTotalAnalysisTime = atoi(var->value);
+ } else if (!strcasecmp(var->name, "min_word_length")) {
+ dfltMinimumWordLength = atoi(var->value);
+ } else if (!strcasecmp(var->name, "between_words_silence")) {
+ dfltBetweenWordsSilence = atoi(var->value);
+ } else if (!strcasecmp(var->name, "maximum_number_of_words")) {
+ dfltMaximumNumberOfWords = atoi(var->value);
+ } else {
+ ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n",
+ app, cat, var->name, var->lineno);
+ }
+ var = var->next;
+ }
+ }
+ cat = ast_category_browse(cfg, cat);
+ }
+
+ ast_config_destroy(cfg);
+
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] "
+ "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n",
+ dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime,
+ dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold );
+
+ return;
+}
+
+static int unload_module(void)
+{
+ ast_module_user_hangup_all();
+ return ast_unregister_application(app);
+}
+
+static int load_module(void)
+{
+ load_config();
+ return ast_register_application(app, amd_exec, synopsis, descrip);
+}
+
+static int reload(void)
+{
+ load_config();
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
diff --git a/apps/app_authenticate.c b/apps/app_authenticate.c
new file mode 100644
index 000000000..e182deee2
--- /dev/null
+++ b/apps/app_authenticate.c
@@ -0,0 +1,254 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Execute arbitrary authenticate commands
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/astdb.h"
+#include "asterisk/utils.h"
+#include "asterisk/options.h"
+
+enum {
+ OPT_ACCOUNT = (1 << 0),
+ OPT_DATABASE = (1 << 1),
+ OPT_JUMP = (1 << 2),
+ OPT_MULTIPLE = (1 << 3),
+ OPT_REMOVE = (1 << 4),
+} auth_option_flags;
+
+AST_APP_OPTIONS(auth_app_options, {
+ AST_APP_OPTION('a', OPT_ACCOUNT),
+ AST_APP_OPTION('d', OPT_DATABASE),
+ AST_APP_OPTION('j', OPT_JUMP),
+ AST_APP_OPTION('m', OPT_MULTIPLE),
+ AST_APP_OPTION('r', OPT_REMOVE),
+});
+
+
+static char *app = "Authenticate";
+
+static char *synopsis = "Authenticate a user";
+
+static char *descrip =
+" Authenticate(password[|options[|maxdigits]]): This application asks the caller\n"
+"to enter a given password in order to continue dialplan execution. If the password\n"
+"begins with the '/' character, it is interpreted as a file which contains a list of\n"
+"valid passwords, listed 1 password per line in the file.\n"
+" When using a database key, the value associated with the key can be anything.\n"
+"Users have three attempts to authenticate before the channel is hung up. If the\n"
+"passsword is invalid, the 'j' option is specified, and priority n+101 exists,\n"
+"dialplan execution will continnue at this location.\n"
+" Options:\n"
+" a - Set the channels' account code to the password that is entered\n"
+" d - Interpret the given path as database key, not a literal file\n"
+" j - Support jumping to n+101 if authentication fails\n"
+" m - Interpret the given path as a file which contains a list of account\n"
+" codes and password hashes delimited with ':', listed one per line in\n"
+" the file. When one of the passwords is matched, the channel will have\n"
+" its account code set to the corresponding account code in the file.\n"
+" r - Remove the database key upon successful entry (valid with 'd' only)\n"
+" maxdigits - maximum acceptable number of digits. Stops reading after\n"
+" maxdigits have been entered (without requiring the user to\n"
+" press the '#' key).\n"
+" Defaults to 0 - no limit - wait for the user press the '#' key.\n"
+;
+
+static int auth_exec(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ int retries;
+ struct ast_module_user *u;
+ char passwd[256];
+ char *prompt;
+ int maxdigits;
+ char *argcopy =NULL;
+ struct ast_flags flags = {0};
+
+ AST_DECLARE_APP_ARGS(arglist,
+ AST_APP_ARG(password);
+ AST_APP_ARG(options);
+ AST_APP_ARG(maxdigits);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n");
+ return -1;
+ }
+
+ u = ast_module_user_add(chan);
+
+ if (chan->_state != AST_STATE_UP) {
+ res = ast_answer(chan);
+ if (res) {
+ ast_module_user_remove(u);
+ return -1;
+ }
+ }
+
+ argcopy = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(arglist,argcopy);
+
+ if (!ast_strlen_zero(arglist.options)) {
+ ast_app_parse_options(auth_app_options, &flags, NULL, arglist.options);
+ }
+
+ if (!ast_strlen_zero(arglist.maxdigits)) {
+ maxdigits = atoi(arglist.maxdigits);
+ if ((maxdigits<1) || (maxdigits>sizeof(passwd)-2))
+ maxdigits = sizeof(passwd) - 2;
+ } else {
+ maxdigits = sizeof(passwd) - 2;
+ }
+
+ /* Start asking for password */
+ prompt = "agent-pass";
+ for (retries = 0; retries < 3; retries++) {
+ res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0);
+ if (res < 0)
+ break;
+ res = 0;
+ if (arglist.password[0] == '/') {
+ if (ast_test_flag(&flags,OPT_DATABASE)) {
+ char tmp[256];
+ /* Compare against a database key */
+ if (!ast_db_get(arglist.password + 1, passwd, tmp, sizeof(tmp))) {
+ /* It's a good password */
+ if (ast_test_flag(&flags,OPT_REMOVE)) {
+ ast_db_del(arglist.password + 1, passwd);
+ }
+ break;
+ }
+ } else {
+ /* Compare against a file */
+ FILE *f;
+ f = fopen(arglist.password, "r");
+ if (f) {
+ char buf[256] = "";
+ char md5passwd[33] = "";
+ char *md5secret = NULL;
+
+ while (!feof(f)) {
+ if (!fgets(buf, sizeof(buf), f)) {
+ continue;
+ }
+ if (!ast_strlen_zero(buf)) {
+ size_t len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ if (ast_test_flag(&flags,OPT_MULTIPLE)) {
+ md5secret = strchr(buf, ':');
+ if (md5secret == NULL)
+ continue;
+ *md5secret = '\0';
+ md5secret++;
+ ast_md5_hash(md5passwd, passwd);
+ if (!strcmp(md5passwd, md5secret)) {
+ if (ast_test_flag(&flags,OPT_ACCOUNT))
+ ast_cdr_setaccount(chan, buf);
+ break;
+ }
+ } else {
+ if (!strcmp(passwd, buf)) {
+ if (ast_test_flag(&flags,OPT_ACCOUNT))
+ ast_cdr_setaccount(chan, buf);
+ break;
+ }
+ }
+ }
+ }
+ fclose(f);
+ if (!ast_strlen_zero(buf)) {
+ if (ast_test_flag(&flags,OPT_MULTIPLE)) {
+ if (md5secret && !strcmp(md5passwd, md5secret))
+ break;
+ } else {
+ if (!strcmp(passwd, buf))
+ break;
+ }
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno));
+ }
+ } else {
+ /* Compare against a fixed password */
+ if (!strcmp(passwd, arglist.password))
+ break;
+ }
+ prompt="auth-incorrect";
+ }
+ if ((retries < 3) && !res) {
+ if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE))
+ ast_cdr_setaccount(chan, passwd);
+ res = ast_streamfile(chan, "auth-thankyou", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ } else {
+ if (ast_test_flag(&flags,OPT_JUMP) && ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101) == 0) {
+ res = 0;
+ } else {
+ if (!ast_streamfile(chan, "vm-goodbye", chan->language))
+ res = ast_waitstream(chan, "");
+ res = -1;
+ }
+ }
+ ast_module_user_remove(u);
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ ast_module_user_hangup_all();
+
+ res = ast_unregister_application(app);
+
+
+ return res;
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, auth_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application");
diff --git a/apps/app_cdr.c b/apps/app_cdr.c
new file mode 100644
index 000000000..a70d9d2f5
--- /dev/null
+++ b/apps/app_cdr.c
@@ -0,0 +1,78 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Martin Pycko <martinp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Applications connected with CDR engine
+ *
+ * Martin Pycko <martinp@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+
+static char *nocdr_descrip =
+" NoCDR(): This application will tell Asterisk not to maintain a CDR for the\n"
+"current call.\n";
+
+static char *nocdr_app = "NoCDR";
+static char *nocdr_synopsis = "Tell Asterisk to not maintain a CDR for the current call";
+
+
+static int nocdr_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_module_user *u;
+
+ u = ast_module_user_add(chan);
+
+ if (chan->cdr) {
+ ast_set_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED);
+ }
+
+ ast_module_user_remove(u);
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(nocdr_app);
+
+ ast_module_user_hangup_all();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ return ast_register_application(nocdr_app, nocdr_exec, nocdr_synopsis, nocdr_descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Tell Asterisk to not maintain a CDR for the current call");
diff --git a/apps/app_chanisavail.c b/apps/app_chanisavail.c
new file mode 100644
index 000000000..c6931d8db
--- /dev/null
+++ b/apps/app_chanisavail.c
@@ -0,0 +1,173 @@
+/*
+* Asterisk -- An open source telephony toolkit.
+*
+* Copyright (C) 1999 - 2005, Digium, Inc.
+*
+* Mark Spencer <markster@digium.com>
+* James Golovich <james@gnuinter.net>
+*
+* See http://www.asterisk.org for more information about
+* the Asterisk project. Please do not directly contact
+* any of the maintainers of this project for assistance;
+* the project provides a web site, mailing lists and IRC
+* channels for your use.
+*
+* This program is free software, distributed under the terms of
+* the GNU General Public License Version 2. See the LICENSE file
+* at the top of the source tree.
+*/
+
+/*! \file
+ *
+ * \brief Check if Channel is Available
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author James Golovich <james@gnuinter.net>
+
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/app.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/options.h"
+
+static char *app = "ChanIsAvail";
+
+static char *synopsis = "Check channel availability";
+
+static char *descrip =
+" ChanIsAvail(Technology/resource[&Technology2/resource2...][|options]): \n"
+"This application will check to see if any of the specified channels are\n"
+"available. The following variables will be set by this application:\n"
+" ${AVAILCHAN} - the name of the available channel, if one exists\n"
+" ${AVAILORIGCHAN} - the canonical channel name that was used to create the channel\n"
+" ${AVAILSTATUS} - the status code for the available channel\n"
+" Options:\n"
+" s - Consider the channel unavailable if the channel is in use at all\n"
+" j - Support jumping to priority n+101 if no channel is available\n";
+
+
+static int chanavail_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1, inuse=-1, option_state=0, priority_jump=0;
+ int status;
+ struct ast_module_user *u;
+ char *info, tmp[512], trychan[512], *peers, *tech, *number, *rest, *cur;
+ struct ast_channel *tempchan;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(reqchans);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ChanIsAvail requires an argument (Zap/1&Zap/2)\n");
+ return -1;
+ }
+
+ u = ast_module_user_add(chan);
+
+ info = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, info);
+
+ if (args.options) {
+ if (strchr(args.options, 's'))
+ option_state = 1;
+ if (strchr(args.options, 'j'))
+ priority_jump = 1;
+ }
+ peers = args.reqchans;
+ if (peers) {
+ cur = peers;
+ do {
+ /* remember where to start next time */
+ rest = strchr(cur, '&');
+ if (rest) {
+ *rest = 0;
+ rest++;
+ }
+ tech = cur;
+ number = strchr(tech, '/');
+ if (!number) {
+ ast_log(LOG_WARNING, "ChanIsAvail argument takes format ([technology]/[device])\n");
+ ast_module_user_remove(u);
+ return -1;
+ }
+ *number = '\0';
+ number++;
+
+ if (option_state) {
+ /* If the pbx says in use then don't bother trying further.
+ This is to permit testing if someone's on a call, even if the
+ channel can permit more calls (ie callwaiting, sip calls, etc). */
+
+ snprintf(trychan, sizeof(trychan), "%s/%s",cur,number);
+ status = inuse = ast_device_state(trychan);
+ }
+ if ((inuse <= 1) && (tempchan = ast_request(tech, chan->nativeformats, number, &status))) {
+ pbx_builtin_setvar_helper(chan, "AVAILCHAN", tempchan->name);
+ /* Store the originally used channel too */
+ snprintf(tmp, sizeof(tmp), "%s/%s", tech, number);
+ pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", tmp);
+ snprintf(tmp, sizeof(tmp), "%d", status);
+ pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp);
+ ast_hangup(tempchan);
+ tempchan = NULL;
+ res = 1;
+ break;
+ } else {
+ snprintf(tmp, sizeof(tmp), "%d", status);
+ pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp);
+ }
+ cur = rest;
+ } while (cur);
+ }
+ if (res < 1) {
+ pbx_builtin_setvar_helper(chan, "AVAILCHAN", "");
+ pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", "");
+ if (priority_jump || ast_opt_priority_jumping) {
+ if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
+ ast_module_user_remove(u);
+ return -1;
+ }
+ }
+ }
+
+ ast_module_user_remove(u);
+ return 0;
+}
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res = ast_unregister_application(app);
+
+ ast_module_user_hangup_all();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, chanavail_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Check channel availability");
diff --git a/apps/app_channelredirect.c b/apps/app_channelredirect.c
new file mode 100644
index 000000000..a8eedf9b4
--- /dev/null
+++ b/apps/app_channelredirect.c
@@ -0,0 +1,140 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Sergey Basmanov
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief ChannelRedirect application
+ *
+ * \author Sergey Basmanov <sergey_basmanov@mail.ru>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+#include "asterisk/features.h"
+#include "asterisk/options.h"
+
+static char *app = "ChannelRedirect";
+static char *synopsis = "Redirects given channel to a dialplan target.";
+static char *descrip =
+"ChannelRedirect(channel|[[context|]extension|]priority):\n"
+" Sends the specified channel to the specified extension priority\n";
+
+
+static int asyncgoto_exec(struct ast_channel *chan, void *data)
+{
+ int res = -1;
+ struct ast_module_user *u;
+ char *info, *context, *exten, *priority;
+ int prio = 1;
+ struct ast_channel *chan2 = NULL;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(channel);
+ AST_APP_ARG(label);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "%s requires an argument (channel|[[context|]exten|]priority)\n", app);
+ return -1;
+ }
+
+ u = ast_module_user_add(chan);
+
+ info = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, info);
+
+ if (ast_strlen_zero(args.channel) || ast_strlen_zero(args.label)) {
+ ast_log(LOG_WARNING, "%s requires an argument (channel|[[context|]exten|]priority)\n", app);
+ goto quit;
+ }
+
+ chan2 = ast_get_channel_by_name_locked(args.channel);
+ if (!chan2) {
+ ast_log(LOG_WARNING, "No such channel: %s\n", args.channel);
+ goto quit;
+ }
+
+ /* Parsed right to left, so standard parsing won't work */
+ context = strsep(&args.label, "|");
+ exten = strsep(&args.label, "|");
+ if (exten) {
+ priority = strsep(&args.label, "|");
+ if (!priority) {
+ priority = exten;
+ exten = context;
+ context = NULL;
+ }
+ } else {
+ priority = context;
+ context = NULL;
+ }
+
+ /* ast_findlabel_extension does not convert numeric priorities; it only does a lookup */
+ if (!(prio = atoi(priority)) && !(prio = ast_findlabel_extension(chan2, S_OR(context, chan2->context),
+ S_OR(exten, chan2->exten), priority, chan2->cid.cid_num))) {
+ ast_log(LOG_WARNING, "'%s' is not a known priority or label\n", priority);
+ goto chanquit;
+ }
+
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "Attempting async goto (%s) to %s|%s|%d\n", args.channel, S_OR(context, chan2->context), S_OR(exten, chan2->exten), prio);
+
+ if (ast_async_goto_if_exists(chan2, S_OR(context, chan2->context), S_OR(exten, chan2->exten), prio))
+ ast_log(LOG_WARNING, "%s failed for %s\n", app, args.channel);
+ else
+ res = 0;
+
+ chanquit:
+ ast_mutex_unlock(&chan2->lock);
+ quit:
+ ast_module_user_remove(u);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ ast_module_user_hangup_all();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, asyncgoto_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Channel Redirect");
diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c
new file mode 100644
index 000000000..18e4972a5
--- /dev/null
+++ b/apps/app_chanspy.c
@@ -0,0 +1,878 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com)
+ * Copyright (C) 2005 - 2008, Digium, Inc.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief ChanSpy: Listen in on any channel.
+ *
+ * \author Anthony Minessale II <anthmct@yahoo.com>
+ * \author Joshua Colp <jcolp@digium.com>
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/audiohook.h"
+#include "asterisk/features.h"
+#include "asterisk/options.h"
+#include "asterisk/app.h"
+#include "asterisk/utils.h"
+#include "asterisk/say.h"
+#include "asterisk/pbx.h"
+#include "asterisk/translate.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+
+#define AST_NAME_STRLEN 256
+
+/* "Zap/pseudo" is ten characters.
+ * "DAHDI/pseudo" is twelve characters.
+ */
+
+static const char *tdesc = "Listen to a channel, and optionally whisper into it";
+static const char *app_chan = "ChanSpy";
+static const char *desc_chan =
+" ChanSpy([chanprefix][|options]): This application is used to listen to the\n"
+"audio from an Asterisk channel. This includes the audio coming in and\n"
+"out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
+"only channels beginning with this string will be spied upon.\n"
+" While spying, the following actions may be performed:\n"
+" - Dialing # cycles the volume level.\n"
+" - Dialing * will stop spying and look for another channel to spy on.\n"
+" - Dialing a series of digits followed by # builds a channel name to append\n"
+" to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
+" the digits '1234#' while spying will begin spying on the channel\n"
+" 'Agent/1234'.\n"
+" Options:\n"
+" b - Only spy on channels involved in a bridged call.\n"
+" g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
+" contain 'grp' in an optional : delimited list.\n"
+" q - Don't play a beep when beginning to spy on a channel, or speak the\n"
+" selected channel name.\n"
+" r[(basename)] - Record the session to the monitor spool directory. An\n"
+" optional base for the filename may be specified. The\n"
+" default is 'chanspy'.\n"
+" v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
+" negative value refers to a quieter setting.\n"
+" w - Enable 'whisper' mode, so the spying channel can talk to\n"
+" the spied-on channel.\n"
+" W - Enable 'private whisper' mode, so the spying channel can\n"
+" talk to the spied-on channel but cannot listen to that\n"
+" channel.\n"
+;
+
+static const char *app_ext = "ExtenSpy";
+static const char *desc_ext =
+" ExtenSpy(exten[@context][|options]): This application is used to listen to the\n"
+"audio from an Asterisk channel. This includes the audio coming in and\n"
+"out of the channel being spied on. Only channels created by outgoing calls for the\n"
+"specified extension will be selected for spying. If the optional context is not\n"
+"supplied, the current channel's context will be used.\n"
+" While spying, the following actions may be performed:\n"
+" - Dialing # cycles the volume level.\n"
+" - Dialing * will stop spying and look for another channel to spy on.\n"
+" Options:\n"
+" b - Only spy on channels involved in a bridged call.\n"
+" g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n"
+" contain 'grp' in an optional : delimited list.\n"
+" q - Don't play a beep when beginning to spy on a channel, or speak the\n"
+" selected channel name.\n"
+" r[(basename)] - Record the session to the monitor spool directory. An\n"
+" optional base for the filename may be specified. The\n"
+" default is 'chanspy'.\n"
+" v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
+" negative value refers to a quieter setting.\n"
+" w - Enable 'whisper' mode, so the spying channel can talk to\n"
+" the spied-on channel.\n"
+" W - Enable 'private whisper' mode, so the spying channel can\n"
+" talk to the spied-on channel but cannot listen to that\n"
+" channel.\n"
+;
+
+enum {
+ OPTION_QUIET = (1 << 0), /* Quiet, no announcement */
+ OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */
+ OPTION_VOLUME = (1 << 2), /* Specify initial volume */
+ OPTION_GROUP = (1 << 3), /* Only look at channels in group */
+ OPTION_RECORD = (1 << 4),
+ OPTION_WHISPER = (1 << 5),
+ OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */
+} chanspy_opt_flags;
+
+enum {
+ OPT_ARG_VOLUME = 0,
+ OPT_ARG_GROUP,
+ OPT_ARG_RECORD,
+ OPT_ARG_ARRAY_SIZE,
+} chanspy_opt_args;
+
+AST_APP_OPTIONS(spy_opts, {
+ AST_APP_OPTION('q', OPTION_QUIET),
+ AST_APP_OPTION('b', OPTION_BRIDGED),
+ AST_APP_OPTION('w', OPTION_WHISPER),
+ AST_APP_OPTION('W', OPTION_PRIVATE),
+ AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
+ AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
+ AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
+});
+
+static int next_unique_id_to_use = 0;
+
+struct chanspy_translation_helper {
+ /* spy data */
+ struct ast_audiohook spy_audiohook;
+ struct ast_audiohook whisper_audiohook;
+ int fd;
+ int volfactor;
+};
+
+static void *spy_alloc(struct ast_channel *chan, void *data)
+{
+ /* just store the data pointer in the channel structure */
+ return data;
+}
+
+static void spy_release(struct ast_channel *chan, void *data)
+{
+ /* nothing to do */
+}
+
+static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct chanspy_translation_helper *csth = data;
+ struct ast_frame *f;
+
+ ast_audiohook_lock(&csth->spy_audiohook);
+ if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
+ ast_audiohook_unlock(&csth->spy_audiohook);
+ return -1;
+ }
+
+ f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
+
+ ast_audiohook_unlock(&csth->spy_audiohook);
+
+ if (!f)
+ return 0;
+
+ if (ast_write(chan, f)) {
+ ast_frfree(f);
+ return -1;
+ }
+
+ if (csth->fd) {
+ if (write(csth->fd, f->data, f->datalen) < 0) {
+ ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
+ }
+ }
+
+ ast_frfree(f);
+
+ return 0;
+}
+
+static struct ast_generator spygen = {
+ .alloc = spy_alloc,
+ .release = spy_release,
+ .generate = spy_generate,
+};
+
+static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
+{
+ int res;
+ struct ast_channel *peer;
+
+ ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
+
+ res = ast_audiohook_attach(chan, audiohook);
+
+ if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
+ ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
+ }
+ return res;
+}
+
+struct chanspy_ds {
+ struct ast_channel *chan;
+ char unique_id[20];
+ ast_mutex_t lock;
+};
+
+static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds,
+ int *volfactor, int fd, const struct ast_flags *flags)
+{
+ struct chanspy_translation_helper csth;
+ int running = 0, res, x = 0;
+ char inp[24] = {0};
+ char *name;
+ struct ast_frame *f;
+ struct ast_silence_generator *silgen = NULL;
+ struct ast_channel *spyee = NULL;
+ const char *spyer_name;
+
+ ast_channel_lock(chan);
+ spyer_name = ast_strdupa(chan->name);
+ ast_channel_unlock(chan);
+
+ ast_mutex_lock(&spyee_chanspy_ds->lock);
+ if (spyee_chanspy_ds->chan) {
+ spyee = spyee_chanspy_ds->chan;
+ ast_channel_lock(spyee);
+ }
+ ast_mutex_unlock(&spyee_chanspy_ds->lock);
+
+ if (!spyee)
+ return 0;
+
+ /* We now hold the channel lock on spyee */
+
+ if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
+ ast_channel_unlock(spyee);
+ return 0;
+ }
+
+ name = ast_strdupa(spyee->name);
+ if (option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name);
+
+ memset(&csth, 0, sizeof(csth));
+
+ ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
+
+ if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
+ ast_audiohook_destroy(&csth.spy_audiohook);
+ ast_channel_unlock(spyee);
+ return 0;
+ }
+
+ if (ast_test_flag(flags, OPTION_WHISPER)) {
+ ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
+ start_spying(spyee, spyer_name, &csth.whisper_audiohook);
+ }
+
+ ast_channel_unlock(spyee);
+ spyee = NULL;
+
+ csth.volfactor = *volfactor;
+
+ if (csth.volfactor) {
+ csth.spy_audiohook.options.read_volume = csth.volfactor;
+ csth.spy_audiohook.options.write_volume = csth.volfactor;
+ }
+
+ csth.fd = fd;
+
+ if (ast_test_flag(flags, OPTION_PRIVATE))
+ silgen = ast_channel_start_silence_generator(chan);
+ else
+ ast_activate_generator(chan, &spygen, &csth);
+
+ /* We can no longer rely on 'spyee' being an actual channel;
+ it can be hung up and freed out from under us. However, the
+ channel destructor will put NULL into our csth.spy.chan
+ field when that happens, so that is our signal that the spyee
+ channel has gone away.
+ */
+
+ /* Note: it is very important that the ast_waitfor() be the first
+ condition in this expression, so that if we wait for some period
+ of time before receiving a frame from our spying channel, we check
+ for hangup on the spied-on channel _after_ knowing that a frame
+ has arrived, since the spied-on channel could have gone away while
+ we were waiting
+ */
+ while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
+ if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
+ running = -1;
+ break;
+ }
+
+ if (ast_test_flag(flags, OPTION_WHISPER) && (f->frametype == AST_FRAME_VOICE)) {
+ ast_audiohook_lock(&csth.whisper_audiohook);
+ ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
+ ast_audiohook_unlock(&csth.whisper_audiohook);
+ ast_frfree(f);
+ continue;
+ }
+
+ res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
+ ast_frfree(f);
+ if (!res)
+ continue;
+
+ if (x == sizeof(inp))
+ x = 0;
+
+ if (res < 0) {
+ running = -1;
+ break;
+ }
+
+ if (res == '*') {
+ running = 0;
+ break;
+ } else if (res == '#') {
+ if (!ast_strlen_zero(inp)) {
+ running = atoi(inp);
+ break;
+ }
+
+ (*volfactor)++;
+ if (*volfactor > 4)
+ *volfactor = -4;
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor);
+ csth.volfactor = *volfactor;
+ csth.spy_audiohook.options.read_volume = csth.volfactor;
+ csth.spy_audiohook.options.write_volume = csth.volfactor;
+ } else if (res >= '0' && res <= '9') {
+ inp[x++] = res;
+ }
+ }
+
+ if (ast_test_flag(flags, OPTION_PRIVATE))
+ ast_channel_stop_silence_generator(chan, silgen);
+ else
+ ast_deactivate_generator(chan);
+
+ if (ast_test_flag(flags, OPTION_WHISPER)) {
+ ast_audiohook_lock(&csth.whisper_audiohook);
+ ast_audiohook_detach(&csth.whisper_audiohook);
+ ast_audiohook_unlock(&csth.whisper_audiohook);
+ ast_audiohook_destroy(&csth.whisper_audiohook);
+ }
+
+ ast_audiohook_lock(&csth.spy_audiohook);
+ ast_audiohook_detach(&csth.spy_audiohook);
+ ast_audiohook_unlock(&csth.spy_audiohook);
+ ast_audiohook_destroy(&csth.spy_audiohook);
+
+ if (option_verbose >= 2)
+ ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name);
+
+ return running;
+}
+
+/*!
+ * \note This relies on the embedded lock to be recursive, as it may be called
+ * due to a call to chanspy_ds_free with the lock held there.
+ */
+static void chanspy_ds_destroy(void *data)
+{
+ struct chanspy_ds *chanspy_ds = data;
+
+ /* Setting chan to be NULL is an atomic operation, but we don't want this
+ * value to change while this lock is held. The lock is held elsewhere
+ * while it performs non-atomic operations with this channel pointer */
+
+ ast_mutex_lock(&chanspy_ds->lock);
+ chanspy_ds->chan = NULL;
+ ast_mutex_unlock(&chanspy_ds->lock);
+}
+
+static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+ struct chanspy_ds *chanspy_ds = data;
+
+ ast_mutex_lock(&chanspy_ds->lock);
+ chanspy_ds->chan = new_chan;
+ ast_mutex_unlock(&chanspy_ds->lock);
+}
+
+static const struct ast_datastore_info chanspy_ds_info = {
+ .type = "chanspy",
+ .destroy = chanspy_ds_destroy,
+ .chan_fixup = chanspy_ds_chan_fixup,
+};
+
+static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
+{
+ if (!chanspy_ds)
+ return NULL;
+
+ ast_mutex_lock(&chanspy_ds->lock);
+ if (chanspy_ds->chan) {
+ struct ast_datastore *datastore;
+ struct ast_channel *chan;
+
+ chan = chanspy_ds->chan;
+
+ ast_channel_lock(chan);
+ if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
+ ast_channel_datastore_remove(chan, datastore);
+ /* chanspy_ds->chan is NULL after this call */
+ chanspy_ds_destroy(datastore->data);
+ datastore->data = NULL;
+ ast_channel_datastore_free(datastore);
+ }
+ ast_channel_unlock(chan);
+ }
+ ast_mutex_unlock(&chanspy_ds->lock);
+
+ return NULL;
+}
+
+/*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */
+static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
+{
+ struct ast_datastore *datastore = NULL;
+
+ ast_mutex_lock(&chanspy_ds->lock);
+
+ if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
+ ast_mutex_unlock(&chanspy_ds->lock);
+ chanspy_ds = chanspy_ds_free(chanspy_ds);
+ ast_channel_unlock(chan);
+ return NULL;
+ }
+
+ chanspy_ds->chan = chan;
+ datastore->data = chanspy_ds;
+ ast_channel_datastore_add(chan, datastore);
+
+ return chanspy_ds;
+}
+
+static struct chanspy_ds *next_channel(struct ast_channel *chan,
+ const struct ast_channel *last, const char *spec,
+ const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
+{
+ struct ast_channel *this;
+ char channel_name[AST_CHANNEL_NAME];
+ static size_t PSEUDO_CHAN_LEN = 0;
+
+ if (!PSEUDO_CHAN_LEN) {
+ PSEUDO_CHAN_LEN = *dahdi_chan_name_len + strlen("/pseudo");
+ }
+
+redo:
+ if (spec)
+ this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
+ else if (exten)
+ this = ast_walk_channel_by_exten_locked(last, exten, context);
+ else
+ this = ast_channel_walk_locked(last);
+
+ if (!this)
+ return NULL;
+
+ snprintf(channel_name, AST_CHANNEL_NAME, "%s/pseudo", dahdi_chan_name);
+ if (!strncmp(this->name, channel_name, PSEUDO_CHAN_LEN)) {
+ last = this;
+ ast_channel_unlock(this);
+ goto redo;
+ } else if (this == chan) {
+ last = this;
+ ast_channel_unlock(this);
+ goto redo;
+ }
+
+ return setup_chanspy_ds(this, chanspy_ds);
+}
+
+static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
+ int volfactor, const int fd, const char *mygroup, const char *spec,
+ const char *exten, const char *context)
+{
+ char nameprefix[AST_NAME_STRLEN];
+ char peer_name[AST_NAME_STRLEN + 5];
+ signed char zero_volume = 0;
+ int waitms;
+ int res;
+ char *ptr;
+ int num;
+ int num_spyed_upon = 1;
+ struct chanspy_ds chanspy_ds = { 0, };
+
+ ast_mutex_init(&chanspy_ds.lock);
+
+ snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */
+
+ waitms = 100;
+
+ for (;;) {
+ struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
+ struct ast_channel *prev = NULL, *peer = NULL;
+
+ if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
+ res = ast_streamfile(chan, "beep", chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ else if (res < 0) {
+ ast_clear_flag(chan, AST_FLAG_SPYING);
+ break;
+ }
+ }
+
+ res = ast_waitfordigit(chan, waitms);
+ if (res < 0) {
+ ast_clear_flag(chan, AST_FLAG_SPYING);
+ break;
+ }
+
+ /* reset for the next loop around, unless overridden later */
+ waitms = 100;
+ num_spyed_upon = 0;
+
+ for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
+ peer_chanspy_ds;
+ chanspy_ds_free(peer_chanspy_ds), prev = peer,
+ peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
+ next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
+ const char *group;
+ int igrp = !mygroup;
+ char *groups[25];
+ int num_groups = 0;
+ char dup_group[512];
+ int x;
+ char *s;
+
+ peer = peer_chanspy_ds->chan;
+
+ ast_mutex_unlock(&peer_chanspy_ds->lock);
+
+ if (peer == prev) {
+ ast_channel_unlock(peer);
+ chanspy_ds_free(peer_chanspy_ds);
+ break;
+ }
+
+ if (ast_check_hangup(chan)) {
+ ast_channel_unlock(peer);
+ chanspy_ds_free(peer_chanspy_ds);
+ break;
+ }
+
+ if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
+ ast_channel_unlock(peer);
+ continue;
+ }
+
+ if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
+ ast_channel_unlock(peer);
+ continue;
+ }
+
+ if (mygroup) {
+ if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
+ ast_copy_string(dup_group, group, sizeof(dup_group));
+ num_groups = ast_app_separate_args(dup_group, ':', groups,
+ sizeof(groups) / sizeof(groups[0]));
+ }
+
+ for (x = 0; x < num_groups; x++) {
+ if (!strcmp(mygroup, groups[x])) {
+ igrp = 1;
+ break;
+ }
+ }
+ }
+
+ if (!igrp) {
+ ast_channel_unlock(peer);
+ continue;
+ }
+
+ strcpy(peer_name, "spy-");
+ strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
+ ptr = strchr(peer_name, '/');
+ *ptr++ = '\0';
+
+ for (s = peer_name; s < ptr; s++)
+ *s = tolower(*s);
+
+ /* We have to unlock the peer channel here to avoid a deadlock.
+ * So, when we need to dereference it again, we have to lock the
+ * datastore and get the pointer from there to see if the channel
+ * is still valid. */
+ ast_channel_unlock(peer);
+
+ if (!ast_test_flag(flags, OPTION_QUIET)) {
+ if (ast_fileexists(peer_name, NULL, NULL) != -1) {
+ res = ast_streamfile(chan, peer_name, chan->language);
+ if (!res)
+ res = ast_waitstream(chan, "");
+ if (res) {
+ chanspy_ds_free(peer_chanspy_ds);
+ break;
+ }
+ } else
+ res = ast_say_character_str(chan, peer_name, "", chan->language);
+ if ((num = atoi(ptr)))
+ ast_say_digits(chan, atoi(ptr), "", chan->language);
+ }
+
+ res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags);
+ num_spyed_upon++;
+
+ if (res == -1) {
+ chanspy_ds_free(peer_chanspy_ds);
+ break;
+ } else if (res > 1 && spec) {
+ struct ast_channel *next;
+
+ snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
+
+ if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
+ peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
+ next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
+ } else {
+ /* stay on this channel, if it is still valid */
+
+ ast_mutex_lock(&peer_chanspy_ds->lock);
+ if (peer_chanspy_ds->chan) {
+ ast_channel_lock(peer_chanspy_ds->chan);
+ next_chanspy_ds = peer_chanspy_ds;
+ peer_chanspy_ds = NULL;
+ } else {
+ /* the channel is gone */
+ ast_mutex_unlock(&peer_chanspy_ds->lock);
+ next_chanspy_ds = NULL;
+ }
+ }
+
+ peer = NULL;
+ }
+ }
+ if (res == -1 || ast_check_hangup(chan))
+ break;
+ }
+
+ ast_clear_flag(chan, AST_FLAG_SPYING);
+
+ ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
+
+ ast_mutex_lock(&chanspy_ds.lock);
+ ast_mutex_unlock(&chanspy_ds.lock);
+ ast_mutex_destroy(&chanspy_ds.lock);
+
+ return res;
+}
+
+static int chanspy_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_module_user *u;
+ char *options = NULL;
+ char *spec = NULL;
+ char *argv[2];
+ char *mygroup = NULL;
+ char *recbase = NULL;
+ int fd = 0;
+ struct ast_flags flags;
+ int oldwf = 0;
+ int argc = 0;
+ int volfactor = 0;
+ int res;
+
+ data = ast_strdupa(data);
+
+ u = ast_module_user_add(chan);
+
+ if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
+ spec = argv[0];
+ if (argc > 1)
+ options = argv[1];
+
+ if (ast_strlen_zero(spec) || !strcmp(spec, "all"))
+ spec = NULL;
+ }
+
+ if (options) {
+ char *opts[OPT_ARG_ARRAY_SIZE];
+
+ ast_app_parse_options(spy_opts, &flags, opts, options);
+ if (ast_test_flag(&flags, OPTION_GROUP))
+ mygroup = opts[OPT_ARG_GROUP];
+
+ if (ast_test_flag(&flags, OPTION_RECORD) &&
+ !(recbase = opts[OPT_ARG_RECORD]))
+ recbase = "chanspy";
+
+ if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
+ int vol;
+
+ if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
+ ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
+ else
+ volfactor = vol;
+ }
+
+ if (ast_test_flag(&flags, OPTION_PRIVATE))
+ ast_set_flag(&flags, OPTION_WHISPER);
+ } else
+ ast_clear_flag(&flags, AST_FLAGS_ALL);
+
+ oldwf = chan->writeformat;
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
+ ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+ ast_module_user_remove(u);
+ return -1;
+ }
+
+ if (recbase) {
+ char filename[PATH_MAX];
+
+ snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
+ if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
+ ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
+ fd = 0;
+ }
+ }
+
+ res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL);
+
+ if (fd)
+ close(fd);
+
+ if (oldwf && ast_set_write_format(chan, oldwf) < 0)
+ ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+
+ ast_module_user_remove(u);
+
+ return res;
+}
+
+static int extenspy_exec(struct ast_channel *chan, void *data)
+{
+ struct ast_module_user *u;
+ char *options = NULL;
+ char *exten = NULL;
+ char *context = NULL;
+ char *argv[2];
+ char *mygroup = NULL;
+ char *recbase = NULL;
+ int fd = 0;
+ struct ast_flags flags;
+ int oldwf = 0;
+ int argc = 0;
+ int volfactor = 0;
+ int res;
+
+ data = ast_strdupa(data);
+
+ u = ast_module_user_add(chan);
+
+ if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
+ context = argv[0];
+ if (!ast_strlen_zero(argv[0]))
+ exten = strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = ast_strdupa(chan->context);
+ if (argc > 1)
+ options = argv[1];
+ }
+
+ if (options) {
+ char *opts[OPT_ARG_ARRAY_SIZE];
+
+ ast_app_parse_options(spy_opts, &flags, opts, options);
+ if (ast_test_flag(&flags, OPTION_GROUP))
+ mygroup = opts[OPT_ARG_GROUP];
+
+ if (ast_test_flag(&flags, OPTION_RECORD) &&
+ !(recbase = opts[OPT_ARG_RECORD]))
+ recbase = "chanspy";
+
+ if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
+ int vol;
+
+ if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
+ ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
+ else
+ volfactor = vol;
+ }
+
+ if (ast_test_flag(&flags, OPTION_PRIVATE))
+ ast_set_flag(&flags, OPTION_WHISPER);
+ } else
+ ast_clear_flag(&flags, AST_FLAGS_ALL);
+
+ oldwf = chan->writeformat;
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
+ ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+ ast_module_user_remove(u);
+ return -1;
+ }
+
+ if (recbase) {
+ char filename[PATH_MAX];
+
+ snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
+ if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) {
+ ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
+ fd = 0;
+ }
+ }
+
+ res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context);
+
+ if (fd)
+ close(fd);
+
+ if (oldwf && ast_set_write_format(chan, oldwf) < 0)
+ ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
+
+ ast_module_user_remove(u);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_unregister_application(app_chan);
+ res |= ast_unregister_application(app_ext);
+
+ ast_module_user_hangup_all();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
+ res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");
diff --git a/apps/app_controlplayback.c b/apps/app_controlplayback.c
new file mode 100644
index 000000000..6f2c03315
--- /dev/null
+++ b/apps/app_controlplayback.c
@@ -0,0 +1,168 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Trivial application to control playback of a sound file
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/utils.h"
+#include "asterisk/options.h"
+
+static const char *app = "ControlPlayback";
+
+static const char *synopsis = "Play a file with fast forward and rewind";
+
+static const char *descrip =
+" ControlPlayback(file[|skipms[|ff[|rew[|stop[|pause[|restart|options]]]]]]]):\n"
+"This application will play back the given filename. By default, the '*' key\n"
+"can be used to rewind, and the '#' key can be used to fast-forward.\n"
+"Parameters:\n"
+" skipms - This is number of milliseconds to skip when rewinding or\n"
+" fast-forwarding.\n"
+" ff - Fast-forward when this DTMF digit is received.\n"
+" rew - Rewind when this DTMF digit is received.\n"
+" stop - Stop playback when this DTMF digit is received.\n"
+" pause - Pause playback when this DTMF digit is received.\n"
+" restart - Restart playback when this DTMF digit is received.\n"
+"Options:\n"
+" j - Jump to priority n+101 if the requested file is not found.\n"
+"This application sets the following channel variable upon completion:\n"
+" CPLAYBACKSTATUS - This variable contains the status of the attempt as a text\n"
+" string, one of: SUCCESS | USERSTOPPED | ERROR\n";
+
+
+static int is_on_phonepad(char key)
+{
+ return key == 35 || key == 42 || (key >= 48 && key <= 57);
+}
+
+static int controlplayback_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0, priority_jump = 0;
+ int skipms = 0;
+ struct ast_module_user *u;
+ char *tmp;
+ int argc;
+ char *argv[8];
+ enum arg_ids {
+ arg_file = 0,
+ arg_skip = 1,
+ arg_fwd = 2,
+ arg_rev = 3,
+ arg_stop = 4,
+ arg_pause = 5,
+ arg_restart = 6,
+ options = 7,
+ };
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
+ return -1;
+ }
+
+ u = ast_module_user_add(chan);
+
+ tmp = ast_strdupa(data);
+ memset(argv, 0, sizeof(argv));
+
+ argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0]));
+
+ if (argc < 1) {
+ ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n");
+ ast_module_user_remove(u);
+ return -1;
+ }
+
+ skipms = argv[arg_skip] ? atoi(argv[arg_skip]) : 3000;
+ if (!skipms)
+ skipms = 3000;
+
+ if (!argv[arg_fwd] || !is_on_phonepad(*argv[arg_fwd]))
+ argv[arg_fwd] = "#";
+ if (!argv[arg_rev] || !is_on_phonepad(*argv[arg_rev]))
+ argv[arg_rev] = "*";
+ if (argv[arg_stop] && !is_on_phonepad(*argv[arg_stop]))
+ argv[arg_stop] = NULL;
+ if (argv[arg_pause] && !is_on_phonepad(*argv[arg_pause]))
+ argv[arg_pause] = NULL;
+ if (argv[arg_restart] && !is_on_phonepad(*argv[arg_restart]))
+ argv[arg_restart] = NULL;
+
+ if (argv[options]) {
+ if (strchr(argv[options], 'j'))
+ priority_jump = 1;
+ }
+
+ res = ast_control_streamfile(chan, argv[arg_file], argv[arg_fwd], argv[arg_rev], argv[arg_stop], argv[arg_pause], argv[arg_restart], skipms);
+
+ /* If we stopped on one of our stop keys, return 0 */
+ if (res > 0 && argv[arg_stop] && strchr(argv[arg_stop], res)) {
+ res = 0;
+ pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "USERSTOPPED");
+ } else {
+ if (res < 0) {
+ if (priority_jump || ast_opt_priority_jumping) {
+ if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
+ ast_log(LOG_WARNING, "ControlPlayback tried to jump to priority n+101 as requested, but priority didn't exist\n");
+ }
+ }
+ res = 0;
+ pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "ERROR");
+ } else
+ pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "SUCCESS");
+ }
+
+ ast_module_user_remove(u);
+
+ return res;
+}
+
+static int unload_module(void)
+{
+ int res;
+ res = ast_unregister_application(app);
+ return res;
+}
+
+static int load_module(void)
+{
+ return ast_register_application(app, controlplayback_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Control Playback Application");
diff --git a/apps/app_dahdibarge.c b/apps/app_dahdibarge.c
new file mode 100644
index 000000000..cba85a9b6
--- /dev/null
+++ b/apps/app_dahdibarge.c
@@ -0,0 +1,358 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Special thanks to comphealth.com for sponsoring this
+ * GPL application.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Zap Barge support
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note Special thanks to comphealth.com for sponsoring this
+ * GPL application.
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>dahdi</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/app.h"
+#include "asterisk/options.h"
+#include "asterisk/cli.h"
+#include "asterisk/say.h"
+#include "asterisk/utils.h"
+
+#include "asterisk/dahdi_compat.h"
+
+static char *dahdi_app = "DAHDIBarge";
+static char *zap_app = "ZapBarge";
+
+static char *dahdi_synopsis = "Barge in (monitor) DAHDI channel";
+static char *zap_synopsis = "Barge in (monitor) Zap channel";
+
+static char *dahdi_descrip =
+" DAHDIBarge([channel]): Barges in on a specified DAHDI\n"
+"channel or prompts if one is not specified. Returns\n"
+"-1 when caller user hangs up and is independent of the\n"
+"state of the channel being monitored.";
+
+static char *zap_descrip =
+" ZapBarge([channel]): Barges in on a specified Zaptel\n"
+"channel or prompts if one is not specified. Returns\n"
+"-1 when caller user hangs up and is independent of the\n"
+"state of the channel being monitored.";
+
+#define CONF_SIZE 160
+
+static int careful_write(int fd, unsigned char *data, int len)
+{
+ int res;
+ while(len) {
+ res = write(fd, data, len);
+ if (res < 1) {
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
+ return -1;
+ } else
+ return 0;
+ }
+ len -= res;
+ data += res;
+ }
+ return 0;
+}
+
+static int conf_run(struct ast_channel *chan, int confno, int confflags)
+{
+ int fd;
+ struct dahdi_confinfo ztc;
+ struct ast_frame *f;
+ struct ast_channel *c;
+ struct ast_frame fr;
+ int outfd;
+ int ms;
+ int nfds;
+ int res;
+ int flags;
+ int retryzap;
+ int origfd;
+ int ret = -1;
+ struct dahdi_bufferinfo bi;
+ char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
+ char *buf = __buf + AST_FRIENDLY_OFFSET;
+
+ /* Set it into U-law mode (write) */
+ if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
+ goto outrun;
+ }
+
+ /* Set it into U-law mode (read) */
+ if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
+ goto outrun;
+ }
+ ast_indicate(chan, -1);
+ retryzap = strcasecmp(chan->tech->type, dahdi_chan_name);
+zapretry:
+ origfd = chan->fds[0];
+ if (retryzap) {
+ fd = open(DAHDI_FILE_PSEUDO, O_RDWR);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
+ goto outrun;
+ }
+ /* Make non-blocking */
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
+ ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
+ ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ /* Setup buffering information */
+ memset(&bi, 0, sizeof(bi));
+ bi.bufsize = CONF_SIZE;
+ bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
+ bi.numbufs = 4;
+ if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
+ ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ nfds = 1;
+ } else {
+ /* XXX Make sure we're not running on a pseudo channel XXX */
+ fd = chan->fds[0];
+ nfds = 0;
+ }
+ memset(&ztc, 0, sizeof(ztc));
+ /* Check to see if we're in a conference... */
+ ztc.chan = 0;
+ if (ioctl(fd, DAHDI_GETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error getting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ if (ztc.confmode) {
+ /* Whoa, already in a conference... Retry... */
+ if (!retryzap) {
+ ast_log(LOG_DEBUG, "Channel is in a conference already, retrying with pseudo\n");
+ retryzap = 1;
+ goto zapretry;
+ }
+ }
+ memset(&ztc, 0, sizeof(ztc));
+ /* Add us to the conference */
+ ztc.chan = 0;
+ ztc.confno = confno;
+ ztc.confmode = DAHDI_CONF_MONITORBOTH;
+
+ if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ ast_log(LOG_DEBUG, "Placed channel %s in channel %d monitor\n", chan->name, confno);
+
+ for(;;) {
+ outfd = -1;
+ ms = -1;
+ c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
+ if (c) {
+ if (c->fds[0] != origfd) {
+ if (retryzap) {
+ /* Kill old pseudo */
+ close(fd);
+ }
+ ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
+ retryzap = 0;
+ goto zapretry;
+ }
+ f = ast_read(c);
+ if (!f)
+ break;
+ if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) {
+ ret = 0;
+ ast_frfree(f);
+ break;
+ } else if (fd != chan->fds[0]) {
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (f->subclass == AST_FORMAT_ULAW) {
+ /* Carefully write */
+ careful_write(fd, f->data, f->datalen);
+ } else
+ ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass);
+ }
+ }
+ ast_frfree(f);
+ } else if (outfd > -1) {
+ res = read(outfd, buf, CONF_SIZE);
+ if (res > 0) {
+ memset(&fr, 0, sizeof(fr));
+ fr.frametype = AST_FRAME_VOICE;
+ fr.subclass = AST_FORMAT_ULAW;
+ fr.datalen = res;
+ fr.samples = res;
+ fr.data = buf;
+ fr.offset = AST_FRIENDLY_OFFSET;
+ if (ast_write(chan, &fr) < 0) {
+ ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
+ /* break; */
+ }
+ } else
+ ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
+ }
+ }
+ if (fd != chan->fds[0])
+ close(fd);
+ else {
+ /* Take out of conference */
+ /* Add us to the conference */
+ ztc.chan = 0;
+ ztc.confno = 0;
+ ztc.confmode = 0;
+ if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ }
+ }
+
+outrun:
+
+ return ret;
+}
+
+static int exec(struct ast_channel *chan, void *data, int dahdimode)
+{
+ int res=-1;
+ struct ast_module_user *u;
+ int retrycnt = 0;
+ int confflags = 0;
+ int confno = 0;
+ char confstr[80] = "";
+
+ u = ast_module_user_add(chan);
+
+ if (!ast_strlen_zero(data)) {
+ if (dahdimode) {
+ if ((sscanf(data, "DAHDI/%d", &confno) != 1) &&
+ (sscanf(data, "%d", &confno) != 1)) {
+ ast_log(LOG_WARNING, "Argument (if specified) must be a channel number, not '%s'\n", (char *) data);
+ ast_module_user_remove(u);
+ return 0;
+ }
+ } else {
+ if ((sscanf(data, "Zap/%d", &confno) != 1) &&
+ (sscanf(data, "%d", &confno) != 1)) {
+ ast_log(LOG_WARNING, "Argument (if specified) must be a channel number, not '%s'\n", (char *) data);
+ ast_module_user_remove(u);
+ return 0;
+ }
+ }
+ }
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ while(!confno && (++retrycnt < 4)) {
+ /* Prompt user for conference number */
+ confstr[0] = '\0';
+ res = ast_app_getdata(chan, "conf-getchannel",confstr, sizeof(confstr) - 1, 0);
+ if (res <0) goto out;
+ if (sscanf(confstr, "%d", &confno) != 1)
+ confno = 0;
+ }
+ if (confno) {
+ /* XXX Should prompt user for pin if pin is required XXX */
+ /* Run the conference */
+ res = conf_run(chan, confno, confflags);
+ }
+out:
+ /* Do the conference */
+ ast_module_user_remove(u);
+ return res;
+}
+
+static int exec_zap(struct ast_channel *chan, void *data)
+{
+ ast_log(LOG_WARNING, "Use of the command %s is deprecated, please use %s instead.\n", zap_app, dahdi_app);
+
+ return exec(chan, data, 0);
+}
+
+static int exec_dahdi(struct ast_channel *chan, void *data)
+{
+ return exec(chan, data, 1);
+}
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) {
+ res |= ast_unregister_application(dahdi_app);
+ }
+
+ res |= ast_unregister_application(zap_app);
+
+ ast_module_user_hangup_all();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) {
+ res |= ast_register_application(dahdi_app, exec_dahdi, dahdi_synopsis, dahdi_descrip);
+ }
+
+ res |= ast_register_application(zap_app, exec_zap, zap_synopsis, zap_descrip);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Barge in on channel application");
diff --git a/apps/app_dahdiras.c b/apps/app_dahdiras.c
new file mode 100644
index 000000000..4ac5daa3d
--- /dev/null
+++ b/apps/app_dahdiras.c
@@ -0,0 +1,288 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Execute an ISDN RAS
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>dahdi</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#ifdef __linux__
+#include <sys/signal.h>
+#else
+#include <signal.h>
+#endif /* __linux__ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+
+#include "asterisk/dahdi_compat.h"
+
+static char *dahdi_app = "DAHDIRAS";
+static char *zap_app = "ZapRAS";
+
+static char *dahdi_synopsis = "Executes DAHDI ISDN RAS application";
+static char *zap_synopsis = "Executes Zaptel ISDN RAS application";
+
+static char *dahdi_descrip =
+" DAHDIRAS(args): Executes a RAS server using pppd on the given channel.\n"
+"The channel must be a clear channel (i.e. PRI source) and a DAHDI\n"
+"channel to be able to use this function (no modem emulation is included).\n"
+"Your pppd must have the DAHDI plugin available. Arguments should be\n"
+"separated by | characters.\n";
+
+static char *zap_descrip =
+" ZapRAS(args): Executes a RAS server using pppd on the given channel.\n"
+"The channel must be a clear channel (i.e. PRI source) and a Zaptel\n"
+"channel to be able to use this function (no modem emulation is included).\n"
+"Your pppd must have the Zaptel plugin available. Arguments should be\n"
+"separated by | characters.\n";
+
+#define PPP_MAX_ARGS 32
+#define PPP_EXEC "/usr/sbin/pppd"
+
+static pid_t spawn_ras(struct ast_channel *chan, char *args)
+{
+ pid_t pid;
+ int x;
+ char *c;
+
+ char *argv[PPP_MAX_ARGS];
+ int argc = 0;
+ char *stringp=NULL;
+ sigset_t fullset, oldset;
+
+ sigfillset(&fullset);
+ pthread_sigmask(SIG_BLOCK, &fullset, &oldset);
+
+ /* Start by forking */
+ pid = fork();
+ if (pid) {
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+ return pid;
+ }
+
+ /* Restore original signal handlers */
+ for (x=0;x<NSIG;x++)
+ signal(x, SIG_DFL);
+
+ pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);
+
+ /* Execute RAS on File handles */
+ dup2(chan->fds[0], STDIN_FILENO);
+
+ /* Drop high priority */
+ if (ast_opt_high_priority)
+ ast_set_priority(0);
+
+ /* Close other file descriptors */
+ for (x=STDERR_FILENO + 1;x<1024;x++)
+ close(x);
+
+ /* Reset all arguments */
+ memset(argv, 0, sizeof(argv));
+
+ /* First argument is executable, followed by standard
+ arguments for DAHDI PPP */
+ argv[argc++] = PPP_EXEC;
+ argv[argc++] = "nodetach";
+
+ /* And all the other arguments */
+ stringp=args;
+ c = strsep(&stringp, "|");
+ while(c && strlen(c) && (argc < (PPP_MAX_ARGS - 4))) {
+ argv[argc++] = c;
+ c = strsep(&stringp, "|");
+ }
+
+ argv[argc++] = "plugin";
+#ifdef HAVE_ZAPTEL
+ argv[argc++] = "zaptel.so";
+#else
+ argv[argc++] = "dahdi.so";
+#endif
+ argv[argc++] = "stdin";
+
+ /* Finally launch PPP */
+ execv(PPP_EXEC, argv);
+ fprintf(stderr, "Failed to exec PPPD!\n");
+ exit(1);
+}
+
+static void run_ras(struct ast_channel *chan, char *args)
+{
+ pid_t pid;
+ int status;
+ int res;
+ int signalled = 0;
+ struct dahdi_bufferinfo savebi;
+ int x;
+
+ res = ioctl(chan->fds[0], DAHDI_GET_BUFINFO, &savebi);
+ if(res) {
+ ast_log(LOG_WARNING, "Unable to check buffer policy on channel %s\n", chan->name);
+ return;
+ }
+
+ pid = spawn_ras(chan, args);
+ if (pid < 0) {
+ ast_log(LOG_WARNING, "Failed to spawn RAS\n");
+ } else {
+ for (;;) {
+ res = wait4(pid, &status, WNOHANG, NULL);
+ if (!res) {
+ /* Check for hangup */
+ if (chan->_softhangup && !signalled) {
+ ast_log(LOG_DEBUG, "Channel '%s' hungup. Signalling RAS at %d to die...\n", chan->name, pid);
+ kill(pid, SIGTERM);
+ signalled=1;
+ }
+ /* Try again */
+ sleep(1);
+ continue;
+ }
+ if (res < 0) {
+ ast_log(LOG_WARNING, "wait4 returned %d: %s\n", res, strerror(errno));
+ }
+ if (option_verbose > 2) {
+ if (WIFEXITED(status)) {
+ ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated with status %d\n", chan->name, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated with signal %d\n",
+ chan->name, WTERMSIG(status));
+ } else {
+ ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated weirdly.\n", chan->name);
+ }
+ }
+ /* Throw back into audio mode */
+ x = 1;
+ ioctl(chan->fds[0], DAHDI_AUDIOMODE, &x);
+
+ /* Restore saved values */
+ res = ioctl(chan->fds[0], DAHDI_SET_BUFINFO, &savebi);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set buffer policy on channel %s\n", chan->name);
+ }
+ break;
+ }
+ }
+}
+
+static int exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ char *args;
+ struct ast_module_user *u;
+ struct dahdi_params ztp;
+
+ if (!data)
+ data = "";
+
+ u = ast_module_user_add(chan);
+
+ args = ast_strdupa(data);
+
+ /* Answer the channel if it's not up */
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+ if (strcasecmp(chan->tech->type, dahdi_chan_name)) {
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Channel %s is not a %s channel\n", chan->name, dahdi_chan_name);
+ sleep(2);
+ } else {
+ memset(&ztp, 0, sizeof(ztp));
+ if (ioctl(chan->fds[0], DAHDI_GET_PARAMS, &ztp)) {
+ ast_log(LOG_WARNING, "Unable to get parameters\n");
+ } else if (ztp.sigtype != DAHDI_SIG_CLEAR) {
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Channel %s is not a clear channel\n", chan->name);
+ } else {
+ /* Everything should be okay. Run PPP. */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Starting RAS on %s\n", chan->name);
+ /* Execute RAS */
+ run_ras(chan, args);
+ }
+ }
+ ast_module_user_remove(u);
+ return res;
+}
+
+static int exec_warn(struct ast_channel *chan, void *data)
+{
+ ast_log(LOG_WARNING, "Use of the command %s is deprecated, please use %s instead.\n", zap_app, dahdi_app);
+
+ return exec(chan, data);
+}
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) {
+ res |= ast_unregister_application(dahdi_app);
+ }
+
+ res |= ast_unregister_application(zap_app);
+
+ ast_module_user_hangup_all();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) {
+ res |= ast_register_application(dahdi_app, exec, dahdi_synopsis, dahdi_descrip);
+ }
+
+ res |= ast_register_application(zap_app, exec_warn, zap_synopsis, zap_descrip);
+
+ return res;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DAHDI RAS Application");
+
diff --git a/apps/app_dahdiscan.c b/apps/app_dahdiscan.c
new file mode 100644
index 000000000..6a600756e
--- /dev/null
+++ b/apps/app_dahdiscan.c
@@ -0,0 +1,389 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Modified from app_zapbarge by David Troy <dave@toad.net>
+ *
+ * Special thanks to comphealth.com for sponsoring this
+ * GPL application.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Zap Scanner
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>dahdi</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/app.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+#include "asterisk/say.h"
+
+#include "asterisk/dahdi_compat.h"
+
+static char *app = "DAHDIScan";
+static char *deprecated_app = "ZapScan";
+
+static char *synopsis = "Scan Zap channels to monitor calls";
+
+static char *descrip =
+" ZapScan([group]) allows a call center manager to monitor Zap channels in\n"
+"a convenient way. Use '#' to select the next channel and use '*' to exit\n"
+"Limit scanning to a channel GROUP by setting the option group argument.\n";
+
+
+#define CONF_SIZE 160
+
+static struct ast_channel *get_zap_channel_locked(int num) {
+ char name[80];
+
+ snprintf(name,sizeof(name),"%s/%d-1", dahdi_chan_name, num);
+ return ast_get_channel_by_name_locked(name);
+}
+
+static int careful_write(int fd, unsigned char *data, int len)
+{
+ int res;
+ while(len) {
+ res = write(fd, data, len);
+ if (res < 1) {
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
+ return -1;
+ } else
+ return 0;
+ }
+ len -= res;
+ data += res;
+ }
+ return 0;
+}
+
+static int conf_run(struct ast_channel *chan, int confno, int confflags)
+{
+ int fd;
+ struct dahdi_confinfo ztc;
+ struct ast_frame *f;
+ struct ast_channel *c;
+ struct ast_frame fr;
+ int outfd;
+ int ms;
+ int nfds;
+ int res;
+ int flags;
+ int retryzap;
+ int origfd;
+ int ret = -1;
+ char input[4];
+ int ic=0;
+ struct dahdi_bufferinfo bi;
+ char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
+ char *buf = __buf + AST_FRIENDLY_OFFSET;
+
+ /* Set it into U-law mode (write) */
+ if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
+ goto outrun;
+ }
+
+ /* Set it into U-law mode (read) */
+ if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
+ goto outrun;
+ }
+ ast_indicate(chan, -1);
+ retryzap = strcasecmp(chan->tech->type, "Zap");
+ zapretry:
+ origfd = chan->fds[0];
+ if (retryzap) {
+ fd = open(DAHDI_FILE_PSEUDO, O_RDWR);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
+ goto outrun;
+ }
+ /* Make non-blocking */
+ flags = fcntl(fd, F_GETFL);
+ if (flags < 0) {
+ ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
+ ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ /* Setup buffering information */
+ memset(&bi, 0, sizeof(bi));
+ bi.bufsize = CONF_SIZE;
+ bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
+ bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
+ bi.numbufs = 4;
+ if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
+ ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
+ close(fd);
+ goto outrun;
+ }
+ nfds = 1;
+ } else {
+ /* XXX Make sure we're not running on a pseudo channel XXX */
+ fd = chan->fds[0];
+ nfds = 0;
+ }
+ memset(&ztc, 0, sizeof(ztc));
+ /* Check to see if we're in a conference... */
+ ztc.chan = 0;
+ if (ioctl(fd, DAHDI_GETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error getting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ if (ztc.confmode) {
+ /* Whoa, already in a conference... Retry... */
+ if (!retryzap) {
+ ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
+ retryzap = 1;
+ goto zapretry;
+ }
+ }
+ memset(&ztc, 0, sizeof(ztc));
+ /* Add us to the conference */
+ ztc.chan = 0;
+ ztc.confno = confno;
+ ztc.confmode = DAHDI_CONF_MONITORBOTH;
+
+ if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ close(fd);
+ goto outrun;
+ }
+ ast_log(LOG_DEBUG, "Placed channel %s in ZAP channel %d monitor\n", chan->name, confno);
+
+ for(;;) {
+ outfd = -1;
+ ms = -1;
+ c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
+ if (c) {
+ if (c->fds[0] != origfd) {
+ if (retryzap) {
+ /* Kill old pseudo */
+ close(fd);
+ }
+ ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
+ retryzap = 0;
+ goto zapretry;
+ }
+ f = ast_read(c);
+ if (!f)
+ break;
+ if(f->frametype == AST_FRAME_DTMF) {
+ if(f->subclass == '#') {
+ ret = 0;
+ break;
+ }
+ else if (f->subclass == '*') {
+ ret = -1;
+ break;
+
+ }
+ else {
+ input[ic++] = f->subclass;
+ }
+ if(ic == 3) {
+ input[ic++] = '\0';
+ ic=0;
+ ret = atoi(input);
+ ast_verbose(VERBOSE_PREFIX_3 "Zapscan: change channel to %d\n",ret);
+ break;
+ }
+ }
+
+ if (fd != chan->fds[0]) {
+ if (f->frametype == AST_FRAME_VOICE) {
+ if (f->subclass == AST_FORMAT_ULAW) {
+ /* Carefully write */
+ careful_write(fd, f->data, f->datalen);
+ } else
+ ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass);
+ }
+ }
+ ast_frfree(f);
+ } else if (outfd > -1) {
+ res = read(outfd, buf, CONF_SIZE);
+ if (res > 0) {
+ memset(&fr, 0, sizeof(fr));
+ fr.frametype = AST_FRAME_VOICE;
+ fr.subclass = AST_FORMAT_ULAW;
+ fr.datalen = res;
+ fr.samples = res;
+ fr.data = buf;
+ fr.offset = AST_FRIENDLY_OFFSET;
+ if (ast_write(chan, &fr) < 0) {
+ ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
+ /* break; */
+ }
+ } else
+ ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
+ }
+ }
+ if (f)
+ ast_frfree(f);
+ if (fd != chan->fds[0])
+ close(fd);
+ else {
+ /* Take out of conference */
+ /* Add us to the conference */
+ ztc.chan = 0;
+ ztc.confno = 0;
+ ztc.confmode = 0;
+ if (ioctl(fd, DAHDI_SETCONF, &ztc)) {
+ ast_log(LOG_WARNING, "Error setting conference\n");
+ }
+ }
+
+ outrun:
+
+ return ret;
+}
+
+static int conf_exec(struct ast_channel *chan, void *data)
+{
+ int res=-1;
+ struct ast_module_user *u;
+ int confflags = 0;
+ int confno = 0;
+ char confstr[80] = "", *tmp = NULL;
+ struct ast_channel *tempchan = NULL, *lastchan = NULL,*ichan = NULL;
+ struct ast_frame *f;
+ char *desired_group;
+ int input=0,search_group=0;
+
+ u = ast_module_user_add(chan);
+
+ if (chan->_state != AST_STATE_UP)
+ ast_answer(chan);
+
+ desired_group = ast_strdupa(data);
+ if(!ast_strlen_zero(desired_group)) {
+ ast_verbose(VERBOSE_PREFIX_3 "Scanning for group %s\n", desired_group);
+ search_group = 1;
+ }
+
+ for (;;) {
+ if (ast_waitfor(chan, 100) < 0)
+ break;
+
+ f = ast_read(chan);
+ if (!f)
+ break;
+ if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) {
+ ast_frfree(f);
+ break;
+ }
+ ast_frfree(f);
+ ichan = NULL;
+ if(input) {
+ ichan = get_zap_channel_locked(input);
+ input = 0;
+ }
+
+ tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan);
+
+ if ( !tempchan && !lastchan )
+ break;
+
+ if (tempchan && search_group) {
+ const char *mygroup;
+ if((mygroup = pbx_builtin_getvar_helper(tempchan, "GROUP")) && (!strcmp(mygroup, desired_group))) {
+ ast_verbose(VERBOSE_PREFIX_3 "Found Matching Channel %s in group %s\n", tempchan->name, desired_group);
+ } else {
+ ast_mutex_unlock(&tempchan->lock);
+ lastchan = tempchan;
+ continue;
+ }
+ }
+ if (tempchan && (!strcmp(tempchan->tech->type, "Zap")) && (tempchan != chan) ) {
+ ast_verbose(VERBOSE_PREFIX_3 "Zap channel %s is in-use, monitoring...\n", tempchan->name);
+ ast_copy_string(confstr, tempchan->name, sizeof(confstr));
+ ast_mutex_unlock(&tempchan->lock);
+ if ((tmp = strchr(confstr,'-'))) {
+ *tmp = '\0';
+ }
+ confno = atoi(strchr(confstr,'/') + 1);
+ ast_stopstream(chan);
+ ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL);
+ res = conf_run(chan, confno, confflags);
+ if (res<0) break;
+ input = res;
+ } else if (tempchan)
+ ast_mutex_unlock(&tempchan->lock);
+ lastchan = tempchan;
+ }
+ ast_module_user_remove(u);
+ return res;
+}
+
+static int conf_exec_warn(struct ast_channel *chan, void *data)
+{
+ ast_log(LOG_WARNING, "Use of the command %s is deprecated, please use %s instead.\n", deprecated_app, app);
+ return conf_exec(chan, data);
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_application(app);
+
+ ast_module_user_hangup_all();
+
+ return res;
+}
+
+static int load_module(void)
+{
+ ast_register_application(deprecated_app, conf_exec_warn, synopsis, descrip);
+ return ast_register_application(app, conf_exec, synopsis, descrip);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Scan Zap channels application");
+
diff --git a/apps/app_db.c b/apps/app_db.c
new file mode 100644
index 000000000..0c8d0585b
--- /dev/null
+++ b/apps/app_db.c
@@ -0,0 +1,167 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ * Copyright (C) 2003, Jefferson Noxon
+ *
+ * Mark Spencer <markster@digium.com>
+ * Jefferson Noxon <jeff@debian.org>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Database access functions
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Jefferson Noxon <jeff@debian.org>
+ *
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "asterisk/options.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/astdb.h"
+#include "asterisk/lock.h"
+#include "asterisk/options.h"
+
+/*! \todo XXX Remove this application after 1.4 is relased */
+static char *d_descrip =
+" DBdel(family/key): This application will delete a key from the Asterisk\n"
+"database.\n"
+" This application has been DEPRECATED in favor of the DB_DELETE function.\n";
+
+static char *dt_descrip =
+" DBdeltree(family[/keytree]): This application will delete a family or keytree\n"
+"from the Asterisk database\n";
+
+static char *d_app = "DBdel";
+static char *dt_app = "DBdeltree";
+
+static char *d_synopsis = "Delete a key from the database";
+static char *dt_synopsis = "Delete a family or keytree from the database";
+
+
+static int deltree_exec(struct ast_channel *chan, void *data)
+{
+ char *argv, *family, *keytree;
+ struct ast_module_user *u;
+
+ u = ast_module_user_add(chan);
+
+ argv = ast_strdupa(data);
+
+ if (strchr(argv, '/')) {
+ family = strsep(&argv, "/");
+ keytree = strsep(&argv, "\0");
+ if (!family || !keytree) {
+ ast_log(LOG_DEBUG, "Ignoring; Syntax error in argument\n");
+ ast_module_user_remove(u);
+ return 0;
+ }
+ if (ast_strlen_zero(keytree))
+ keytree = 0;
+ } else {
+ family = argv;
+ keytree = 0;
+ }
+
+ if (option_verbose > 2) {
+ if (keytree)
+ ast_verbose(VERBOSE_PREFIX_3 "DBdeltree: family=%s, keytree=%s\n", family, keytree);
+ else
+ ast_verbose(VERBOSE_PREFIX_3 "DBdeltree: family=%s\n", family);
+ }
+
+ if (ast_db_deltree(family, keytree)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "DBdeltree: Error deleting key from database.\n");
+ }
+
+ ast_module_user_remove(u);
+
+ return 0;
+}
+
+static int del_exec(struct ast_channel *chan, void *data)
+{
+ char *argv, *family, *key;
+ struct ast_module_user *u;
+ static int deprecation_warning = 0;
+
+ u = ast_module_user_add(chan);
+
+ if (!deprecation_warning) {
+ deprecation_warning = 1;
+ ast_log(LOG_WARNING, "The DBdel application has been deprecated in favor of the DB_DELETE dialplan function!\n");
+ }
+
+ argv = ast_strdupa(data);
+
+ if (strchr(argv, '/')) {
+ family = strsep(&argv, "/");
+ key = strsep(&argv, "\0");
+ if (!family || !key) {
+ ast_log(LOG_DEBUG, "Ignoring; Syntax error in argument\n");
+ ast_module_user_remove(u);
+ return 0;
+ }
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "DBdel: family=%s, key=%s\n", family, key);
+ if (ast_db_del(family, key)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "DBdel: Error deleting key from database.\n");
+ }
+ } else {
+ ast_log(LOG_DEBUG, "Ignoring, no parameters\n");
+ }
+
+ ast_module_user_remove(u);
+
+ return 0;
+}
+
+static int unload_module(void)
+{
+ int retval;
+
+ retval = ast_unregister_application(dt_app);
+ retval |= ast_unregister_application(d_app);
+
+ return retval;
+}
+
+static int load_module(void)
+{
+ int retval;
+
+ retval = ast_register_application(d_app, del_exec, d_synopsis, d_descrip);
+ retval |= ast_register_application(dt_app, deltree_exec, dt_synopsis, dt_descrip);
+
+ return retval;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Database Access Functions");
diff --git a/apps/app_dial.c b/apps/app_dial.c
new file mode 100644
index 000000000..303b36121
--- /dev/null
+++ b/apps/app_dial.c
@@ -0,0 +1,2015 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief dial() & retrydial() - Trivial application to dial a channel and send an URL on answer
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \ingroup applications
+ */
+
+/*** MODULEINFO
+ <depend>chan_local</depend>
+ ***/
+
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/module.h"
+#include "asterisk/translate.h"
+#include "asterisk/say.h"
+#include "asterisk/config.h"
+#include "asterisk/features.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/callerid.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/causes.h"
+#include "asterisk/rtp.h"
+#include "asterisk/cdr.h"
+#include "asterisk/manager.h"
+#include "asterisk/privacy.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/global_datastores.h"
+
+static char *app = "Dial";
+
+static char *synopsis = "Place a call and connect to the current channel";
+
+static char *descrip =
+" Dial(Technology/resource[&Tech2/resource2...][|timeout][|options][|URL]):\n"
+"This application will place calls to one or more specified channels. As soon\n"
+"as one of the requested channels answers, the originating channel will be\n"
+"answered, if it has not already been answered. These two channels will then\n"
+"be active in a bridged call. All other channels that were requested will then\n"
+"be hung up.\n"
+" Unless there is a timeout specified, the Dial application will wait\n"
+"indefinitely until one of the called channels answers, the user hangs up, or\n"
+"if all of the called channels are busy or unavailable. Dialplan executing will\n"
+"continue if no requested channels can be called, or if the timeout expires.\n\n"
+" This application sets the following channel variables upon completion:\n"
+" DIALEDTIME - This is the time from dialing a channel until when it\n"
+" is disconnected.\n"
+" ANSWEREDTIME - This is the amount of time for actual call.\n"
+" DIALSTATUS - This is the status of the call:\n"
+" CHANUNAVAIL | CONGESTION | NOANSWER | BUSY | ANSWER | CANCEL\n"
+" DONTCALL | TORTURE | INVALIDARGS\n"
+" For the Privacy and Screening Modes, the DIALSTATUS variable will be set to\n"
+"DONTCALL if the called party chooses to send the calling party to the 'Go Away'\n"
+"script. The DIALSTATUS variable will be set to TORTURE if the called party\n"
+"wants to send the caller to the 'torture' script.\n"
+" This application will report normal termination if the originating channel\n"
+"hangs up, or if the call is bridged and either of the parties in the bridge\n"
+"ends the call.\n"
+" The optional URL will be sent to the called party if the channel supports it.\n"
+" If the OUTBOUND_GROUP variable is set, all peer channels created by this\n"
+"application will be put into that group (as in Set(GROUP()=...).\n"
+" If the OUTBOUND_GROUP_ONCE variable is set, all peer channels created by this\n"
+"application will be put into that group (as in Set(GROUP()=...). Unlike OUTBOUND_GROUP,\n"
+"however, the variable will be unset after use.\n\n"
+" Options:\n"
+" A(x) - Play an announcement to the called party, using 'x' as the file.\n"
+" C - Reset the CDR for this call.\n"
+" d - Allow the calling user to dial a 1 digit extension while waiting for\n"
+" a call to be answered. Exit to that extension if it exists in the\n"
+" current context, or the context defined in the EXITCONTEXT variable,\n"
+" if it exists.\n"
+" D([called][:calling]) - Send the specified DTMF strings *after* the called\n"
+" party has answered, but before the call gets bridged. The 'called'\n"
+" DTMF string is sent to the called party, and the 'calling' DTMF\n"
+" string is sent to the calling party. Both parameters can be used\n"
+" alone.\n"
+" f - Force the callerid of the *calling* channel to be set as the\n"
+" extension associated with the channel using a dialplan 'hint'.\n"
+" For example, some PSTNs do not allow CallerID to be set to anything\n"
+" other than the number assigned to the caller.\n"
+" g - Proceed with dialplan execution at the current extension if the\n"
+" destination channel hangs up.\n"
+" G(context^exten^pri) - If the call is answered, transfer the calling party to\n"
+" the specified priority and the called party to the specified priority+1.\n"
+" Optionally, an extension, or extension and context may be specified. \n"
+" Otherwise, the current extension is used. You cannot use any additional\n"
+" action post answer options in conjunction with this option.\n"
+" h - Allow the called party to hang up by sending the '*' DTMF digit, or\n"
+" whatever sequence was defined in the featuremap section for\n"
+" 'disconnect' in features.conf\n"
+" H - Allow the calling party to hang up by hitting the '*' DTMF digit, or\n"
+" whatever sequence was defined in the featuremap section for\n"
+" 'disconnect' in features.conf\n"
+" i - Asterisk will ignore any forwarding requests it may receive on this\n"
+" dial attempt.\n"
+" j - Jump to priority n+101 if all of the requested channels were busy.\n"
+" k - Allow the called party to enable parking of the call by sending\n"
+" the DTMF sequence defined for call parking in the featuremap section of features.conf.\n"
+" K - Allow the calling party to enable parking of the call by sending\n"
+" the DTMF sequence defined for call parking in the featuremap section of features.conf.\n"
+" L(x[:y][:z]) - Limit the call to 'x' ms. Play a warning when 'y' ms are\n"
+" left. Repeat the warning every 'z' ms. The following special\n"
+" variables can be used with this option:\n"
+" * LIMIT_PLAYAUDIO_CALLER yes|no (default yes)\n"
+" Play sounds to the caller.\n"
+" * LIMIT_PLAYAUDIO_CALLEE yes|no\n"
+" Play sounds to the callee.\n"
+" * LIMIT_TIMEOUT_FILE File to play when time is up.\n"
+" * LIMIT_CONNECT_FILE File to play when call begins.\n"
+" * LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n"
+" The default is to say the time remaining.\n"
+" m([class]) - Provide hold music to the calling party until a requested\n"
+" channel answers. A specific MusicOnHold class can be\n"
+" specified.\n"
+" M(x[^arg]) - Execute the Macro for the *called* channel before connecting\n"
+" to the calling channel. Arguments can be specified to the Macro\n"
+" using '^' as a delimeter. The Macro can set the variable\n"
+" MACRO_RESULT to specify the following actions after the Macro is\n"
+" finished executing.\n"
+" * ABORT Hangup both legs of the call.\n"
+" * CONGESTION Behave as if line congestion was encountered.\n"
+" * BUSY Behave as if a busy signal was encountered. This will also\n"
+" have the application jump to priority n+101 if the\n"
+" 'j' option is set.\n"
+" * CONTINUE Hangup the called party and allow the calling party\n"
+" to continue dialplan execution at the next priority.\n"
+" * GOTO:<context>^<exten>^<priority> - Transfer the call to the\n"
+" specified priority. Optionally, an extension, or\n"
+" extension and priority can be specified.\n"
+" You cannot use any additional action post answer options in conjunction\n"
+" with this option. Also, pbx services are not run on the peer (called) channel,\n"
+" so you will not be able to set timeouts via the TIMEOUT() function in this macro.\n"
+" n - This option is a modifier for the screen/privacy mode. It specifies\n"
+" that no introductions are to be saved in the priv-callerintros\n"
+" directory.\n"
+" N - This option is a modifier for the screen/privacy mode. It specifies\n"
+" that if callerID is present, do not screen the call.\n"
+" o - Specify that the CallerID that was present on the *calling* channel\n"
+" be set as the CallerID on the *called* channel. This was the\n"
+" behavior of Asterisk 1.0 and earlier.\n"
+" O([x]) - \"Operator Services\" mode (Zaptel channel to Zaptel channel\n"
+" only, if specified on non-Zaptel interface, it will be ignored).\n"
+" When the destination answers (presumably an operator services\n"
+" station), the originator no longer has control of their line.\n"
+" They may hang up, but the switch will not release their line\n"
+" until the destination party hangs up (the operator). Specified\n"
+" without an arg, or with 1 as an arg, the originator hanging up\n"
+" will cause the phone to ring back immediately. With a 2 specified,\n"
+" when the \"operator\" flashes the trunk, it will ring their phone\n"
+" back.\n"
+" p - This option enables screening mode. This is basically Privacy mode\n"
+" without memory.\n"
+" P([x]) - Enable privacy mode. Use 'x' as the family/key in the database if\n"
+" it is provided. The current extension is used if a database\n"
+" family/key is not specified.\n"
+" r - Indicate ringing to the calling party. Pass no audio to the calling\n"
+" party until the called channel has answered.\n"
+" S(x) - Hang up the call after 'x' seconds *after* the called party has\n"
+" answered the call.\n"
+" t - Allow the called party to transfer the calling party by sending the\n"
+" DTMF sequence defined in the blindxfer setting in the featuremap section\n"
+" of features.conf.\n"
+" T - Allow the calling party to transfer the called party by sending the\n"
+" DTMF sequence defined in the blindxfer setting in the featuremap section\n"
+" of features.conf.\n"
+" w - Allow the called party to enable recording of the call by sending\n"
+" the DTMF sequence defined in the automon setting in the featuremap section\n"
+" of features.conf.\n"
+" W - Allow the calling party to enable recording of the call by sending\n"
+" the DTMF sequence defined in the automon setting in the featuremap section\n"
+" of features.conf.\n";
+
+/* RetryDial App by Anthony Minessale II <anthmct@yahoo.com> Jan/2005 */
+static char *rapp = "RetryDial";
+static char *rsynopsis = "Place a call, retrying on failure allowing optional exit extension.";
+static char *rdescrip =
+" RetryDial(announce|sleep|retries|dialargs): This application will attempt to\n"
+"place a call using the normal Dial application. If no channel can be reached,\n"
+"the 'announce' file will be played. Then, it will wait 'sleep' number of\n"
+"seconds before retrying the call. After 'retries' number of attempts, the\n"
+"calling channel will continue at the next priority in the dialplan. If the\n"
+"'retries' setting is set to 0, this application will retry endlessly.\n"
+" While waiting to retry a call, a 1 digit extension may be dialed. If that\n"
+"extension exists in either the context defined in ${EXITCONTEXT} or the current\n"
+"one, The call will jump to that extension immediately.\n"
+" The 'dialargs' are specified in the same format that arguments are provided\n"
+"to the Dial application.\n";
+
+enum {
+ OPT_ANNOUNCE = (1 << 0),
+ OPT_RESETCDR = (1 << 1),
+ OPT_DTMF_EXIT = (1 << 2),
+ OPT_SENDDTMF = (1 << 3),
+ OPT_FORCECLID = (1 << 4),
+ OPT_GO_ON = (1 << 5),
+ OPT_CALLEE_HANGUP = (1 << 6),
+ OPT_CALLER_HANGUP = (1 << 7),
+ OPT_PRIORITY_JUMP = (1 << 8),
+ OPT_DURATION_LIMIT = (1 << 9),
+ OPT_MUSICBACK = (1 << 10),
+ OPT_CALLEE_MACRO = (1 << 11),
+ OPT_SCREEN_NOINTRO = (1 << 12),
+ OPT_SCREEN_NOCLID = (1 << 13),
+ OPT_ORIGINAL_CLID = (1 << 14),
+ OPT_SCREENING = (1 << 15),
+ OPT_PRIVACY = (1 << 16),
+ OPT_RINGBACK = (1 << 17),
+ OPT_DURATION_STOP = (1 << 18),
+ OPT_CALLEE_TRANSFER = (1 << 19),
+ OPT_CALLER_TRANSFER = (1 << 20),
+ OPT_CALLEE_MONITOR = (1 << 21),
+ OPT_CALLER_MONITOR = (1 << 22),
+ OPT_GOTO = (1 << 23),
+ OPT_OPERMODE = (1 << 24),
+ OPT_CALLEE_PARK = (1 << 25),
+ OPT_CALLER_PARK = (1 << 26),
+ OPT_IGNORE_FORWARDING = (1 << 27),
+} dial_exec_option_flags;
+
+#define DIAL_STILLGOING (1 << 30)
+#define DIAL_NOFORWARDHTML (1 << 31)
+
+enum {
+ OPT_ARG_ANNOUNCE = 0,
+ OPT_ARG_SENDDTMF,
+ OPT_ARG_GOTO,
+ OPT_ARG_DURATION_LIMIT,
+ OPT_ARG_MUSICBACK,
+ OPT_ARG_CALLEE_MACRO,
+ OPT_ARG_PRIVACY,
+ OPT_ARG_DURATION_STOP,
+ OPT_ARG_OPERMODE,
+ /* note: this entry _MUST_ be the last one in the enum */
+ OPT_ARG_ARRAY_SIZE,
+} dial_exec_option_args;
+
+AST_APP_OPTIONS(dial_exec_options, {
+ AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE),
+ AST_APP_OPTION('C', OPT_RESETCDR),
+ AST_APP_OPTION('d', OPT_DTMF_EXIT),
+ AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF),
+ AST_APP_OPTION('f', OPT_FORCECLID),
+ AST_APP_OPTION('g', OPT_GO_ON),
+ AST_APP_OPTION_ARG('G', OPT_GOTO, OPT_ARG_GOTO),
+ AST_APP_OPTION('h', OPT_CALLEE_HANGUP),
+ AST_APP_OPTION('H', OPT_CALLER_HANGUP),
+ AST_APP_OPTION('i', OPT_IGNORE_FORWARDING),
+ AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
+ AST_APP_OPTION('k', OPT_CALLEE_PARK),
+ AST_APP_OPTION('K', OPT_CALLER_PARK),
+ AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
+ AST_APP_OPTION_ARG('m', OPT_MUSICBACK, OPT_ARG_MUSICBACK),
+ AST_APP_OPTION_ARG('M', OPT_CALLEE_MACRO, OPT_ARG_CALLEE_MACRO),
+ AST_APP_OPTION('n', OPT_SCREEN_NOINTRO),
+ AST_APP_OPTION('N', OPT_SCREEN_NOCLID),
+ AST_APP_OPTION('o', OPT_ORIGINAL_CLID),
+ AST_APP_OPTION_ARG('O', OPT_OPERMODE,OPT_ARG_OPERMODE),
+ AST_APP_OPTION('p', OPT_SCREENING),
+ AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY),
+ AST_APP_OPTION('r', OPT_RINGBACK),
+ AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP),
+ AST_APP_OPTION('t', OPT_CALLEE_TRANSFER),
+ AST_APP_OPTION('T', OPT_CALLER_TRANSFER),
+ AST_APP_OPTION('w', OPT_CALLEE_MONITOR),
+ AST_APP_OPTION('W', OPT_CALLER_MONITOR),
+});
+
+#define CAN_EARLY_BRIDGE(flags,chan,peer) (!ast_test_flag(flags, OPT_CALLEE_HANGUP | \
+ OPT_CALLER_HANGUP | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | \
+ OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | OPT_CALLER_PARK) && \
+ !chan->audiohooks && !peer->audiohooks)
+
+/* We define a custom "local user" structure because we
+ use it not only for keeping track of what is in use but
+ also for keeping track of who we're dialing. */
+
+struct dial_localuser {
+ struct ast_channel *chan;
+ unsigned int flags;
+ struct dial_localuser *next;
+};
+
+
+static void hanguptree(struct dial_localuser *outgoing, struct ast_channel *exception)
+{
+ /* Hang up a tree of stuff */
+ struct dial_localuser *oo;
+ while (outgoing) {
+ /* Hangup any existing lines we have open */
+ if (outgoing->chan && (outgoing->chan != exception))
+ ast_hangup(outgoing->chan);
+ oo = outgoing;
+ outgoing=outgoing->next;
+ free(oo);
+ }
+}
+
+#define AST_MAX_WATCHERS 256
+
+#define HANDLE_CAUSE(cause, chan) do { \
+ switch(cause) { \
+ case AST_CAUSE_BUSY: \
+ if (chan->cdr) \
+ ast_cdr_busy(chan->cdr); \
+ numbusy++; \
+ break; \
+ case AST_CAUSE_CONGESTION: \
+ if (chan->cdr) \
+ ast_cdr_failed(chan->cdr); \
+ numcongestion++; \
+ break; \
+ case AST_CAUSE_NO_ROUTE_DESTINATION: \
+ case AST_CAUSE_UNREGISTERED: \
+ if (chan->cdr) \
+ ast_cdr_failed(chan->cdr); \
+ numnochan++; \
+ break; \
+ case AST_CAUSE_NORMAL_CLEARING: \
+ break; \
+ default: \
+ numnochan++; \
+ break; \
+ } \
+} while (0)
+
+
+static int onedigit_goto(struct ast_channel *chan, const char *context, char exten, int pri)
+{
+ char rexten[2] = { exten, '\0' };
+
+ if (context) {
+ if (!ast_goto_if_exists(chan, context, rexten, pri))
+ return 1;
+ } else {
+ if (!ast_goto_if_exists(chan, chan->context, rexten, pri))
+ return 1;
+ else if (!ast_strlen_zero(chan->macrocontext)) {
+ if (!ast_goto_if_exists(chan, chan->macrocontext, rexten, pri))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static const char *get_cid_name(char *name, int namelen, struct ast_channel *chan)
+{
+ const char *context = S_OR(chan->macrocontext, chan->context);
+ const char *exten = S_OR(chan->macroexten, chan->exten);
+
+ return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) ? name : "";
+}
+
+static void senddialevent(struct ast_channel *src, struct ast_channel *dst)
+{
+ /* XXX do we need also CallerIDnum ? */
+ manager_event(EVENT_FLAG_CALL, "Dial",
+ "Source: %s\r\n"
+ "Destination: %s\r\n"
+ "CallerID: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "SrcUniqueID: %s\r\n"
+ "DestUniqueID: %s\r\n",
+ src->name, dst->name, S_OR(src->cid.cid_num, "<unknown>"),
+ S_OR(src->cid.cid_name, "<unknown>"), src->uniqueid,
+ dst->uniqueid);
+}
+
+static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_localuser *outgoing, int *to, struct ast_flags *peerflags, int *sentringing, char *status, size_t statussize, int busystart, int nochanstart, int congestionstart, int priority_jump, int *result)
+{
+ int numbusy = busystart;
+ int numcongestion = congestionstart;
+ int numnochan = nochanstart;
+ int prestart = busystart + congestionstart + nochanstart;
+ int orig = *to;
+ struct ast_channel *peer = NULL;
+ /* single is set if only one destination is enabled */
+ int single = outgoing && !outgoing->next && !ast_test_flag(outgoing, OPT_MUSICBACK | OPT_RINGBACK);
+
+ if (single) {
+ /* Turn off hold music, etc */
+ ast_deactivate_generator(in);
+ /* If we are calling a single channel, make them compatible for in-band tone purpose */
+ ast_channel_make_compatible(outgoing->chan, in);
+ }
+
+
+ while (*to && !peer) {
+ struct dial_localuser *o;
+ int pos = 0; /* how many channels do we handle */
+ int numlines = prestart;
+ struct ast_channel *winner;
+ struct ast_channel *watchers[AST_MAX_WATCHERS];
+
+ watchers[pos++] = in;
+ for (o = outgoing; o; o = o->next) {
+ /* Keep track of important channels */
+ if (ast_test_flag(o, DIAL_STILLGOING) && o->chan)
+ watchers[pos++] = o->chan;
+ numlines++;
+ }
+ if (pos == 1) { /* only the input channel is available */
+ if (numlines == (numbusy + numcongestion + numnochan)) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy/congested at this time (%d:%d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan);
+ if (numbusy)
+ strcpy(status, "BUSY");
+ else if (numcongestion)
+ strcpy(status, "CONGESTION");
+ else if (numnochan)
+ strcpy(status, "CHANUNAVAIL");
+ if (ast_opt_priority_jumping || priority_jump)
+ ast_goto_if_exists(in, in->context, in->exten, in->priority + 101);
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan);
+ }
+ *to = 0;
+ return NULL;
+ }
+ winner = ast_waitfor_n(watchers, pos, to);
+ for (o = outgoing; o; o = o->next) {
+ struct ast_frame *f;
+ struct ast_channel *c = o->chan;
+
+ if (c == NULL)
+ continue;
+ if (ast_test_flag(o, DIAL_STILLGOING) && c->_state == AST_STATE_UP) {
+ if (!peer) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s answered %s\n", c->name, in->name);
+ peer = c;
+ ast_copy_flags(peerflags, o,
+ OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
+ OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
+ OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
+ OPT_CALLEE_PARK | OPT_CALLER_PARK |
+ DIAL_NOFORWARDHTML);
+ ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext));
+ ast_copy_string(c->exten, "", sizeof(c->exten));
+ }
+ continue;
+ }
+ if (c != winner)
+ continue;
+ if (!ast_strlen_zero(c->call_forward)) {
+ char tmpchan[256];
+ char *stuff;
+ char *tech;
+ int cause;
+
+ ast_copy_string(tmpchan, c->call_forward, sizeof(tmpchan));
+ if ((stuff = strchr(tmpchan, '/'))) {
+ *stuff++ = '\0';
+ tech = tmpchan;
+ } else {
+ const char *forward_context = pbx_builtin_getvar_helper(c, "FORWARD_CONTEXT");
+ snprintf(tmpchan, sizeof(tmpchan), "%s@%s", c->call_forward, forward_context ? forward_context : c->context);
+ stuff = tmpchan;
+ tech = "Local";
+ }
+ /* Before processing channel, go ahead and check for forwarding */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, c->name);
+ /* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */
+ if (ast_test_flag(peerflags, OPT_IGNORE_FORWARDING)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s/%s' prevented.\n", in->name, tech, stuff);
+ c = o->chan = NULL;
+ cause = AST_CAUSE_BUSY;
+ } else {
+ /* Setup parameters */
+ if ((c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause))) {
+ if (single)
+ ast_channel_make_compatible(o->chan, in);
+ ast_channel_inherit_variables(in, o->chan);
+ ast_channel_datastore_inherit(in, o->chan);
+ } else
+ ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause);
+ }
+ if (!c) {
+ ast_clear_flag(o, DIAL_STILLGOING);
+ HANDLE_CAUSE(cause, in);
+ } else {
+ ast_rtp_make_compatible(c, in, single);
+ if (c->cid.cid_num)
+ free(c->cid.cid_num);
+ c->cid.cid_num = NULL;
+ if (c->cid.cid_name)
+ free(c->cid.cid_name);
+ c->cid.cid_name = NULL;
+
+ if (ast_test_flag(o, OPT_FORCECLID)) {
+ c->cid.cid_num = ast_strdup(S_OR(in->macroexten, in->exten));
+ ast_string_field_set(c, accountcode, winner->accountcode);
+ c->cdrflags = winner->cdrflags;
+ } else {
+ c->cid.cid_num = ast_strdup(in->cid.cid_num);
+ c->cid.cid_name = ast_strdup(in->cid.cid_name);
+ ast_string_field_set(c, accountcode, in->accountcode);
+ c->cdrflags = in->cdrflags;
+ }
+
+ if (in->cid.cid_ani) {
+ if (c->cid.cid_ani)
+ free(c->cid.cid_ani);
+ c->cid.cid_ani = ast_strdup(in->cid.cid_ani);
+ }
+ if (c->cid.cid_rdnis)
+ free(c->cid.cid_rdnis);
+ c->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
+ if (ast_call(c, tmpchan, 0)) {
+ ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
+ ast_clear_flag(o, DIAL_STILLGOING);
+ ast_hangup(c);
+ c = o->chan = NULL;
+ numnochan++;
+ } else {
+ senddialevent(in, c);
+ /* After calling, set callerid to extension */
+ if (!ast_test_flag(peerflags, OPT_ORIGINAL_CLID)) {
+ char cidname[AST_MAX_EXTENSION] = "";
+ ast_set_callerid(c, S_OR(in->macroexten, in->exten), get_cid_name(cidname, sizeof(cidname), in), NULL);
+ }
+ }
+ }
+ /* Hangup the original channel now, in case we needed it */
+ ast_hangup(winner);
+ continue;
+ }
+ f = ast_read(winner);
+ if (!f) {
+ in->hangupcause = c->hangupcause;
+ ast_hangup(c);
+ c = o->chan = NULL;
+ ast_clear_flag(o, DIAL_STILLGOING);
+ HANDLE_CAUSE(in->hangupcause, in);
+ continue;
+ }
+ if (f->frametype == AST_FRAME_CONTROL) {
+ switch(f->subclass) {
+ case AST_CONTROL_ANSWER:
+ /* This is our guy if someone answered. */
+ if (!peer) {
+ if (option_verbose > 2)
+ ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", c->name, in->name);
+ peer = c;
+ if (peer->cdr) {
+ peer->cdr->answer = ast_tvnow();
+ peer->cdr->disposition = AST_CDR_ANSWERED;
+ }
+ ast_copy_flags(peerflags, o,
+ OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER |
+ OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP |
+ OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR |
+ OPT_CALLEE_PARK | OPT_CALLER_PARK |
+ DIAL_NOFORWARDHTML);
+ ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext));
+ ast_copy_string(c->exten, "", sizeof(c->exten));
+ /* Setup RTP early bridge if appropriate */
+ if (CAN_EARLY_BRIDGE(peerflags, in, peer))
+ ast_rtp_early_bridge(in, peer);
+ }
+ /* If call has been answered, then the eventual hangup is likely to be normal hangup */
+ in->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+ c->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+ break;
+ case AST_CONTROL_BUSY:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s is busy\n", c->name);
+ in->hangupcause = c->hangupcause;
+ ast_hangup(c);
+ c = o->chan = NULL;
+ ast_clear_flag(o, DIAL_STILLGOING);
+ HANDLE_CAUSE(AST_CAUSE_BUSY, in);
+ break;
+ case AST_CONTROL_CONGESTION:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s is circuit-busy\n", c->name);
+ in->hangupcause = c->hangupcause;
+ ast_hangup(c);
+ c = o->chan = NULL;
+ ast_clear_flag(o, DIAL_STILLGOING);
+ HANDLE_CAUSE(AST_CAUSE_CONGESTION, in);
+ break;
+ case AST_CONTROL_RINGING:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s is ringing\n", c->name);
+ /* Setup early media if appropriate */
+ if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
+ ast_rtp_early_bridge(in, c);
+ if (!(*sentringing) && !ast_test_flag(outgoing, OPT_MUSICBACK)) {
+ ast_indicate(in, AST_CONTROL_RINGING);
+ (*sentringing)++;
+ }
+ break;
+ case AST_CONTROL_PROGRESS:
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_3 "%s is making progress passing it to %s\n", c->name, in->name);
+ /* Setup early media if appropriate */
+ if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
+ ast_rtp_early_bridge(in, c);
+ if (!ast_test_flag(outgoing, OPT_RINGBACK))
+ ast_indicate(in, AST_CONTROL_PROGRESS);
+ break;
+ case AST_CONTROL_VIDUPDATE:
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_3 "%s requested a video update, passing it to %s\n", c->name, in->name);
+ ast_indicate(in, AST_CONTROL_VIDUPDATE);
+ break;
+ case AST_CONTROL_SRCUPDATE:
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_3 "%s requested a source update, passing it to %s\n", c->name, in->name);
+ ast_indicate(in, AST_CONTROL_SRCUPDATE);
+ break;
+ case AST_CONTROL_PROCEEDING:
+ if (option_verbose > 2)
+ ast_verbose (VERBOSE_PREFIX_3 "%s is proceeding passing it to %s\n", c->name, in->name);
+ if (single && CAN_EARLY_BRIDGE(peerflags, in, c))
+ ast_rtp_early_bridge(in, c);
+ if (!ast_test_flag(outgoing, OPT_RINGBACK))
+ ast_indicate(in, AST_CONTROL_PROCEEDING);
+ break;
+ case AST_CONTROL_HOLD:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Call on %s placed on hold\n", c->name);
+ ast_indicate(in, AST_CONTROL_HOLD);
+ break;
+ case AST_CONTROL_UNHOLD:
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Call on %s left from hold\n", c->name);
+ ast_indicate(in, AST_CONTROL_UNHOLD);
+ break;
+ case AST_CONTROL_OFFHOOK:
+ case AST_CONTROL_FLASH:
+ /* Ignore going off hook and flash */
+ break;
+ case -1:
+ if (!ast_test_flag(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s stopped sounds\n", c->name);
+ ast_indicate(in, -1);
+ (*sentringing) = 0;
+ }
+ break;
+ default:
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
+ }
+ } else if (single) {
+ /* XXX are we sure the logic is correct ? or we should just switch on f->frametype ? */
+ if (f->frametype == AST_FRAME_VOICE && !ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK)) {
+ if (ast_write(in, f))
+ ast_log(LOG_WARNING, "Unable to forward voice frame\n");
+ } else if (f->frametype == AST_FRAME_IMAGE && !ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK)) {
+ if (ast_write(in, f))
+ ast_log(LOG_WARNING, "Unable to forward image\n");
+ } else if (f->frametype == AST_FRAME_TEXT && !ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK)) {
+ if (ast_write(in, f))
+ ast_log(LOG_WARNING, "Unable to send text\n");
+ } else if (f->frametype == AST_FRAME_HTML && !ast_test_flag(outgoing, DIAL_NOFORWARDHTML)) {
+ if (ast_channel_sendhtml(in, f->subclass, f->data, f->datalen) == -1)
+ ast_log(LOG_WARNING, "Unable to send URL\n");
+ }
+ }
+ ast_frfree(f);
+ } /* end for */
+ if (winner == in) {
+ struct ast_frame *f = ast_read(in);
+#if 0
+ if (f && (f->frametype != AST_FRAME_VOICE))
+ printf("Frame type: %d, %d\n", f->frametype, f->subclass);
+ else if (!f || (f->frametype != AST_FRAME_VOICE))
+ printf("Hangup received on %s\n", in->name);
+#endif
+ if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
+ /* Got hung up */
+ *to = -1;
+ ast_cdr_noanswer(in->cdr);
+ strcpy(status, "CANCEL");
+ if (f)
+ ast_frfree(f);
+ return NULL;
+ }
+
+ if (f && (f->frametype == AST_FRAME_DTMF)) {
+ if (ast_test_flag(peerflags, OPT_DTMF_EXIT)) {
+ const char *context = pbx_builtin_getvar_helper(in, "EXITCONTEXT");
+ if (onedigit_goto(in, context, (char) f->subclass, 1)) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
+ *to=0;
+ ast_cdr_noanswer(in->cdr);
+ *result = f->subclass;
+ strcpy(status, "CANCEL");
+ ast_frfree(f);
+ return NULL;
+ }
+ }
+
+ if (ast_test_flag(peerflags, OPT_CALLER_HANGUP) &&
+ (f->subclass == '*')) { /* hmm it it not guaranteed to be '*' anymore. */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
+ *to=0;
+ ast_cdr_noanswer(in->cdr);
+ strcpy(status, "CANCEL");
+ ast_frfree(f);
+ return NULL;
+ }
+ }
+
+ /* Forward HTML stuff */
+ if (single && f && (f->frametype == AST_FRAME_HTML) && !ast_test_flag(outgoing, DIAL_NOFORWARDHTML))
+ if(ast_channel_sendhtml(outgoing->chan, f->subclass, f->data, f->datalen) == -1)
+ ast_log(LOG_WARNING, "Unable to send URL\n");
+
+
+ if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF_BEGIN) || (f->frametype == AST_FRAME_DTMF_END))) {
+ if (ast_write(outgoing->chan, f))
+ ast_log(LOG_WARNING, "Unable to forward voice or dtmf\n");
+ }
+ if (single && (f->frametype == AST_FRAME_CONTROL) &&
+ ((f->subclass == AST_CONTROL_HOLD) ||
+ (f->subclass == AST_CONTROL_UNHOLD) ||
+ (f->subclass == AST_CONTROL_VIDUPDATE) ||
+ (f->subclass == AST_CONTROL_SRCUPDATE))) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "%s requested special control %d, passing it to %s\n", in->name, f->subclass, outgoing->chan->name);
+ ast_indicate_data(outgoing->chan, f->subclass, f->data, f->datalen);
+ }
+ ast_frfree(f);
+ }
+ if (!*to && (option_verbose > 2))
+ ast_verbose(VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig);
+ if (!*to || ast_check_hangup(in)) {
+ ast_cdr_noanswer(in->cdr);
+ }
+
+ }
+
+ return peer;
+}
+
+static void replace_macro_delimiter(char *s)
+{
+ for (; *s; s++)
+ if (*s == '^')
+ *s = '|';
+}
+
+
+/* returns true if there is a valid privacy reply */
+static int valid_priv_reply(struct ast_flags *opts, int res)
+{
+ if (res < '1')
+ return 0;
+ if (ast_test_flag(opts, OPT_PRIVACY) && res <= '5')
+ return 1;
+ if (ast_test_flag(opts, OPT_SCREENING) && res <= '4')
+ return 1;
+ return 0;
+}
+
+static void set_dial_features(struct ast_flags *opts, struct ast_dial_features *features)
+{
+ struct ast_flags perm_opts = {.flags = 0};
+
+ ast_copy_flags(&perm_opts, opts,
+ OPT_CALLER_TRANSFER | OPT_CALLER_PARK | OPT_CALLER_MONITOR | OPT_CALLER_HANGUP |
+ OPT_CALLEE_TRANSFER | OPT_CALLEE_PARK | OPT_CALLEE_MONITOR | OPT_CALLEE_HANGUP);
+
+ memset(features->options, 0, sizeof(features->options));
+
+ ast_app_options2str(dial_exec_options, &perm_opts, features->options, sizeof(features->options));
+ if (ast_test_flag(&perm_opts, OPT_CALLEE_TRANSFER))
+ ast_set_flag(&(features->features_callee), AST_FEATURE_REDIRECT);
+ if (ast_test_flag(&perm_opts, OPT_CALLER_TRANSFER))
+ ast_set_flag(&(features->features_caller), AST_FEATURE_REDIRECT);
+ if (ast_test_flag(&perm_opts, OPT_CALLEE_HANGUP))
+ ast_set_flag(&(features->features_callee), AST_FEATURE_DISCONNECT);
+ if (ast_test_flag(&perm_opts, OPT_CALLER_HANGUP))
+ ast_set_flag(&(features->features_caller), AST_FEATURE_DISCONNECT);
+ if (ast_test_flag(&perm_opts, OPT_CALLEE_MONITOR))
+ ast_set_flag(&(features->features_callee), AST_FEATURE_AUTOMON);
+ if (ast_test_flag(&perm_opts, OPT_CALLER_MONITOR))
+ ast_set_flag(&(features->features_caller), AST_FEATURE_AUTOMON);
+ if (ast_test_flag(&perm_opts, OPT_CALLEE_PARK))
+ ast_set_flag(&(features->features_callee), AST_FEATURE_PARKCALL);
+ if (ast_test_flag(&perm_opts, OPT_CALLER_PARK))
+ ast_set_flag(&(features->features_caller), AST_FEATURE_PARKCALL);
+}
+
+static void end_bridge_callback (void *data)
+{
+ char buf[80];
+ time_t end;
+ struct ast_channel *chan = data;
+
+ if (!chan->cdr) {
+ return;
+ }
+
+ time(&end);
+
+ ast_channel_lock(chan);
+ if (chan->cdr->answer.tv_sec) {
+ snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->answer.tv_sec);
+ pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf);
+ }
+
+ if (chan->cdr->start.tv_sec) {
+ snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->start.tv_sec);
+ pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf);
+ }
+ ast_channel_unlock(chan);
+}
+
+static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator) {
+ bconfig->end_bridge_callback_data = originator;
+}
+
+static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags, int *continue_exec)
+{
+ int res = -1;
+ struct ast_module_user *u;
+ char *rest, *cur;
+ struct dial_localuser *outgoing = NULL;
+ struct ast_channel *peer;
+ int to;
+ int numbusy = 0;
+ int numcongestion = 0;
+ int numnochan = 0;
+ int cause;
+ char numsubst[256];
+ char cidname[AST_MAX_EXTENSION] = "";
+ int privdb_val = 0;
+ int calldurationlimit = -1;
+ long timelimit = 0;
+ long play_warning = 0;
+ long warning_freq = 0;
+ const char *warning_sound = NULL;
+ const char *end_sound = NULL;
+ const char *start_sound = NULL;
+ char *dtmfcalled = NULL, *dtmfcalling = NULL;
+ char status[256] = "INVALIDARGS";
+ int play_to_caller = 0, play_to_callee = 0;
+ int sentringing = 0, moh = 0;
+ const char *outbound_group = NULL;
+ int result = 0;
+ time_t start_time;
+ char privintro[1024];
+ char privcid[256];
+ char *parse;
+ int opermode = 0;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(peers);
+ AST_APP_ARG(timeout);
+ AST_APP_ARG(options);
+ AST_APP_ARG(url);
+ );
+ struct ast_flags opts = { 0, };
+ char *opt_args[OPT_ARG_ARRAY_SIZE];
+ struct ast_datastore *datastore = NULL;
+ struct ast_datastore *ds_caller_features = NULL;
+ struct ast_datastore *ds_callee_features = NULL;
+ struct ast_dial_features *caller_features;
+ int fulldial = 0, num_dialed = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
+ pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
+ return -1;
+ }
+
+ /* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */
+ pbx_builtin_setvar_helper(chan, "DIALSTATUS", "");
+ pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", "");
+ pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", "");
+ pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", "");
+ pbx_builtin_setvar_helper(chan, "DIALEDTIME", "");
+
+ u = ast_module_user_add(chan);
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (!ast_strlen_zero(args.options) &&
+ ast_app_parse_options(dial_exec_options, &opts, opt_args, args.options)) {
+ pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
+ goto done;
+ }
+
+ if (ast_strlen_zero(args.peers)) {
+ ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n");
+ pbx_builtin_setvar_helper(chan, "DIALSTATUS", status);
+ goto done;
+ }
+
+ if (ast_test_flag(&opts, OPT_OPERMODE)) {
+ if (ast_strlen_zero(opt_args[OPT_ARG_OPERMODE]))
+ opermode = 1;
+ else opermode = atoi(opt_args[OPT_ARG_OPERMODE]);
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Setting operator services mode to %d.\n", opermode);<